| @@ -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_) | |||
| { | |||
| } | |||
| @@ -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 <ComponentPosition> components; | |||
| OwnedArray <MarkerPosition> markers; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleLayoutManager); | |||
| }; | |||
| @@ -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> 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<int> Component::getLocalBounds() const throw() | |||
| { | |||
| return Rectangle<int> (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 <Component*> sourceComponents; | |||
| Array <MarkerList*> 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<int> (x, 0)).getX() + getComponent().getX())); | |||
| } | |||
| const Expression yToExpression (const Component* const source, const int y) const | |||
| { | |||
| return Expression ((double) (getComponent().getLocalPoint (source, Point<int> (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<float> RelativeRectangle::resolve (const Expression::EvaluationC | |||
| const double t = top.resolve (context); | |||
| const double b = bottom.resolve (context); | |||
| return Rectangle<float> ((float) l, (float) t, (float) (r - l), (float) (b - t)); | |||
| return Rectangle<float> ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t)); | |||
| } | |||
| void RelativeRectangle::moveToAbsolute (const Rectangle<float>& newPos, const Expression::EvaluationContext* context) | |||
| @@ -79909,6 +80212,11 @@ void RelativeRectangle::moveToAbsolute (const Rectangle<float>& 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<int> 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 <RelativeRectangleComponentPositioner*> (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> 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 <CachedGlyph> 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<int>::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. "<Sans-Serif>" 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 <CachedFace> 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<int>::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 <CachedFace> faces; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache); | |||
| }; | |||
| juce_ImplementSingleton_SingleThreaded (TypefaceCache) | |||
| Typeface* Font::getTypeface() const | |||
| { | |||
| if (font->typeface == 0) | |||
| @@ -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<Term> 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<int> relativePositionToGlobal (const Point<int>& 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<int> globalPositionToRelative (const Point<int>& 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<int> relativePositionToOtherComponent (const Component* targetComponent, | |||
| const Point<int>& 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<int> relativePositionToGlobal (const Point<int>&) const); | |||
| JUCE_DEPRECATED (const Point<int> globalPositionToRelative (const Point<int>&) const); | |||
| JUCE_DEPRECATED (const Point<int> relativePositionToOtherComponent (const Component*, const Point<int>&) const); | |||
| #endif | |||
| private: | |||
| @@ -28917,6 +28963,7 @@ private: | |||
| String componentName, componentID; | |||
| Component* parentComponent; | |||
| Rectangle<int> bounds; | |||
| ScopedPointer <Positioner> positioner; | |||
| ScopedPointer <AffineTransform> affineTransform; | |||
| Array <Component*> 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<float>& 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<Marker> markers; | |||
| void markersHaveChanged(); | |||
| ListenerList <Listener> listeners; | |||
| JUCE_LEAK_DETECTOR (MarkerList); | |||
| }; | |||
| @@ -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: | |||
| //============================================================================== | |||
| @@ -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<NamedValue>* 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<NamedValue>* 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<NamedValue>::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<NamedValue>* 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<NamedValue>* 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<NamedValue>::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 | |||
| @@ -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. | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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. | |||
| @@ -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); | |||
| @@ -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); | |||
| } | |||
| @@ -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<int> Component::getLocalBounds() const throw() | |||
| { | |||
| @@ -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<int> relativePositionToGlobal (const Point<int>& 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<int> globalPositionToRelative (const Point<int>& screenPosition) const; | |||
| private: | |||
| Component& component; | |||
| /** This method is deprecated - use getLocalPoint instead. */ | |||
| const Point<int> relativePositionToOtherComponent (const Component* targetComponent, | |||
| const Point<int>& 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<int> relativePositionToGlobal (const Point<int>&) const); | |||
| JUCE_DEPRECATED (const Point<int> globalPositionToRelative (const Point<int>&) const); | |||
| JUCE_DEPRECATED (const Point<int> relativePositionToOtherComponent (const Component*, const Point<int>&) const); | |||
| #endif | |||
| private: | |||
| @@ -2134,6 +2168,7 @@ private: | |||
| String componentName, componentID; | |||
| Component* parentComponent; | |||
| Rectangle<int> bounds; | |||
| ScopedPointer <Positioner> positioner; | |||
| ScopedPointer <AffineTransform> affineTransform; | |||
| Array <Component*> childComponentList; | |||
| LookAndFeel* lookAndFeel; | |||
| @@ -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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| //============================================================================== | |||
| @@ -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<Marker> markers; | |||
| void markersHaveChanged(); | |||
| ListenerList <Listener> listeners; | |||
| JUCE_LEAK_DETECTOR (MarkerList); | |||
| }; | |||
| @@ -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> 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 <CachedGlyph> glyphs; | |||
| int accessCounter, hits, misses; | |||
| void addNewGlyphSlots (int num) | |||
| { | |||
| while (--num >= 0) | |||
| glyphs.add (new CachedGlyph()); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache); | |||
| }; | |||
| @@ -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<int>::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. "<Sans-Serif>" 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 <CachedFace> 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<int>::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 <CachedFace> faces; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache); | |||
| }; | |||
| juce_ImplementSingleton_SingleThreaded (TypefaceCache) | |||
| Typeface* Font::getTypeface() const | |||
| { | |||
| if (font->typeface == 0) | |||
| @@ -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; | |||
| @@ -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: | |||
| //============================================================================== | |||
| @@ -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 <Component*> sourceComponents; | |||
| Array <MarkerList*> 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<int> (x, 0)).getX() + getComponent().getX())); | |||
| } | |||
| const Expression yToExpression (const Component* const source, const int y) const | |||
| { | |||
| return Expression ((double) (getComponent().getLocalPoint (source, Point<int> (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<float> RelativeRectangle::resolve (const Expression::EvaluationC | |||
| const double t = top.resolve (context); | |||
| const double b = bottom.resolve (context); | |||
| return Rectangle<float> ((float) l, (float) t, (float) (r - l), (float) (b - t)); | |||
| return Rectangle<float> ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t)); | |||
| } | |||
| void RelativeRectangle::moveToAbsolute (const Rectangle<float>& newPos, const Expression::EvaluationContext* context) | |||
| @@ -312,6 +579,11 @@ void RelativeRectangle::moveToAbsolute (const Rectangle<float>& 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<int> 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 <RelativeRectangleComponentPositioner*> (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), | |||
| @@ -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<float>& 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; | |||
| }; | |||
| @@ -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> 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 | |||
| @@ -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<Term> 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: | |||