From 2c669674ada3177a6e6049f349b8d2219cc3d0d8 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Sun, 2 Jan 2011 14:59:43 +0000 Subject: [PATCH] Fix for listbox and table multi-selection. Internal updates for relative positioning. Additions to Typeface class to allow hinted subclasses. --- .../Source/Utility/jucer_MiscUtilities.cpp | 140 ---- .../Source/Utility/jucer_MiscUtilities.h | 70 -- juce_amalgamated.cpp | 691 ++++++++++++++---- juce_amalgamated.h | 131 +++- .../audio_file_formats/juce_AudioThumbnail.h | 5 + src/containers/juce_NamedValueSet.cpp | 432 +++++------ src/core/juce_StandardHeader.h | 2 +- src/gui/components/controls/juce_ListBox.cpp | 9 +- src/gui/components/controls/juce_ListBox.h | 3 +- .../components/controls/juce_TableListBox.cpp | 4 +- .../filebrowser/juce_FileListComponent.cpp | 2 +- src/gui/components/juce_Component.cpp | 18 + src/gui/components/juce_Component.h | 53 +- .../layout/juce_ComponentBuilder.cpp | 18 +- src/gui/components/layout/juce_MarkerList.cpp | 17 +- src/gui/components/layout/juce_MarkerList.h | 38 +- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 24 +- src/gui/graphics/fonts/juce_Font.cpp | 238 +++--- src/gui/graphics/fonts/juce_Font.h | 7 +- src/gui/graphics/fonts/juce_Typeface.h | 8 + .../geometry/juce_RelativeCoordinate.cpp | 341 ++++++++- .../geometry/juce_RelativeCoordinate.h | 8 + src/maths/juce_Expression.cpp | 30 +- src/maths/juce_Expression.h | 7 +- 24 files changed, 1536 insertions(+), 760 deletions(-) diff --git a/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.cpp b/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.cpp index a0c71ce1da..73f5f5e0b6 100644 --- a/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.cpp +++ b/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.cpp @@ -336,143 +336,3 @@ void FloatingLabelComponent::paint (Graphics& g) g.setColour (colour); glyphs.draw (g, AffineTransform::translation (1.0f, 1.0f)); } - -//============================================================================== -RelativeRectangleLayoutManager::RelativeRectangleLayoutManager (Component* parentComponent) - : parent (parentComponent) -{ - parent->addComponentListener (this); -} - -RelativeRectangleLayoutManager::~RelativeRectangleLayoutManager() -{ - parent->removeComponentListener (this); - - for (int i = components.size(); --i >= 0;) - components.getUnchecked(i)->component->removeComponentListener (this); -} - -void RelativeRectangleLayoutManager::setMarker (const String& name, const RelativeCoordinate& coord) -{ - for (int i = markers.size(); --i >= 0;) - { - MarkerPosition* m = markers.getUnchecked(i); - if (m->markerName == name) - { - m->position = coord; - applyLayout(); - return; - } - } - - markers.add (new MarkerPosition (name, coord)); - applyLayout(); -} - -void RelativeRectangleLayoutManager::setComponentBounds (Component* comp, const String& name, const RelativeRectangle& coords) -{ - jassert (comp != 0); - - // All the components that this layout manages must be inside the parent component.. - jassert (parent->isParentOf (comp)); - - for (int i = components.size(); --i >= 0;) - { - ComponentPosition* c = components.getUnchecked(i); - if (c->component == comp) - { - c->name = name; - c->coords = coords; - triggerAsyncUpdate(); - return; - } - } - - components.add (new ComponentPosition (comp, name, coords)); - comp->addComponentListener (this); - triggerAsyncUpdate(); -} - -void RelativeRectangleLayoutManager::applyLayout() -{ - for (int i = components.size(); --i >= 0;) - { - ComponentPosition* c = components.getUnchecked(i); - - // All the components that this layout manages must be inside the parent component.. - jassert (parent->isParentOf (c->component)); - - c->component->setBounds (c->coords.resolve (this).getSmallestIntegerContainer()); - } -} - -const Expression RelativeRectangleLayoutManager::getSymbolValue (const String& objectName, const String& edge) const -{ - if (objectName == RelativeCoordinate::Strings::parent) - { - if (edge == RelativeCoordinate::Strings::right) return Expression ((double) parent->getWidth()); - if (edge == RelativeCoordinate::Strings::bottom) return Expression ((double) parent->getHeight()); - } - - if (objectName.isNotEmpty() && edge.isNotEmpty()) - { - for (int i = components.size(); --i >= 0;) - { - ComponentPosition* c = components.getUnchecked(i); - - if (c->name == objectName) - { - if (edge == RelativeCoordinate::Strings::left) return c->coords.left.getExpression(); - if (edge == RelativeCoordinate::Strings::right) return c->coords.right.getExpression(); - if (edge == RelativeCoordinate::Strings::top) return c->coords.top.getExpression(); - if (edge == RelativeCoordinate::Strings::bottom) return c->coords.bottom.getExpression(); - } - } - } - - for (int i = markers.size(); --i >= 0;) - { - MarkerPosition* m = markers.getUnchecked(i); - - if (m->markerName == objectName) - return m->position.getExpression(); - } - - return Expression(); -} - -void RelativeRectangleLayoutManager::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) -{ - triggerAsyncUpdate(); - - if (parent == &component) - handleUpdateNowIfNeeded(); -} - -void RelativeRectangleLayoutManager::componentBeingDeleted (Component& component) -{ - for (int i = components.size(); --i >= 0;) - { - ComponentPosition* c = components.getUnchecked(i); - if (c->component == &component) - { - components.remove (i); - break; - } - } -} - -void RelativeRectangleLayoutManager::handleAsyncUpdate() -{ - applyLayout(); -} - -RelativeRectangleLayoutManager::MarkerPosition::MarkerPosition (const String& name, const RelativeCoordinate& coord) - : markerName (name), position (coord) -{ -} - -RelativeRectangleLayoutManager::ComponentPosition::ComponentPosition (Component* component_, const String& name_, const RelativeRectangle& coords_) - : component (component_), name (name_), coords (coords_) -{ -} diff --git a/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h b/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h index 69c60a3fa2..0148ca74bc 100644 --- a/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h +++ b/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h @@ -96,10 +96,6 @@ public: setClickingTogglesState (false); } - ~JucerToolbarButton() - { - } - //============================================================================== bool getToolbarItemSizes (int toolbarDepth, bool isToolbarVertical, int& preferredSize, int& minSize, int& maxSize) { @@ -137,69 +133,3 @@ public: private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JucerToolbarButton); }; - - -//============================================================================== -/** -*/ -class RelativeRectangleLayoutManager : public ComponentListener, - public Expression::EvaluationContext, - public AsyncUpdater -{ -public: - //============================================================================== - /** - */ - RelativeRectangleLayoutManager (Component* parentComponent); - - /** Destructor. */ - ~RelativeRectangleLayoutManager(); - - //============================================================================== - /** - */ - void setMarker (const String& name, const RelativeCoordinate& coord); - - /** - */ - void setComponentBounds (Component* component, const String& componentName, const RelativeRectangle& bounds); - - /** - */ - void applyLayout(); - - //============================================================================== - /** @internal */ - const Expression getSymbolValue (const String& symbol, const String& member) const; - /** @internal */ - void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); - /** @internal */ - void componentBeingDeleted (Component& component); - /** @internal */ - void handleAsyncUpdate(); - -private: - //============================================================================== - struct ComponentPosition - { - ComponentPosition (Component* component, const String& name, const RelativeRectangle& coords); - - Component* component; - String name; - RelativeRectangle coords; - }; - - struct MarkerPosition - { - MarkerPosition (const String& name, const RelativeCoordinate& coord); - - String markerName; - RelativeCoordinate position; - }; - - Component* parent; - OwnedArray components; - OwnedArray markers; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleLayoutManager); -}; diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 2034bd3bd6..99685f23ef 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -4719,13 +4719,14 @@ public: return 0; } - Type getType() const throw() { return symbolType; } - Term* clone() const { return new Symbol (mainSymbol, member); } - int getNumInputs() const { return 0; } - Term* getInput (int) const { return 0; } - const String getSymbolName() const { return toString(); } + Type getType() const throw() { return symbolType; } + Term* clone() const { return new Symbol (mainSymbol, member); } + int getNumInputs() const { return 0; } + Term* getInput (int) const { return 0; } + const String toString() const { return joinParts (mainSymbol, member); } + void getSymbolParts (String& objectName, String& memberName) const { objectName = mainSymbol; memberName = member; } - const String toString() const + static const String joinParts (const String& mainSymbol, const String& member) { return member.isEmpty() ? mainSymbol : mainSymbol + "." + member; @@ -5501,6 +5502,9 @@ const Expression Expression::withRenamedSymbol (const String& oldSymbol, const S { jassert (newSymbol.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); + if (oldSymbol == newSymbol) + return *this; + Expression newExpression (term->clone()); Helpers::renameSymbol (newExpression.term, oldSymbol, newSymbol); return newExpression; @@ -5523,7 +5527,14 @@ Expression::Type Expression::getType() const throw() const String Expression::getSymbol() const { - return term->getSymbolName(); + String objectName, memberName; + term->getSymbolParts (objectName, memberName); + return Expression::Helpers::Symbol::joinParts (objectName, memberName); +} + +void Expression::getSymbolParts (String& objectName, String& memberName) const +{ + term->getSymbolParts (objectName, memberName); } const String Expression::getFunction() const @@ -5572,10 +5583,9 @@ const ReferenceCountedObjectPtr Expression::Term::negated() return new Helpers::Negate (this); } -const String Expression::Term::getSymbolName() const +void Expression::Term::getSymbolParts (String&, String&) const { jassertfalse; // You should only call getSymbol() on an expression that's actually a symbol! - return String::empty; } const String Expression::Term::getFunctionName() const @@ -41530,6 +41540,23 @@ MarkerList* Component::getMarkers (bool /*xAxis*/) return 0; } +Component::Positioner::Positioner (Component& component_) throw() + : component (component_) +{ +} + +Component::Positioner* Component::getPositioner() const throw() +{ + return positioner; +} + +void Component::setPositioner (Positioner* newPositioner) +{ + // You can only assign a positioner to the component that it was created for! + jassert (newPositioner == 0 || this == &(newPositioner->getComponent())); + positioner = newPositioner; +} + const Rectangle Component::getLocalBounds() const throw() { return Rectangle (getWidth(), getHeight()); @@ -48343,7 +48370,7 @@ public: { if (! selected) { - owner.selectRowsBasedOnModifierKeys (row, e.mods); + owner.selectRowsBasedOnModifierKeys (row, e.mods, false); if (owner.getModel() != 0) owner.getModel()->listBoxItemClicked (row, e); @@ -48359,7 +48386,7 @@ public: { if (isEnabled() && selectRowOnMouseUp && ! isDragging) { - owner.selectRowsBasedOnModifierKeys (row, e.mods); + owner.selectRowsBasedOnModifierKeys (row, e.mods, true); if (owner.getModel() != 0) owner.getModel()->listBoxItemClicked (row, e); @@ -48827,7 +48854,8 @@ void ListBox::deselectAllRows() } void ListBox::selectRowsBasedOnModifierKeys (const int row, - const ModifierKeys& mods) + const ModifierKeys& mods, + const bool isMouseUpEvent) { if (multipleSelection && mods.isCommandDown()) { @@ -48839,7 +48867,7 @@ void ListBox::selectRowsBasedOnModifierKeys (const int row, } else if ((! mods.isPopupMenu()) || ! isRowSelected (row)) { - selectRowInternal (row, false, ! (multipleSelection && isRowSelected (row)), true); + selectRowInternal (row, false, ! (multipleSelection && (! isMouseUpEvent) && isRowSelected (row)), true); } } @@ -51692,7 +51720,7 @@ public: { if (! isSelected) { - owner.selectRowsBasedOnModifierKeys (row, e.mods); + owner.selectRowsBasedOnModifierKeys (row, e.mods, false); const int columnId = owner.getHeader().getColumnIdAtX (e.x); @@ -51729,7 +51757,7 @@ public: { if (selectRowOnMouseUp && e.mouseWasClicked() && isEnabled()) { - owner.selectRowsBasedOnModifierKeys (row, e.mods); + owner.selectRowsBasedOnModifierKeys (row, e.mods, true); const int columnId = owner.getHeader().getColumnIdAtX (e.x); @@ -58728,7 +58756,7 @@ public: void mouseDown (const MouseEvent& e) { - owner.selectRowsBasedOnModifierKeys (index, e.mods); + owner.selectRowsBasedOnModifierKeys (index, e.mods, false); owner.sendMouseClickMessage (file, e); } @@ -61350,24 +61378,20 @@ namespace ComponentBuilderHelpers if (topLevelComp != 0) { - const String compId (getStateId (state)); + ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state); - if (compId.isEmpty() && state.getParent().isValid()) + if (type == 0) { // ..handle the case where a child of the actual state node has changed. - updateComponent (builder, state.getParent()); + if (state.getParent().isValid()) + updateComponent (builder, state.getParent()); } else { - ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state); + Component* const changedComp = findComponentWithID (topLevelComp, getStateId (state)); - if (type != 0) - { - Component* const changedComp = findComponentWithID (topLevelComp, compId); - - if (changedComp != 0) - type->updateComponentFromState (changedComp, state); - } + if (changedComp != 0) + type->updateComponentFromState (changedComp, state); } } } @@ -62036,6 +62060,7 @@ MarkerList& MarkerList::operator= (const MarkerList& other) MarkerList::~MarkerList() { + listeners.call (&MarkerList::Listener::markerListBeingDeleted, this); } bool MarkerList::operator== (const MarkerList& other) const throw() @@ -62129,7 +62154,21 @@ void MarkerList::removeMarker (const String& name) void MarkerList::markersHaveChanged() { - sendChangeMessage(); + listeners.call (&MarkerList::Listener::markersChanged, this); +} + +void MarkerList::Listener::markerListBeingDeleted (MarkerList* markerList) +{ +} + +void MarkerList::addListener (Listener* listener) +{ + listeners.add (listener); +} + +void MarkerList::removeListener (Listener* listener) +{ + listeners.remove (listener); } MarkerList::Marker::Marker (const Marker& other) @@ -79647,7 +79686,271 @@ namespace RelativeCoordinateHelpers } } +class RelativeComponentPositioner : public Component::Positioner, + public ComponentListener, + public MarkerList::Listener, + public Expression::EvaluationContext +{ +public: + RelativeComponentPositioner (Component& component_) + : Component::Positioner (component_), registeredOk (false) + { + } + + ~RelativeComponentPositioner() + { + unregisterListeners(); + } + + const Expression getSymbolValue (const String& objectName, const String& member) const + { + jassert (objectName.isNotEmpty()); + + if (member.isNotEmpty()) + { + const Component* comp = getSourceComponent (objectName); + + if (comp == 0) + { + if (objectName == RelativeCoordinate::Strings::parent) + comp = getComponent().getParentComponent(); + else if (objectName == RelativeCoordinate::Strings::this_ || objectName == getComponent().getComponentID()) + comp = &getComponent(); + } + + if (comp != 0) + { + if (member == RelativeCoordinate::Strings::left) return xToExpression (comp, 0); + if (member == RelativeCoordinate::Strings::right) return xToExpression (comp, comp->getWidth()); + if (member == RelativeCoordinate::Strings::top) return yToExpression (comp, 0); + if (member == RelativeCoordinate::Strings::bottom) return yToExpression (comp, comp->getHeight()); + } + } + + for (int i = sourceMarkerLists.size(); --i >= 0;) + { + MarkerList* const markerList = sourceMarkerLists.getUnchecked(i); + const MarkerList::Marker* const marker = markerList->getMarker (objectName); + + if (marker != 0) + return marker->position.getExpression(); + } + + return Expression::EvaluationContext::getSymbolValue (objectName, member); + } + + void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) + { + apply(); + } + + void componentParentHierarchyChanged (Component& component) + { + apply(); + } + + void componentBeingDeleted (Component& component) + { + jassert (sourceComponents.contains (&component)); + sourceComponents.removeValue (&component); + } + + void markersChanged (MarkerList* markerList) + { + apply(); + } + + void markerListBeingDeleted (MarkerList* markerList) + { + jassert (sourceMarkerLists.contains (markerList)); + sourceMarkerLists.removeValue (markerList); + } + + void apply() + { + if (! registeredOk) + { + unregisterListeners(); + registeredOk = registerCoordinates(); + } + + applyToComponentBounds(); + } + +protected: + bool addCoordinate (const RelativeCoordinate& coord) + { + return registerListeners (coord.getExpression()); + } + + virtual bool registerCoordinates() = 0; + virtual void applyToComponentBounds() = 0; + +private: + Array sourceComponents; + Array sourceMarkerLists; + bool registeredOk; + + bool registerListeners (const Expression& e) + { + bool ok = true; + + if (e.getType() == Expression::symbolType) + { + String objectName, memberName; + e.getSymbolParts (objectName, memberName); + + if (memberName.isNotEmpty()) + ok = registerComponentEdge (objectName, memberName) && ok; + else + ok = registerMarker (objectName) && ok; + } + else + { + for (int i = e.getNumInputs(); --i >= 0;) + ok = registerListeners (e.getInput (i)) && ok; + } + + return ok; + } + + bool registerComponentEdge (const String& objectName, const String memberName) + { + Component* comp = findComponent (objectName); + + if (comp == 0) + { + if (objectName == RelativeCoordinate::Strings::parent) + comp = getComponent().getParentComponent(); + else if (objectName == RelativeCoordinate::Strings::this_ || objectName == getComponent().getComponentID()) + comp = &getComponent(); + } + + if (comp != 0) + { + if (comp != &getComponent()) + registerComponentListener (comp); + + return true; + } + else + { + // The component we want doesn't exist, so watch the parent in case the hierarchy changes and it appears later.. + Component* const parent = getComponent().getParentComponent(); + + if (parent != 0) + registerComponentListener (parent); + else + registerComponentListener (&getComponent()); + + return false; + } + } + + bool registerMarker (const String markerName) + { + Component* const parent = getComponent().getParentComponent(); + + if (parent != 0) + { + MarkerList* list = parent->getMarkers (true); + + if (list == 0 || list->getMarker (markerName) == 0) + list = parent->getMarkers (false); + + if (list != 0 && list->getMarker (markerName) != 0) + { + registerMarkerListListener (list); + return true; + } + else + { + // The marker we want doesn't exist, so watch all lists in case they change and the marker appears later.. + registerMarkerListListener (parent->getMarkers (true)); + registerMarkerListListener (parent->getMarkers (false)); + } + } + + return false; + } + + void registerComponentListener (Component* const comp) + { + if (comp != 0 && ! sourceComponents.contains (comp)) + { + comp->addComponentListener (this); + sourceComponents.add (comp); + } + } + + void registerMarkerListListener (MarkerList* const list) + { + if (list != 0 && ! sourceMarkerLists.contains (list)) + { + list->addListener (this); + sourceMarkerLists.add (list); + } + } + + void unregisterListeners() + { + int i; + for (i = sourceComponents.size(); --i >= 0;) + sourceComponents.getUnchecked(i)->removeComponentListener (this); + + for (i = sourceMarkerLists.size(); --i >= 0;) + sourceMarkerLists.getUnchecked(i)->removeListener (this); + + sourceComponents.clear(); + sourceMarkerLists.clear(); + } + + Component* findComponent (const String& componentID) const + { + Component* const parent = getComponent().getParentComponent(); + + if (parent != 0) + { + for (int i = parent->getNumChildComponents(); --i >= 0;) + { + Component* const c = parent->getChildComponent(i); + + if (c->getComponentID() == componentID) + return c; + } + } + + return 0; + } + + Component* getSourceComponent (const String& objectName) const + { + for (int i = sourceComponents.size(); --i >= 0;) + { + Component* const comp = sourceComponents.getUnchecked(i); + + if (comp->getComponentID() == objectName) + return comp; + } + + return 0; + } + + const Expression xToExpression (const Component* const source, const int x) const + { + return Expression ((double) (getComponent().getLocalPoint (source, Point (x, 0)).getX() + getComponent().getX())); + } + + const Expression yToExpression (const Component* const source, const int y) const + { + return Expression ((double) (getComponent().getLocalPoint (source, Point (0, y)).getY() + getComponent().getY())); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeComponentPositioner); +}; + const String RelativeCoordinate::Strings::parent ("parent"); +const String RelativeCoordinate::Strings::this_ ("this"); const String RelativeCoordinate::Strings::left ("left"); const String RelativeCoordinate::Strings::right ("right"); const String RelativeCoordinate::Strings::top ("top"); @@ -79898,7 +80201,7 @@ const Rectangle RelativeRectangle::resolve (const Expression::EvaluationC const double t = top.resolve (context); const double b = bottom.resolve (context); - return Rectangle ((float) l, (float) t, (float) (r - l), (float) (b - t)); + return Rectangle ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t)); } void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* context) @@ -79909,6 +80212,11 @@ void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Ex bottom.moveToAbsolute (newPos.getBottom(), context); } +bool RelativeRectangle::isDynamic() const +{ + return left.isDynamic() || right.isDynamic() || top.isDynamic() || bottom.isDynamic(); +} + const String RelativeRectangle::toString() const { return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString(); @@ -79922,6 +80230,71 @@ void RelativeRectangle::renameSymbolIfUsed (const String& oldName, const String& bottom.renameSymbolIfUsed (oldName, newName); } +class RelativeRectangleComponentPositioner : public RelativeComponentPositioner +{ +public: + RelativeRectangleComponentPositioner (Component& component_, const RelativeRectangle& rectangle_) + : RelativeComponentPositioner (component_), + rectangle (rectangle_) + { + } + + bool registerCoordinates() + { + bool ok = addCoordinate (rectangle.left); + ok = addCoordinate (rectangle.right) && ok; + ok = addCoordinate (rectangle.top) && ok; + ok = addCoordinate (rectangle.bottom) && ok; + return ok; + } + + bool isUsingRectangle (const RelativeRectangle& other) const throw() + { + return rectangle == other; + } + + void applyToComponentBounds() + { + for (int i = 4; --i >= 0;) + { + const Rectangle newBounds (rectangle.resolve (this).getSmallestIntegerContainer()); + + if (newBounds == getComponent().getBounds()) + return; + + getComponent().setBounds (newBounds); + } + + jassertfalse; // must be a recursive reference! + } + +private: + const RelativeRectangle rectangle; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner); +}; + +void RelativeRectangle::applyToComponent (Component& component) const +{ + if (isDynamic()) + { + RelativeRectangleComponentPositioner* current = dynamic_cast (component.getPositioner()); + + if (current == 0 || ! current->isUsingRectangle (*this)) + { + RelativeRectangleComponentPositioner* p = new RelativeRectangleComponentPositioner (component, *this); + + component.setPositioner (p); + p->apply(); + } + } + else + { + component.setPositioner (0); + component.setBounds (resolve (0).getSmallestIntegerContainer()); + } +} + RelativePointPath::RelativePointPath() : usesNonZeroWinding (true), containsDynamicPoints (false) @@ -85678,10 +86051,12 @@ class LowLevelGraphicsSoftwareRenderer::CachedGlyph { public: CachedGlyph() : glyph (0), lastAccessCount (0) {} - ~CachedGlyph() {} - void draw (SavedState& state, const float x, const float y) const + void draw (SavedState& state, float x, const float y) const { + if (snapToIntegerCoordinate) + x = std::floor (x + 0.5f); + if (edgeTable != 0) state.fillEdgeTable (*edgeTable, x, roundToInt (y)); } @@ -85689,6 +86064,7 @@ public: void generate (const Font& newFont, const int glyphNumber) { font = newFont; + snapToIntegerCoordinate = newFont.getTypeface()->isHinted(); glyph = glyphNumber; edgeTable = 0; @@ -85709,8 +86085,9 @@ public: } } - int glyph, lastAccessCount; Font font; + int glyph, lastAccessCount; + bool snapToIntegerCoordinate; private: ScopedPointer edgeTable; @@ -85724,8 +86101,7 @@ public: GlyphCache() : accessCounter (0), hits (0), misses (0) { - for (int i = 120; --i >= 0;) - glyphs.add (new CachedGlyph()); + addNewGlyphSlots (120); } ~GlyphCache() @@ -85763,10 +86139,7 @@ public: if (hits + ++misses > (glyphs.size() << 4)) { if (misses * 2 > hits) - { - for (int i = 32; --i >= 0;) - glyphs.add (new CachedGlyph()); - } + addNewGlyphSlots (32); hits = misses = 0; oldest = glyphs.getLast(); @@ -85783,6 +86156,12 @@ private: OwnedArray glyphs; int accessCounter, hits, misses; + void addNewGlyphSlots (int num) + { + while (--num >= 0) + glyphs.add (new CachedGlyph()); + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache); }; @@ -89314,15 +89693,119 @@ namespace FontValues String fallbackFont; } -Font::SharedFontInternal::SharedFontInternal (const String& typefaceName_, const float height_, const float horizontalScale_, - const float kerning_, const float ascent_, const int styleFlags_, - Typeface* const typeface_) throw() +class TypefaceCache : public DeletedAtShutdown +{ +public: + TypefaceCache (int numToCache = 10) + : counter (1) + { + while (--numToCache >= 0) + faces.add (CachedFace()); + } + + ~TypefaceCache() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (TypefaceCache) + + const Typeface::Ptr findTypefaceFor (const Font& font) + { + // (can't initialise defaultFace in the constructor or in getDefaultTypeface() because of recursion). + if (defaultFace == 0) + defaultFace = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (Font()); + + const int flags = font.getStyleFlags() & (Font::bold | Font::italic); + const String faceName (font.getTypefaceName()); + + int i; + for (i = faces.size(); --i >= 0;) + { + CachedFace& face = faces.getReference(i); + + if (face.flags == flags + && face.typefaceName == faceName + && face.typeface->isSuitableForFont (font)) + { + face.lastUsageCount = ++counter; + return face.typeface; + } + } + + int replaceIndex = 0; + int bestLastUsageCount = std::numeric_limits::max(); + + for (i = faces.size(); --i >= 0;) + { + const int lu = faces.getReference(i).lastUsageCount; + + if (bestLastUsageCount > lu) + { + bestLastUsageCount = lu; + replaceIndex = i; + } + } + + CachedFace& face = faces.getReference (replaceIndex); + face.typefaceName = faceName; + face.flags = flags; + face.lastUsageCount = ++counter; + face.typeface = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); + jassert (face.typeface != 0); // the look and feel must return a typeface! + + return face.typeface; + } + + const Typeface::Ptr getDefaultTypeface() const throw() + { + return defaultFace; + } + +private: + struct CachedFace + { + CachedFace() throw() + : lastUsageCount (0), flags (-1) + { + } + + // Although it seems a bit wacky to store the name here, it's because it may be a + // placeholder rather than a real one, e.g. "" vs the actual typeface name. + // Since the typeface itself doesn't know that it may have this alias, the name under + // which it was fetched needs to be stored separately. + String typefaceName; + int lastUsageCount, flags; + Typeface::Ptr typeface; + }; + + Array faces; + Typeface::Ptr defaultFace; + int counter; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache); +}; + +juce_ImplementSingleton_SingleThreaded (TypefaceCache) + +Font::SharedFontInternal::SharedFontInternal (const String& typefaceName_, const float height_, const int styleFlags_) throw() : typefaceName (typefaceName_), height (height_), - horizontalScale (horizontalScale_), - kerning (kerning_), - ascent (ascent_), + horizontalScale (1.0f), + kerning (0), + ascent (0), styleFlags (styleFlags_), + typeface (TypefaceCache::getInstance()->getDefaultTypeface()) +{ +} + +Font::SharedFontInternal::SharedFontInternal (const Typeface::Ptr& typeface_) throw() + : typefaceName (typeface_->getName()), + height (FontValues::defaultFontHeight), + horizontalScale (1.0f), + kerning (0), + ascent (0), + styleFlags (Font::plain), typeface (typeface_) { } @@ -89338,23 +89821,32 @@ Font::SharedFontInternal::SharedFontInternal (const SharedFontInternal& other) t { } +bool Font::SharedFontInternal::operator== (const SharedFontInternal& other) const throw() +{ + return height == other.height + && styleFlags == other.styleFlags + && horizontalScale == other.horizontalScale + && kerning == other.kerning + && typefaceName == other.typefaceName; +} + Font::Font() - : font (new SharedFontInternal (getDefaultSansSerifFontName(), FontValues::defaultFontHeight, - 1.0f, 0, 0, Font::plain, 0)) + : font (new SharedFontInternal (getDefaultSansSerifFontName(), FontValues::defaultFontHeight, Font::plain)) { } Font::Font (const float fontHeight, const int styleFlags_) - : font (new SharedFontInternal (getDefaultSansSerifFontName(), FontValues::limitFontHeight (fontHeight), - 1.0f, 0, 0, styleFlags_, 0)) + : font (new SharedFontInternal (getDefaultSansSerifFontName(), FontValues::limitFontHeight (fontHeight), styleFlags_)) { } -Font::Font (const String& typefaceName_, - const float fontHeight, - const int styleFlags_) - : font (new SharedFontInternal (typefaceName_, FontValues::limitFontHeight (fontHeight), - 1.0f, 0, 0, styleFlags_, 0)) +Font::Font (const String& typefaceName_, const float fontHeight, const int styleFlags_) + : font (new SharedFontInternal (typefaceName_, FontValues::limitFontHeight (fontHeight), styleFlags_)) +{ +} + +Font::Font (const Typeface::Ptr& typeface) + : font (new SharedFontInternal (typeface)) { } @@ -89373,20 +89865,10 @@ Font::~Font() throw() { } -Font::Font (const Typeface::Ptr& typeface) - : font (new SharedFontInternal (typeface->getName(), FontValues::defaultFontHeight, - 1.0f, 0, 0, Font::plain, typeface)) -{ -} - bool Font::operator== (const Font& other) const throw() { return font == other.font - || (font->height == other.font->height - && font->styleFlags == other.font->styleFlags - && font->horizontalScale == other.font->horizontalScale - && font->kerning == other.font->kerning - && font->typefaceName == other.font->typefaceName); + || *font == *other.font; } bool Font::operator!= (const Font& other) const throw() @@ -89659,87 +90141,6 @@ const Font Font::fromString (const String& fontDescription) return Font (name, height, flags); } -class TypefaceCache : public DeletedAtShutdown -{ -public: - TypefaceCache (int numToCache = 10) - : counter (1) - { - while (--numToCache >= 0) - faces.add (new CachedFace()); - } - - ~TypefaceCache() - { - clearSingletonInstance(); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (TypefaceCache) - - const Typeface::Ptr findTypefaceFor (const Font& font) - { - const int flags = font.getStyleFlags() & (Font::bold | Font::italic); - const String faceName (font.getTypefaceName()); - - int i; - for (i = faces.size(); --i >= 0;) - { - CachedFace* const face = faces.getUnchecked(i); - - if (face->flags == flags - && face->typefaceName == faceName) - { - face->lastUsageCount = ++counter; - return face->typeFace; - } - } - - int replaceIndex = 0; - int bestLastUsageCount = std::numeric_limits::max(); - - for (i = faces.size(); --i >= 0;) - { - const int lu = faces.getUnchecked(i)->lastUsageCount; - - if (bestLastUsageCount > lu) - { - bestLastUsageCount = lu; - replaceIndex = i; - } - } - - CachedFace* const face = faces.getUnchecked (replaceIndex); - face->typefaceName = faceName; - face->flags = flags; - face->lastUsageCount = ++counter; - face->typeFace = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); - jassert (face->typeFace != 0); // the look and feel must return a typeface! - - return face->typeFace; - } - -private: - struct CachedFace - { - CachedFace() throw() - : lastUsageCount (0), flags (-1) - { - } - - String typefaceName; - int lastUsageCount; - int flags; - Typeface::Ptr typeFace; - }; - - int counter; - OwnedArray faces; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache); -}; - -juce_ImplementSingleton_SingleThreaded (TypefaceCache) - Typeface* Font::getTypeface() const { if (font->typeface == 0) diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 72932e04ae..9e7df3b605 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -73,7 +73,7 @@ namespace JuceDummyNamespace {} */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 3 +#define JUCE_BUILDNUMBER 4 /** Current Juce version number. @@ -17892,9 +17892,12 @@ public: /** Returns the type of this expression. */ Type getType() const throw(); - /** If this expression is a symbol, this returns its name. */ + /** If this expression is a symbol, this returns its full name. */ const String getSymbol() const; + /** For a symbol that contains a dot, this returns the two */ + void getSymbolParts (String& objectName, String& memberName) const; + /** If this expression is a function, this returns its name. */ const String getFunction() const; @@ -17936,7 +17939,7 @@ private: double overallTarget, Term* topLevelTerm) const; virtual const ReferenceCountedObjectPtr negated(); virtual Type getType() const throw() = 0; - virtual const String getSymbolName() const; + virtual void getSymbolParts (String& objectName, String& memberName) const; virtual const String getFunctionName() const; private: @@ -23409,6 +23412,12 @@ public: /** Destructor. */ virtual ~Typeface(); + /** Returns true if this typeface can be used to render the specified font. + When called, the font will already have been checked to make sure that its name and + style flags match the typeface. + */ + virtual bool isSuitableForFont (const Font&) const { return true; } + /** Returns the ascent of the font, as a proportion of its height. The height is considered to always be normalised as 1.0, so this will be a value less that 1.0, indicating the proportion of the font that lies above @@ -23445,6 +23454,9 @@ public: */ virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0; + /** Returns true if the typeface uses hinting. */ + virtual bool isHinted() const { return false; } + protected: String name; @@ -23887,11 +23899,12 @@ private: class SharedFontInternal : public ReferenceCountedObject { public: - SharedFontInternal (const String& typefaceName, float height, float horizontalScale, - float kerning, float ascent, int styleFlags, - Typeface* typeface) throw(); + SharedFontInternal (const String& typefaceName, float height, int styleFlags) throw(); + SharedFontInternal (const Typeface::Ptr& typeface) throw(); SharedFontInternal (const SharedFontInternal& other) throw(); + bool operator== (const SharedFontInternal&) const throw(); + String typefaceName; float height, horizontalScale, kerning, ascent; int styleFlags; @@ -28878,7 +28891,7 @@ public: the list iterator to stop cleanly if the component is deleted by a listener callback while the list is still being iterated. */ - class BailOutChecker + class JUCE_API BailOutChecker { public: /** Creates a checker that watches one component. */ @@ -28893,16 +28906,49 @@ public: JUCE_DECLARE_NON_COPYABLE (BailOutChecker); }; - #ifndef DOXYGEN - /** This method is deprecated - use localPointToGlobal instead. */ - const Point relativePositionToGlobal (const Point& relativePosition) const; + /** + Base class for objects that can be used to automatically position a component according to + some kind of algorithm. - /** This method is deprecated - use getLocalPoint instead. */ - const Point globalPositionToRelative (const Point& screenPosition) const; + The component class simply holds onto a reference to a Positioner, but doesn't actually do + anything with it - all the functionality must be implemented by the positioner itself (e.g. + it might choose to watch some kind of value and move the component when the value changes). + */ + class JUCE_API Positioner + { + public: + /** Creates a Positioner which can control the specified component. */ + explicit Positioner (Component& component) throw(); + /** Destructor. */ + virtual ~Positioner() {} + + /** Returns the component that this positioner controls. */ + Component& getComponent() const throw() { return component; } + + private: + Component& component; - /** This method is deprecated - use getLocalPoint instead. */ - const Point relativePositionToOtherComponent (const Component* targetComponent, - const Point& positionRelativeToThis) const; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner); + }; + + /** Returns the Positioner object that has been set for this component. + @see setPositioner() + */ + Positioner* getPositioner() const throw(); + + /** Sets a new Positioner object for this component. + If there's currently another positioner set, it will be deleted. The object that is passed in + will be deleted automatically by this component when it's no longer required. Pass a null pointer + to clear the current positioner. + @see getPositioner() + */ + void setPositioner (Positioner* newPositioner); + + #ifndef DOXYGEN + // These methods are deprecated - use localPointToGlobal, getLocalPoint, getLocalPoint, etc instead. + JUCE_DEPRECATED (const Point relativePositionToGlobal (const Point&) const); + JUCE_DEPRECATED (const Point globalPositionToRelative (const Point&) const); + JUCE_DEPRECATED (const Point relativePositionToOtherComponent (const Component*, const Point&) const); #endif private: @@ -28917,6 +28963,7 @@ private: String componentName, componentID; Component* parentComponent; Rectangle bounds; + ScopedPointer positioner; ScopedPointer affineTransform; Array childComponentList; LookAndFeel* lookAndFeel; @@ -33468,6 +33515,9 @@ public: /** Returns true if the low res preview is fully generated. */ bool isFullyLoaded() const throw(); + /** Returns the number of samples that have been set in the thumbnail. */ + int64 getNumSamplesFinished() const throw(); + /** Returns the highest level in the thumbnail. Note that because the thumb only stores low-resolution data, this isn't an accurate representation of the highest value, it's only a rough approximation. @@ -33477,8 +33527,10 @@ public: /** Returns the hash code that was set by setSource() or setReader(). */ int64 getHashCode() const; + #ifndef DOXYGEN // (this is only public to avoid a VC6 bug) class LevelDataSource; + #endif private: @@ -42737,7 +42789,8 @@ public: @see selectRow */ void selectRowsBasedOnModifierKeys (int rowThatWasClickedOn, - const ModifierKeys& modifiers); + const ModifierKeys& modifiers, + bool isMouseUpEvent); /** Scrolls the list to a particular position. @@ -45291,6 +45344,8 @@ private: #ifndef __JUCE_RELATIVECOORDINATE_JUCEHEADER__ #define __JUCE_RELATIVECOORDINATE_JUCEHEADER__ +class Component; + /** Expresses a coordinate as a dynamically evaluated expression. @@ -45372,6 +45427,7 @@ public: struct Strings { static const String parent; /**< "parent" */ + static const String this_; /**< "this" */ static const String left; /**< "left" */ static const String right; /**< "right" */ static const String top; /**< "top" */ @@ -45498,6 +45554,9 @@ public: */ void moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* evaluationContext); + /** Returns true if this rectangle depends on any other coordinates for its position. */ + bool isDynamic() const; + /** Returns a string which represents this point. This returns a comma-separated list of coordinates, in the order left, top, right, bottom. For details of the string syntax used by the coordinates, see the RelativeCoordinate constructor notes. @@ -45510,6 +45569,9 @@ public: */ void renameSymbolIfUsed (const String& oldName, const String& newName); + /** */ + void applyToComponent (Component& component) const; + // The actual rectangle coords... RelativeCoordinate left, right, top, bottom; }; @@ -53971,11 +54033,8 @@ private: This class is used to store sets of X and Y marker points in components. @see Component::getMarkers(). - - The MarkerList is also a ChangeBroadcaster, so that listeners can register to receive - a callback when a marker is moved, */ -class JUCE_API MarkerList : public ChangeBroadcaster +class JUCE_API MarkerList { public: @@ -54038,6 +54097,36 @@ public: /** Returns true if not all the markers in these two lists match exactly. */ bool operator!= (const MarkerList& other) const throw(); + /** + A class for receiving events when changes are made to a MarkerList. + + You can register a MarkerList::Listener with a MarkerList using the MarkerList::addListener() + method, and it will be called when markers are moved, added, or deleted. + + @see MarkerList::addListener, MarkerList::removeListener + */ + class JUCE_API Listener + { + public: + /** Destructor. */ + virtual ~Listener() {} + + /** Called when something in the given marker list changes. */ + virtual void markersChanged (MarkerList* markerList) = 0; + + /** Called when the given marker list is being deleted. */ + virtual void markerListBeingDeleted (MarkerList* markerList); + }; + + /** Registers a listener that will be called when the markers are changed. */ + void addListener (Listener* listener); + + /** Deregisters a previously-registered listener. */ + void removeListener (Listener* listener); + + /** Synchronously calls markersChanged() on all the registered listeners. */ + void markersHaveChanged(); + /** Forms a wrapper around a ValueTree that can be used for storing a MarkerList. */ class ValueTreeWrapper { @@ -54065,7 +54154,7 @@ public: private: OwnedArray markers; - void markersHaveChanged(); + ListenerList listeners; JUCE_LEAK_DETECTOR (MarkerList); }; diff --git a/src/audio/audio_file_formats/juce_AudioThumbnail.h b/src/audio/audio_file_formats/juce_AudioThumbnail.h index a3b22fc4bf..9113ffb114 100644 --- a/src/audio/audio_file_formats/juce_AudioThumbnail.h +++ b/src/audio/audio_file_formats/juce_AudioThumbnail.h @@ -177,6 +177,9 @@ public: /** Returns true if the low res preview is fully generated. */ bool isFullyLoaded() const throw(); + /** Returns the number of samples that have been set in the thumbnail. */ + int64 getNumSamplesFinished() const throw(); + /** Returns the highest level in the thumbnail. Note that because the thumb only stores low-resolution data, this isn't an accurate representation of the highest value, it's only a rough approximation. @@ -186,8 +189,10 @@ public: /** Returns the hash code that was set by setSource() or setReader(). */ int64 getHashCode() const; + #ifndef DOXYGEN // (this is only public to avoid a VC6 bug) class LevelDataSource; + #endif private: //============================================================================== diff --git a/src/containers/juce_NamedValueSet.cpp b/src/containers/juce_NamedValueSet.cpp index 45794e2dd2..3bb9c76018 100644 --- a/src/containers/juce_NamedValueSet.cpp +++ b/src/containers/juce_NamedValueSet.cpp @@ -1,216 +1,216 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-10 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - 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. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#include "../core/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - -#include "juce_NamedValueSet.h" -#include "../text/juce_XmlElement.h" - - -//============================================================================== -NamedValueSet::NamedValue::NamedValue() throw() -{ -} - -inline NamedValueSet::NamedValue::NamedValue (const Identifier& name_, const var& value_) - : name (name_), value (value_) -{ -} - -bool NamedValueSet::NamedValue::operator== (const NamedValueSet::NamedValue& other) const throw() -{ - return name == other.name && value == other.value; -} - -//============================================================================== -NamedValueSet::NamedValueSet() throw() -{ -} - -NamedValueSet::NamedValueSet (const NamedValueSet& other) -{ - values.addCopyOfList (other.values); -} - -NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) -{ - clear(); - values.addCopyOfList (other.values); - return *this; -} - -NamedValueSet::~NamedValueSet() -{ - clear(); -} - -void NamedValueSet::clear() -{ - values.deleteAll(); -} - -bool NamedValueSet::operator== (const NamedValueSet& other) const -{ - const NamedValue* i1 = values; - const NamedValue* i2 = other.values; - - while (i1 != 0 && i2 != 0) - { - if (! (*i1 == *i2)) - return false; - - i1 = i1->nextListItem; - i2 = i2->nextListItem; - } - - return true; -} - -bool NamedValueSet::operator!= (const NamedValueSet& other) const -{ - return ! operator== (other); -} - -int NamedValueSet::size() const throw() -{ - return values.size(); -} - -const var& NamedValueSet::operator[] (const Identifier& name) const -{ - for (NamedValue* i = values; i != 0; i = i->nextListItem) - if (i->name == name) - return i->value; - - return var::null; -} - -const var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const -{ - const var* v = getVarPointer (name); - return v != 0 ? *v : defaultReturnValue; -} - -var* NamedValueSet::getVarPointer (const Identifier& name) const -{ - for (NamedValue* i = values; i != 0; i = i->nextListItem) - if (i->name == name) - return &(i->value); - - return 0; -} - -bool NamedValueSet::set (const Identifier& name, const var& newValue) -{ - LinkedListPointer* i = &values; - - while (i->get() != 0) - { - NamedValue* const v = i->get(); - - if (v->name == name) - { - if (v->value == newValue) - return false; - - v->value = newValue; - return true; - } - - i = &(v->nextListItem); - } - - i->insertNext (new NamedValue (name, newValue)); - return true; -} - -bool NamedValueSet::contains (const Identifier& name) const -{ - return getVarPointer (name) != 0; -} - -bool NamedValueSet::remove (const Identifier& name) -{ - LinkedListPointer* i = &values; - - for (;;) - { - NamedValue* const v = i->get(); - - if (v == 0) - break; - - if (v->name == name) - { - delete i->removeNext(); - return true; - } - - i = &(v->nextListItem); - } - - return false; -} - -const Identifier NamedValueSet::getName (const int index) const -{ - const NamedValue* const v = values[index]; - jassert (v != 0); - return v->name; -} - -const var NamedValueSet::getValueAt (const int index) const -{ - const NamedValue* const v = values[index]; - jassert (v != 0); - return v->value; -} - -void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) -{ - clear(); - LinkedListPointer::Appender appender (values); - - const int numAtts = xml.getNumAttributes(); // xxx inefficient - should write an att iterator.. - - for (int i = 0; i < numAtts; ++i) - appender.append (new NamedValue (xml.getAttributeName (i), var (xml.getAttributeValue (i)))); -} - -void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const -{ - for (NamedValue* i = values; i != 0; i = i->nextListItem) - { - jassert (! i->value.isObject()); // DynamicObjects can't be stored as XML! - - xml.setAttribute (i->name.toString(), - i->value.toString()); - } -} - - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#include "../core/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_NamedValueSet.h" +#include "../text/juce_XmlElement.h" + + +//============================================================================== +NamedValueSet::NamedValue::NamedValue() throw() +{ +} + +inline NamedValueSet::NamedValue::NamedValue (const Identifier& name_, const var& value_) + : name (name_), value (value_) +{ +} + +bool NamedValueSet::NamedValue::operator== (const NamedValueSet::NamedValue& other) const throw() +{ + return name == other.name && value == other.value; +} + +//============================================================================== +NamedValueSet::NamedValueSet() throw() +{ +} + +NamedValueSet::NamedValueSet (const NamedValueSet& other) +{ + values.addCopyOfList (other.values); +} + +NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) +{ + clear(); + values.addCopyOfList (other.values); + return *this; +} + +NamedValueSet::~NamedValueSet() +{ + clear(); +} + +void NamedValueSet::clear() +{ + values.deleteAll(); +} + +bool NamedValueSet::operator== (const NamedValueSet& other) const +{ + const NamedValue* i1 = values; + const NamedValue* i2 = other.values; + + while (i1 != 0 && i2 != 0) + { + if (! (*i1 == *i2)) + return false; + + i1 = i1->nextListItem; + i2 = i2->nextListItem; + } + + return true; +} + +bool NamedValueSet::operator!= (const NamedValueSet& other) const +{ + return ! operator== (other); +} + +int NamedValueSet::size() const throw() +{ + return values.size(); +} + +const var& NamedValueSet::operator[] (const Identifier& name) const +{ + for (NamedValue* i = values; i != 0; i = i->nextListItem) + if (i->name == name) + return i->value; + + return var::null; +} + +const var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const +{ + const var* v = getVarPointer (name); + return v != 0 ? *v : defaultReturnValue; +} + +var* NamedValueSet::getVarPointer (const Identifier& name) const +{ + for (NamedValue* i = values; i != 0; i = i->nextListItem) + if (i->name == name) + return &(i->value); + + return 0; +} + +bool NamedValueSet::set (const Identifier& name, const var& newValue) +{ + LinkedListPointer* i = &values; + + while (i->get() != 0) + { + NamedValue* const v = i->get(); + + if (v->name == name) + { + if (v->value == newValue) + return false; + + v->value = newValue; + return true; + } + + i = &(v->nextListItem); + } + + i->insertNext (new NamedValue (name, newValue)); + return true; +} + +bool NamedValueSet::contains (const Identifier& name) const +{ + return getVarPointer (name) != 0; +} + +bool NamedValueSet::remove (const Identifier& name) +{ + LinkedListPointer* i = &values; + + for (;;) + { + NamedValue* const v = i->get(); + + if (v == 0) + break; + + if (v->name == name) + { + delete i->removeNext(); + return true; + } + + i = &(v->nextListItem); + } + + return false; +} + +const Identifier NamedValueSet::getName (const int index) const +{ + const NamedValue* const v = values[index]; + jassert (v != 0); + return v->name; +} + +const var NamedValueSet::getValueAt (const int index) const +{ + const NamedValue* const v = values[index]; + jassert (v != 0); + return v->value; +} + +void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) +{ + clear(); + LinkedListPointer::Appender appender (values); + + const int numAtts = xml.getNumAttributes(); // xxx inefficient - should write an att iterator.. + + for (int i = 0; i < numAtts; ++i) + appender.append (new NamedValue (xml.getAttributeName (i), var (xml.getAttributeValue (i)))); +} + +void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const +{ + for (NamedValue* i = values; i != 0; i = i->nextListItem) + { + jassert (! i->value.isObject()); // DynamicObjects can't be stored as XML! + + xml.setAttribute (i->name.toString(), + i->value.toString()); + } +} + + +END_JUCE_NAMESPACE diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index b2b5aa236c..29ebfae444 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 3 +#define JUCE_BUILDNUMBER 4 /** Current Juce version number. diff --git a/src/gui/components/controls/juce_ListBox.cpp b/src/gui/components/controls/juce_ListBox.cpp index 296296033d..547d1aa283 100644 --- a/src/gui/components/controls/juce_ListBox.cpp +++ b/src/gui/components/controls/juce_ListBox.cpp @@ -80,7 +80,7 @@ public: { if (! selected) { - owner.selectRowsBasedOnModifierKeys (row, e.mods); + owner.selectRowsBasedOnModifierKeys (row, e.mods, false); if (owner.getModel() != 0) owner.getModel()->listBoxItemClicked (row, e); @@ -96,7 +96,7 @@ public: { if (isEnabled() && selectRowOnMouseUp && ! isDragging) { - owner.selectRowsBasedOnModifierKeys (row, e.mods); + owner.selectRowsBasedOnModifierKeys (row, e.mods, true); if (owner.getModel() != 0) owner.getModel()->listBoxItemClicked (row, e); @@ -571,7 +571,8 @@ void ListBox::deselectAllRows() } void ListBox::selectRowsBasedOnModifierKeys (const int row, - const ModifierKeys& mods) + const ModifierKeys& mods, + const bool isMouseUpEvent) { if (multipleSelection && mods.isCommandDown()) { @@ -583,7 +584,7 @@ void ListBox::selectRowsBasedOnModifierKeys (const int row, } else if ((! mods.isPopupMenu()) || ! isRowSelected (row)) { - selectRowInternal (row, false, ! (multipleSelection && isRowSelected (row)), true); + selectRowInternal (row, false, ! (multipleSelection && (! isMouseUpEvent) && isRowSelected (row)), true); } } diff --git a/src/gui/components/controls/juce_ListBox.h b/src/gui/components/controls/juce_ListBox.h index 21a7315702..9e8b97d0ae 100644 --- a/src/gui/components/controls/juce_ListBox.h +++ b/src/gui/components/controls/juce_ListBox.h @@ -330,7 +330,8 @@ public: @see selectRow */ void selectRowsBasedOnModifierKeys (int rowThatWasClickedOn, - const ModifierKeys& modifiers); + const ModifierKeys& modifiers, + bool isMouseUpEvent); //============================================================================== /** Scrolls the list to a particular position. diff --git a/src/gui/components/controls/juce_TableListBox.cpp b/src/gui/components/controls/juce_TableListBox.cpp index 60ef5ffb81..e22bd4ef4c 100644 --- a/src/gui/components/controls/juce_TableListBox.cpp +++ b/src/gui/components/controls/juce_TableListBox.cpp @@ -146,7 +146,7 @@ public: { if (! isSelected) { - owner.selectRowsBasedOnModifierKeys (row, e.mods); + owner.selectRowsBasedOnModifierKeys (row, e.mods, false); const int columnId = owner.getHeader().getColumnIdAtX (e.x); @@ -183,7 +183,7 @@ public: { if (selectRowOnMouseUp && e.mouseWasClicked() && isEnabled()) { - owner.selectRowsBasedOnModifierKeys (row, e.mods); + owner.selectRowsBasedOnModifierKeys (row, e.mods, true); const int columnId = owner.getHeader().getColumnIdAtX (e.x); diff --git a/src/gui/components/filebrowser/juce_FileListComponent.cpp b/src/gui/components/filebrowser/juce_FileListComponent.cpp index 20402b46e3..002f74d525 100644 --- a/src/gui/components/filebrowser/juce_FileListComponent.cpp +++ b/src/gui/components/filebrowser/juce_FileListComponent.cpp @@ -110,7 +110,7 @@ public: void mouseDown (const MouseEvent& e) { - owner.selectRowsBasedOnModifierKeys (index, e.mods); + owner.selectRowsBasedOnModifierKeys (index, e.mods, false); owner.sendMouseClickMessage (file, e); } diff --git a/src/gui/components/juce_Component.cpp b/src/gui/components/juce_Component.cpp index 99dc9e0d94..81ce32d20d 100644 --- a/src/gui/components/juce_Component.cpp +++ b/src/gui/components/juce_Component.cpp @@ -2055,6 +2055,24 @@ MarkerList* Component::getMarkers (bool /*xAxis*/) return 0; } +//============================================================================== +Component::Positioner::Positioner (Component& component_) throw() + : component (component_) +{ +} + +Component::Positioner* Component::getPositioner() const throw() +{ + return positioner; +} + +void Component::setPositioner (Positioner* newPositioner) +{ + // You can only assign a positioner to the component that it was created for! + jassert (newPositioner == 0 || this == &(newPositioner->getComponent())); + positioner = newPositioner; +} + //============================================================================== const Rectangle Component::getLocalBounds() const throw() { diff --git a/src/gui/components/juce_Component.h b/src/gui/components/juce_Component.h index 98858a5528..e763f4f2ab 100644 --- a/src/gui/components/juce_Component.h +++ b/src/gui/components/juce_Component.h @@ -2093,7 +2093,7 @@ public: the list iterator to stop cleanly if the component is deleted by a listener callback while the list is still being iterated. */ - class BailOutChecker + class JUCE_API BailOutChecker { public: /** Creates a checker that watches one component. */ @@ -2109,16 +2109,50 @@ public: }; //============================================================================== - #ifndef DOXYGEN - /** This method is deprecated - use localPointToGlobal instead. */ - const Point relativePositionToGlobal (const Point& relativePosition) const; + /** + Base class for objects that can be used to automatically position a component according to + some kind of algorithm. + + The component class simply holds onto a reference to a Positioner, but doesn't actually do + anything with it - all the functionality must be implemented by the positioner itself (e.g. + it might choose to watch some kind of value and move the component when the value changes). + */ + class JUCE_API Positioner + { + public: + /** Creates a Positioner which can control the specified component. */ + explicit Positioner (Component& component) throw(); + /** Destructor. */ + virtual ~Positioner() {} + + /** Returns the component that this positioner controls. */ + Component& getComponent() const throw() { return component; } - /** This method is deprecated - use getLocalPoint instead. */ - const Point globalPositionToRelative (const Point& screenPosition) const; + private: + Component& component; - /** This method is deprecated - use getLocalPoint instead. */ - const Point relativePositionToOtherComponent (const Component* targetComponent, - const Point& positionRelativeToThis) const; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner); + }; + + /** Returns the Positioner object that has been set for this component. + @see setPositioner() + */ + Positioner* getPositioner() const throw(); + + /** Sets a new Positioner object for this component. + If there's currently another positioner set, it will be deleted. The object that is passed in + will be deleted automatically by this component when it's no longer required. Pass a null pointer + to clear the current positioner. + @see getPositioner() + */ + void setPositioner (Positioner* newPositioner); + + //============================================================================== + #ifndef DOXYGEN + // These methods are deprecated - use localPointToGlobal, getLocalPoint, getLocalPoint, etc instead. + JUCE_DEPRECATED (const Point relativePositionToGlobal (const Point&) const); + JUCE_DEPRECATED (const Point globalPositionToRelative (const Point&) const); + JUCE_DEPRECATED (const Point relativePositionToOtherComponent (const Component*, const Point&) const); #endif private: @@ -2134,6 +2168,7 @@ private: String componentName, componentID; Component* parentComponent; Rectangle bounds; + ScopedPointer positioner; ScopedPointer affineTransform; Array childComponentList; LookAndFeel* lookAndFeel; diff --git a/src/gui/components/layout/juce_ComponentBuilder.cpp b/src/gui/components/layout/juce_ComponentBuilder.cpp index d76ea7cf11..82b217f5b9 100644 --- a/src/gui/components/layout/juce_ComponentBuilder.cpp +++ b/src/gui/components/layout/juce_ComponentBuilder.cpp @@ -85,24 +85,20 @@ namespace ComponentBuilderHelpers if (topLevelComp != 0) { - const String compId (getStateId (state)); + ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state); - if (compId.isEmpty() && state.getParent().isValid()) + if (type == 0) { // ..handle the case where a child of the actual state node has changed. - updateComponent (builder, state.getParent()); + if (state.getParent().isValid()) + updateComponent (builder, state.getParent()); } else { - ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state); + Component* const changedComp = findComponentWithID (topLevelComp, getStateId (state)); - if (type != 0) - { - Component* const changedComp = findComponentWithID (topLevelComp, compId); - - if (changedComp != 0) - type->updateComponentFromState (changedComp, state); - } + if (changedComp != 0) + type->updateComponentFromState (changedComp, state); } } } diff --git a/src/gui/components/layout/juce_MarkerList.cpp b/src/gui/components/layout/juce_MarkerList.cpp index 1cb64c36c3..be86a438df 100644 --- a/src/gui/components/layout/juce_MarkerList.cpp +++ b/src/gui/components/layout/juce_MarkerList.cpp @@ -54,6 +54,7 @@ MarkerList& MarkerList::operator= (const MarkerList& other) MarkerList::~MarkerList() { + listeners.call (&MarkerList::Listener::markerListBeingDeleted, this); } bool MarkerList::operator== (const MarkerList& other) const throw() @@ -148,7 +149,21 @@ void MarkerList::removeMarker (const String& name) void MarkerList::markersHaveChanged() { - sendChangeMessage(); + listeners.call (&MarkerList::Listener::markersChanged, this); +} + +void MarkerList::Listener::markerListBeingDeleted (MarkerList* markerList) +{ +} + +void MarkerList::addListener (Listener* listener) +{ + listeners.add (listener); +} + +void MarkerList::removeListener (Listener* listener) +{ + listeners.remove (listener); } //============================================================================== diff --git a/src/gui/components/layout/juce_MarkerList.h b/src/gui/components/layout/juce_MarkerList.h index b11b933e81..016b4c49e6 100644 --- a/src/gui/components/layout/juce_MarkerList.h +++ b/src/gui/components/layout/juce_MarkerList.h @@ -36,11 +36,8 @@ This class is used to store sets of X and Y marker points in components. @see Component::getMarkers(). - - The MarkerList is also a ChangeBroadcaster, so that listeners can register to receive - a callback when a marker is moved, */ -class JUCE_API MarkerList : public ChangeBroadcaster +class JUCE_API MarkerList { public: //============================================================================== @@ -105,6 +102,37 @@ public: /** Returns true if not all the markers in these two lists match exactly. */ bool operator!= (const MarkerList& other) const throw(); + //============================================================================== + /** + A class for receiving events when changes are made to a MarkerList. + + You can register a MarkerList::Listener with a MarkerList using the MarkerList::addListener() + method, and it will be called when markers are moved, added, or deleted. + + @see MarkerList::addListener, MarkerList::removeListener + */ + class JUCE_API Listener + { + public: + /** Destructor. */ + virtual ~Listener() {} + + /** Called when something in the given marker list changes. */ + virtual void markersChanged (MarkerList* markerList) = 0; + + /** Called when the given marker list is being deleted. */ + virtual void markerListBeingDeleted (MarkerList* markerList); + }; + + /** Registers a listener that will be called when the markers are changed. */ + void addListener (Listener* listener); + + /** Deregisters a previously-registered listener. */ + void removeListener (Listener* listener); + + /** Synchronously calls markersChanged() on all the registered listeners. */ + void markersHaveChanged(); + //============================================================================== /** Forms a wrapper around a ValueTree that can be used for storing a MarkerList. */ class ValueTreeWrapper @@ -133,7 +161,7 @@ public: private: //============================================================================== OwnedArray markers; - void markersHaveChanged(); + ListenerList listeners; JUCE_LEAK_DETECTOR (MarkerList); }; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index 554131e402..7de8986454 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -2394,10 +2394,12 @@ class LowLevelGraphicsSoftwareRenderer::CachedGlyph { public: CachedGlyph() : glyph (0), lastAccessCount (0) {} - ~CachedGlyph() {} - void draw (SavedState& state, const float x, const float y) const + void draw (SavedState& state, float x, const float y) const { + if (snapToIntegerCoordinate) + x = std::floor (x + 0.5f); + if (edgeTable != 0) state.fillEdgeTable (*edgeTable, x, roundToInt (y)); } @@ -2405,6 +2407,7 @@ public: void generate (const Font& newFont, const int glyphNumber) { font = newFont; + snapToIntegerCoordinate = newFont.getTypeface()->isHinted(); glyph = glyphNumber; edgeTable = 0; @@ -2425,8 +2428,9 @@ public: } } - int glyph, lastAccessCount; Font font; + int glyph, lastAccessCount; + bool snapToIntegerCoordinate; private: ScopedPointer edgeTable; @@ -2441,8 +2445,7 @@ public: GlyphCache() : accessCounter (0), hits (0), misses (0) { - for (int i = 120; --i >= 0;) - glyphs.add (new CachedGlyph()); + addNewGlyphSlots (120); } ~GlyphCache() @@ -2481,10 +2484,7 @@ public: if (hits + ++misses > (glyphs.size() << 4)) { if (misses * 2 > hits) - { - for (int i = 32; --i >= 0;) - glyphs.add (new CachedGlyph()); - } + addNewGlyphSlots (32); hits = misses = 0; oldest = glyphs.getLast(); @@ -2501,6 +2501,12 @@ private: OwnedArray glyphs; int accessCounter, hits, misses; + void addNewGlyphSlots (int num) + { + while (--num >= 0) + glyphs.add (new CachedGlyph()); + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache); }; diff --git a/src/gui/graphics/fonts/juce_Font.cpp b/src/gui/graphics/fonts/juce_Font.cpp index d827bb6868..748fd156f0 100644 --- a/src/gui/graphics/fonts/juce_Font.cpp +++ b/src/gui/graphics/fonts/juce_Font.cpp @@ -48,15 +48,121 @@ namespace FontValues } //============================================================================== -Font::SharedFontInternal::SharedFontInternal (const String& typefaceName_, const float height_, const float horizontalScale_, - const float kerning_, const float ascent_, const int styleFlags_, - Typeface* const typeface_) throw() +class TypefaceCache : public DeletedAtShutdown +{ +public: + TypefaceCache (int numToCache = 10) + : counter (1) + { + while (--numToCache >= 0) + faces.add (CachedFace()); + } + + ~TypefaceCache() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (TypefaceCache) + + const Typeface::Ptr findTypefaceFor (const Font& font) + { + // (can't initialise defaultFace in the constructor or in getDefaultTypeface() because of recursion). + if (defaultFace == 0) + defaultFace = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (Font()); + + const int flags = font.getStyleFlags() & (Font::bold | Font::italic); + const String faceName (font.getTypefaceName()); + + int i; + for (i = faces.size(); --i >= 0;) + { + CachedFace& face = faces.getReference(i); + + if (face.flags == flags + && face.typefaceName == faceName + && face.typeface->isSuitableForFont (font)) + { + face.lastUsageCount = ++counter; + return face.typeface; + } + } + + int replaceIndex = 0; + int bestLastUsageCount = std::numeric_limits::max(); + + for (i = faces.size(); --i >= 0;) + { + const int lu = faces.getReference(i).lastUsageCount; + + if (bestLastUsageCount > lu) + { + bestLastUsageCount = lu; + replaceIndex = i; + } + } + + CachedFace& face = faces.getReference (replaceIndex); + face.typefaceName = faceName; + face.flags = flags; + face.lastUsageCount = ++counter; + face.typeface = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); + jassert (face.typeface != 0); // the look and feel must return a typeface! + + return face.typeface; + } + + const Typeface::Ptr getDefaultTypeface() const throw() + { + return defaultFace; + } + +private: + struct CachedFace + { + CachedFace() throw() + : lastUsageCount (0), flags (-1) + { + } + + // Although it seems a bit wacky to store the name here, it's because it may be a + // placeholder rather than a real one, e.g. "" vs the actual typeface name. + // Since the typeface itself doesn't know that it may have this alias, the name under + // which it was fetched needs to be stored separately. + String typefaceName; + int lastUsageCount, flags; + Typeface::Ptr typeface; + }; + + Array faces; + Typeface::Ptr defaultFace; + int counter; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache); +}; + +juce_ImplementSingleton_SingleThreaded (TypefaceCache) + + +//============================================================================== +Font::SharedFontInternal::SharedFontInternal (const String& typefaceName_, const float height_, const int styleFlags_) throw() : typefaceName (typefaceName_), height (height_), - horizontalScale (horizontalScale_), - kerning (kerning_), - ascent (ascent_), + horizontalScale (1.0f), + kerning (0), + ascent (0), styleFlags (styleFlags_), + typeface (TypefaceCache::getInstance()->getDefaultTypeface()) +{ +} + +Font::SharedFontInternal::SharedFontInternal (const Typeface::Ptr& typeface_) throw() + : typefaceName (typeface_->getName()), + height (FontValues::defaultFontHeight), + horizontalScale (1.0f), + kerning (0), + ascent (0), + styleFlags (Font::plain), typeface (typeface_) { } @@ -72,25 +178,33 @@ Font::SharedFontInternal::SharedFontInternal (const SharedFontInternal& other) t { } +bool Font::SharedFontInternal::operator== (const SharedFontInternal& other) const throw() +{ + return height == other.height + && styleFlags == other.styleFlags + && horizontalScale == other.horizontalScale + && kerning == other.kerning + && typefaceName == other.typefaceName; +} //============================================================================== Font::Font() - : font (new SharedFontInternal (getDefaultSansSerifFontName(), FontValues::defaultFontHeight, - 1.0f, 0, 0, Font::plain, 0)) + : font (new SharedFontInternal (getDefaultSansSerifFontName(), FontValues::defaultFontHeight, Font::plain)) { } Font::Font (const float fontHeight, const int styleFlags_) - : font (new SharedFontInternal (getDefaultSansSerifFontName(), FontValues::limitFontHeight (fontHeight), - 1.0f, 0, 0, styleFlags_, 0)) + : font (new SharedFontInternal (getDefaultSansSerifFontName(), FontValues::limitFontHeight (fontHeight), styleFlags_)) { } -Font::Font (const String& typefaceName_, - const float fontHeight, - const int styleFlags_) - : font (new SharedFontInternal (typefaceName_, FontValues::limitFontHeight (fontHeight), - 1.0f, 0, 0, styleFlags_, 0)) +Font::Font (const String& typefaceName_, const float fontHeight, const int styleFlags_) + : font (new SharedFontInternal (typefaceName_, FontValues::limitFontHeight (fontHeight), styleFlags_)) +{ +} + +Font::Font (const Typeface::Ptr& typeface) + : font (new SharedFontInternal (typeface)) { } @@ -109,20 +223,10 @@ Font::~Font() throw() { } -Font::Font (const Typeface::Ptr& typeface) - : font (new SharedFontInternal (typeface->getName(), FontValues::defaultFontHeight, - 1.0f, 0, 0, Font::plain, typeface)) -{ -} - bool Font::operator== (const Font& other) const throw() { return font == other.font - || (font->height == other.font->height - && font->styleFlags == other.font->styleFlags - && font->horizontalScale == other.font->horizontalScale - && font->kerning == other.font->kerning - && font->typefaceName == other.font->typefaceName); + || *font == *other.font; } bool Font::operator!= (const Font& other) const throw() @@ -400,88 +504,6 @@ const Font Font::fromString (const String& fontDescription) } //============================================================================== -class TypefaceCache : public DeletedAtShutdown -{ -public: - TypefaceCache (int numToCache = 10) - : counter (1) - { - while (--numToCache >= 0) - faces.add (new CachedFace()); - } - - ~TypefaceCache() - { - clearSingletonInstance(); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (TypefaceCache) - - const Typeface::Ptr findTypefaceFor (const Font& font) - { - const int flags = font.getStyleFlags() & (Font::bold | Font::italic); - const String faceName (font.getTypefaceName()); - - int i; - for (i = faces.size(); --i >= 0;) - { - CachedFace* const face = faces.getUnchecked(i); - - if (face->flags == flags - && face->typefaceName == faceName) - { - face->lastUsageCount = ++counter; - return face->typeFace; - } - } - - int replaceIndex = 0; - int bestLastUsageCount = std::numeric_limits::max(); - - for (i = faces.size(); --i >= 0;) - { - const int lu = faces.getUnchecked(i)->lastUsageCount; - - if (bestLastUsageCount > lu) - { - bestLastUsageCount = lu; - replaceIndex = i; - } - } - - CachedFace* const face = faces.getUnchecked (replaceIndex); - face->typefaceName = faceName; - face->flags = flags; - face->lastUsageCount = ++counter; - face->typeFace = LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); - jassert (face->typeFace != 0); // the look and feel must return a typeface! - - return face->typeFace; - } - -private: - struct CachedFace - { - CachedFace() throw() - : lastUsageCount (0), flags (-1) - { - } - - String typefaceName; - int lastUsageCount; - int flags; - Typeface::Ptr typeFace; - }; - - int counter; - OwnedArray faces; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache); -}; - -juce_ImplementSingleton_SingleThreaded (TypefaceCache) - - Typeface* Font::getTypeface() const { if (font->typeface == 0) diff --git a/src/gui/graphics/fonts/juce_Font.h b/src/gui/graphics/fonts/juce_Font.h index 531d87e2e8..05b57b0164 100644 --- a/src/gui/graphics/fonts/juce_Font.h +++ b/src/gui/graphics/fonts/juce_Font.h @@ -367,11 +367,12 @@ private: class SharedFontInternal : public ReferenceCountedObject { public: - SharedFontInternal (const String& typefaceName, float height, float horizontalScale, - float kerning, float ascent, int styleFlags, - Typeface* typeface) throw(); + SharedFontInternal (const String& typefaceName, float height, int styleFlags) throw(); + SharedFontInternal (const Typeface::Ptr& typeface) throw(); SharedFontInternal (const SharedFontInternal& other) throw(); + bool operator== (const SharedFontInternal&) const throw(); + String typefaceName; float height, horizontalScale, kerning, ascent; int styleFlags; diff --git a/src/gui/graphics/fonts/juce_Typeface.h b/src/gui/graphics/fonts/juce_Typeface.h index 36dd7e5f12..c5dfe97407 100644 --- a/src/gui/graphics/fonts/juce_Typeface.h +++ b/src/gui/graphics/fonts/juce_Typeface.h @@ -69,6 +69,12 @@ public: /** Destructor. */ virtual ~Typeface(); + /** Returns true if this typeface can be used to render the specified font. + When called, the font will already have been checked to make sure that its name and + style flags match the typeface. + */ + virtual bool isSuitableForFont (const Font&) const { return true; } + /** Returns the ascent of the font, as a proportion of its height. The height is considered to always be normalised as 1.0, so this will be a value less that 1.0, indicating the proportion of the font that lies above @@ -105,6 +111,8 @@ public: */ virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0; + /** Returns true if the typeface uses hinting. */ + virtual bool isHinted() const { return false; } protected: //============================================================================== diff --git a/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp b/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp index 2074684a99..2971119685 100644 --- a/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp +++ b/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp @@ -29,6 +29,7 @@ BEGIN_JUCE_NAMESPACE #include "juce_RelativeCoordinate.h" #include "../drawables/juce_DrawablePath.h" +#include "../../components/layout/juce_MarkerList.h" #include "../../../io/streams/juce_MemoryOutputStream.h" @@ -45,8 +46,274 @@ namespace RelativeCoordinateHelpers } } +//============================================================================== +class RelativeComponentPositioner : public Component::Positioner, + public ComponentListener, + public MarkerList::Listener, + public Expression::EvaluationContext +{ +public: + RelativeComponentPositioner (Component& component_) + : Component::Positioner (component_), registeredOk (false) + { + } + + ~RelativeComponentPositioner() + { + unregisterListeners(); + } + + const Expression getSymbolValue (const String& objectName, const String& member) const + { + jassert (objectName.isNotEmpty()); + + if (member.isNotEmpty()) + { + const Component* comp = getSourceComponent (objectName); + + if (comp == 0) + { + if (objectName == RelativeCoordinate::Strings::parent) + comp = getComponent().getParentComponent(); + else if (objectName == RelativeCoordinate::Strings::this_ || objectName == getComponent().getComponentID()) + comp = &getComponent(); + } + + if (comp != 0) + { + if (member == RelativeCoordinate::Strings::left) return xToExpression (comp, 0); + if (member == RelativeCoordinate::Strings::right) return xToExpression (comp, comp->getWidth()); + if (member == RelativeCoordinate::Strings::top) return yToExpression (comp, 0); + if (member == RelativeCoordinate::Strings::bottom) return yToExpression (comp, comp->getHeight()); + } + } + + for (int i = sourceMarkerLists.size(); --i >= 0;) + { + MarkerList* const markerList = sourceMarkerLists.getUnchecked(i); + const MarkerList::Marker* const marker = markerList->getMarker (objectName); + + if (marker != 0) + return marker->position.getExpression(); + } + + return Expression::EvaluationContext::getSymbolValue (objectName, member); + } + + void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) + { + apply(); + } + + void componentParentHierarchyChanged (Component& component) + { + apply(); + } + + void componentBeingDeleted (Component& component) + { + jassert (sourceComponents.contains (&component)); + sourceComponents.removeValue (&component); + } + + void markersChanged (MarkerList* markerList) + { + apply(); + } + + void markerListBeingDeleted (MarkerList* markerList) + { + jassert (sourceMarkerLists.contains (markerList)); + sourceMarkerLists.removeValue (markerList); + } + + void apply() + { + if (! registeredOk) + { + unregisterListeners(); + registeredOk = registerCoordinates(); + } + + applyToComponentBounds(); + } + +protected: + bool addCoordinate (const RelativeCoordinate& coord) + { + return registerListeners (coord.getExpression()); + } + + virtual bool registerCoordinates() = 0; + virtual void applyToComponentBounds() = 0; + +private: + Array sourceComponents; + Array sourceMarkerLists; + bool registeredOk; + + bool registerListeners (const Expression& e) + { + bool ok = true; + + if (e.getType() == Expression::symbolType) + { + String objectName, memberName; + e.getSymbolParts (objectName, memberName); + + if (memberName.isNotEmpty()) + ok = registerComponentEdge (objectName, memberName) && ok; + else + ok = registerMarker (objectName) && ok; + } + else + { + for (int i = e.getNumInputs(); --i >= 0;) + ok = registerListeners (e.getInput (i)) && ok; + } + + return ok; + } + + bool registerComponentEdge (const String& objectName, const String memberName) + { + Component* comp = findComponent (objectName); + + if (comp == 0) + { + if (objectName == RelativeCoordinate::Strings::parent) + comp = getComponent().getParentComponent(); + else if (objectName == RelativeCoordinate::Strings::this_ || objectName == getComponent().getComponentID()) + comp = &getComponent(); + } + + if (comp != 0) + { + if (comp != &getComponent()) + registerComponentListener (comp); + + return true; + } + else + { + // The component we want doesn't exist, so watch the parent in case the hierarchy changes and it appears later.. + Component* const parent = getComponent().getParentComponent(); + + if (parent != 0) + registerComponentListener (parent); + else + registerComponentListener (&getComponent()); + + return false; + } + } + + bool registerMarker (const String markerName) + { + Component* const parent = getComponent().getParentComponent(); + + if (parent != 0) + { + MarkerList* list = parent->getMarkers (true); + + if (list == 0 || list->getMarker (markerName) == 0) + list = parent->getMarkers (false); + + if (list != 0 && list->getMarker (markerName) != 0) + { + registerMarkerListListener (list); + return true; + } + else + { + // The marker we want doesn't exist, so watch all lists in case they change and the marker appears later.. + registerMarkerListListener (parent->getMarkers (true)); + registerMarkerListListener (parent->getMarkers (false)); + } + } + + return false; + } + + void registerComponentListener (Component* const comp) + { + if (comp != 0 && ! sourceComponents.contains (comp)) + { + comp->addComponentListener (this); + sourceComponents.add (comp); + } + } + + void registerMarkerListListener (MarkerList* const list) + { + if (list != 0 && ! sourceMarkerLists.contains (list)) + { + list->addListener (this); + sourceMarkerLists.add (list); + } + } + + void unregisterListeners() + { + int i; + for (i = sourceComponents.size(); --i >= 0;) + sourceComponents.getUnchecked(i)->removeComponentListener (this); + + for (i = sourceMarkerLists.size(); --i >= 0;) + sourceMarkerLists.getUnchecked(i)->removeListener (this); + + sourceComponents.clear(); + sourceMarkerLists.clear(); + } + + Component* findComponent (const String& componentID) const + { + Component* const parent = getComponent().getParentComponent(); + + if (parent != 0) + { + for (int i = parent->getNumChildComponents(); --i >= 0;) + { + Component* const c = parent->getChildComponent(i); + + if (c->getComponentID() == componentID) + return c; + } + } + + return 0; + } + + Component* getSourceComponent (const String& objectName) const + { + for (int i = sourceComponents.size(); --i >= 0;) + { + Component* const comp = sourceComponents.getUnchecked(i); + + if (comp->getComponentID() == objectName) + return comp; + } + + return 0; + } + + const Expression xToExpression (const Component* const source, const int x) const + { + return Expression ((double) (getComponent().getLocalPoint (source, Point (x, 0)).getX() + getComponent().getX())); + } + + const Expression yToExpression (const Component* const source, const int y) const + { + return Expression ((double) (getComponent().getLocalPoint (source, Point (0, y)).getY() + getComponent().getY())); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeComponentPositioner); +}; + + //============================================================================== const String RelativeCoordinate::Strings::parent ("parent"); +const String RelativeCoordinate::Strings::this_ ("this"); const String RelativeCoordinate::Strings::left ("left"); const String RelativeCoordinate::Strings::right ("right"); const String RelativeCoordinate::Strings::top ("top"); @@ -301,7 +568,7 @@ const Rectangle RelativeRectangle::resolve (const Expression::EvaluationC const double t = top.resolve (context); const double b = bottom.resolve (context); - return Rectangle ((float) l, (float) t, (float) (r - l), (float) (b - t)); + return Rectangle ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t)); } void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* context) @@ -312,6 +579,11 @@ void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, const Ex bottom.moveToAbsolute (newPos.getBottom(), context); } +bool RelativeRectangle::isDynamic() const +{ + return left.isDynamic() || right.isDynamic() || top.isDynamic() || bottom.isDynamic(); +} + const String RelativeRectangle::toString() const { return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString(); @@ -326,6 +598,73 @@ void RelativeRectangle::renameSymbolIfUsed (const String& oldName, const String& } +//============================================================================== +class RelativeRectangleComponentPositioner : public RelativeComponentPositioner +{ +public: + RelativeRectangleComponentPositioner (Component& component_, const RelativeRectangle& rectangle_) + : RelativeComponentPositioner (component_), + rectangle (rectangle_) + { + } + + bool registerCoordinates() + { + bool ok = addCoordinate (rectangle.left); + ok = addCoordinate (rectangle.right) && ok; + ok = addCoordinate (rectangle.top) && ok; + ok = addCoordinate (rectangle.bottom) && ok; + return ok; + } + + bool isUsingRectangle (const RelativeRectangle& other) const throw() + { + return rectangle == other; + } + + void applyToComponentBounds() + { + for (int i = 4; --i >= 0;) + { + const Rectangle newBounds (rectangle.resolve (this).getSmallestIntegerContainer()); + + if (newBounds == getComponent().getBounds()) + return; + + getComponent().setBounds (newBounds); + } + + jassertfalse; // must be a recursive reference! + } + +private: + const RelativeRectangle rectangle; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner); +}; + +void RelativeRectangle::applyToComponent (Component& component) const +{ + if (isDynamic()) + { + RelativeRectangleComponentPositioner* current = dynamic_cast (component.getPositioner()); + + if (current == 0 || ! current->isUsingRectangle (*this)) + { + RelativeRectangleComponentPositioner* p = new RelativeRectangleComponentPositioner (component, *this); + + component.setPositioner (p); + p->apply(); + } + } + else + { + component.setPositioner (0); + component.setBounds (resolve (0).getSmallestIntegerContainer()); + } +} + + //============================================================================== RelativePointPath::RelativePointPath() : usesNonZeroWinding (true), diff --git a/src/gui/graphics/geometry/juce_RelativeCoordinate.h b/src/gui/graphics/geometry/juce_RelativeCoordinate.h index 20bbc77a97..dc26ac3f7d 100644 --- a/src/gui/graphics/geometry/juce_RelativeCoordinate.h +++ b/src/gui/graphics/geometry/juce_RelativeCoordinate.h @@ -31,6 +31,7 @@ #include "../../../maths/juce_Expression.h" #include "../../../containers/juce_OwnedArray.h" #include "../../../containers/juce_ValueTree.h" +class Component; //============================================================================== @@ -120,6 +121,7 @@ public: struct Strings { static const String parent; /**< "parent" */ + static const String this_; /**< "this" */ static const String left; /**< "left" */ static const String right; /**< "right" */ static const String top; /**< "top" */ @@ -251,6 +253,9 @@ public: */ void moveToAbsolute (const Rectangle& newPos, const Expression::EvaluationContext* evaluationContext); + /** Returns true if this rectangle depends on any other coordinates for its position. */ + bool isDynamic() const; + /** Returns a string which represents this point. This returns a comma-separated list of coordinates, in the order left, top, right, bottom. For details of the string syntax used by the coordinates, see the RelativeCoordinate constructor notes. @@ -263,6 +268,9 @@ public: */ void renameSymbolIfUsed (const String& oldName, const String& newName); + /** */ + void applyToComponent (Component& component) const; + // The actual rectangle coords... RelativeCoordinate left, right, top, bottom; }; diff --git a/src/maths/juce_Expression.cpp b/src/maths/juce_Expression.cpp index ecaa29684a..d48e377be4 100644 --- a/src/maths/juce_Expression.cpp +++ b/src/maths/juce_Expression.cpp @@ -101,13 +101,14 @@ public: return 0; } - Type getType() const throw() { return symbolType; } - Term* clone() const { return new Symbol (mainSymbol, member); } - int getNumInputs() const { return 0; } - Term* getInput (int) const { return 0; } - const String getSymbolName() const { return toString(); } - - const String toString() const + Type getType() const throw() { return symbolType; } + Term* clone() const { return new Symbol (mainSymbol, member); } + int getNumInputs() const { return 0; } + Term* getInput (int) const { return 0; } + const String toString() const { return joinParts (mainSymbol, member); } + void getSymbolParts (String& objectName, String& memberName) const { objectName = mainSymbol; memberName = member; } + + static const String joinParts (const String& mainSymbol, const String& member) { return member.isEmpty() ? mainSymbol : mainSymbol + "." + member; @@ -894,6 +895,9 @@ const Expression Expression::withRenamedSymbol (const String& oldSymbol, const S { jassert (newSymbol.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); + if (oldSymbol == newSymbol) + return *this; + Expression newExpression (term->clone()); Helpers::renameSymbol (newExpression.term, oldSymbol, newSymbol); return newExpression; @@ -916,7 +920,14 @@ Expression::Type Expression::getType() const throw() const String Expression::getSymbol() const { - return term->getSymbolName(); + String objectName, memberName; + term->getSymbolParts (objectName, memberName); + return Expression::Helpers::Symbol::joinParts (objectName, memberName); +} + +void Expression::getSymbolParts (String& objectName, String& memberName) const +{ + term->getSymbolParts (objectName, memberName); } const String Expression::getFunction() const @@ -966,10 +977,9 @@ const ReferenceCountedObjectPtr Expression::Term::negated() return new Helpers::Negate (this); } -const String Expression::Term::getSymbolName() const +void Expression::Term::getSymbolParts (String&, String&) const { jassertfalse; // You should only call getSymbol() on an expression that's actually a symbol! - return String::empty; } const String Expression::Term::getFunctionName() const diff --git a/src/maths/juce_Expression.h b/src/maths/juce_Expression.h index 3eb5afff65..9a4d205f9c 100644 --- a/src/maths/juce_Expression.h +++ b/src/maths/juce_Expression.h @@ -202,9 +202,12 @@ public: /** Returns the type of this expression. */ Type getType() const throw(); - /** If this expression is a symbol, this returns its name. */ + /** If this expression is a symbol, this returns its full name. */ const String getSymbol() const; + /** For a symbol that contains a dot, this returns the two */ + void getSymbolParts (String& objectName, String& memberName) const; + /** If this expression is a function, this returns its name. */ const String getFunction() const; @@ -246,7 +249,7 @@ private: double overallTarget, Term* topLevelTerm) const; virtual const ReferenceCountedObjectPtr negated(); virtual Type getType() const throw() = 0; - virtual const String getSymbolName() const; + virtual void getSymbolParts (String& objectName, String& memberName) const; virtual const String getFunctionName() const; private: