| @@ -36,24 +36,24 @@ public: | |||
| Component* createComponent() { return new TextButton (String::empty); } | |||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 150, 24); } | |||
| void updateComponent (Component* comp, const ValueTree& state) | |||
| void updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) | |||
| { | |||
| TextButton* tb = dynamic_cast <TextButton*> (comp); | |||
| jassert (tb != 0); | |||
| ComponentTypeHandler::updateComponent (comp, state); | |||
| ComponentTypeHandler::updateComponent (document, comp, state); | |||
| tb->setButtonText (state ["text"].toString()); | |||
| } | |||
| void initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||
| void initialiseNewItem (ComponentDocument& document, ValueTree& state) | |||
| { | |||
| ComponentTypeHandler::initialiseNewItem (state, document); | |||
| ComponentTypeHandler::initialiseNewItem (document, state); | |||
| state.setProperty ("text", "New Toggle Button", 0); | |||
| } | |||
| void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props) | |||
| void createPropertyEditors (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||
| { | |||
| ComponentTypeHandler::createPropertyEditors (state, document, props); | |||
| ComponentTypeHandler::createPropertyEditors (document, state, props); | |||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | |||
| props.getLast()->setTooltip ("The button's text."); | |||
| } | |||
| @@ -36,24 +36,24 @@ public: | |||
| Component* createComponent() { return new ToggleButton (String::empty); } | |||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 180, 24); } | |||
| void updateComponent (Component* comp, const ValueTree& state) | |||
| void updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) | |||
| { | |||
| ToggleButton* tb = dynamic_cast <ToggleButton*> (comp); | |||
| jassert (tb != 0); | |||
| ComponentTypeHandler::updateComponent (comp, state); | |||
| ComponentTypeHandler::updateComponent (document, comp, state); | |||
| tb->setButtonText (state ["text"].toString()); | |||
| } | |||
| void initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||
| void initialiseNewItem (ComponentDocument& document, ValueTree& state) | |||
| { | |||
| ComponentTypeHandler::initialiseNewItem (state, document); | |||
| ComponentTypeHandler::initialiseNewItem (document, state); | |||
| state.setProperty ("text", "New Toggle Button", 0); | |||
| } | |||
| void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props) | |||
| void createPropertyEditors (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||
| { | |||
| ComponentTypeHandler::createPropertyEditors (state, document, props); | |||
| ComponentTypeHandler::createPropertyEditors (document, state, props); | |||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | |||
| props.getLast()->setTooltip ("The button's text."); | |||
| } | |||
| @@ -57,28 +57,30 @@ Value ComponentTypeHandler::getValue (const var::identifier& name, ValueTree& st | |||
| return state.getPropertyAsValue (name, document.getUndoManager()); | |||
| } | |||
| void ComponentTypeHandler::updateComponent (Component* comp, const ValueTree& state) | |||
| void ComponentTypeHandler::updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) | |||
| { | |||
| if (comp->getParentComponent() != 0) | |||
| { | |||
| PositionedRectangle pos (state [compBoundsProperty].toString()); | |||
| comp->setBounds (pos.getRectangle (comp->getParentComponent()->getLocalBounds())); | |||
| RectangleCoordinates pos (state [compBoundsProperty].toString()); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (state, comp->getParentComponent())); | |||
| comp->setBounds (pos.resolve (*markers)); | |||
| } | |||
| comp->setName (state [compNameProperty]); | |||
| } | |||
| void ComponentTypeHandler::initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||
| void ComponentTypeHandler::initialiseNewItem (ComponentDocument& document, ValueTree& state) | |||
| { | |||
| state.setProperty (compNameProperty, String::empty, 0); | |||
| state.setProperty (memberNameProperty, document.getNonExistentMemberName (getMemberNameRoot()), 0); | |||
| state.setProperty (compBoundsProperty, | |||
| getDefaultSize().withPosition (Point<int> (Random::getSystemRandom().nextInt (100) + 100, | |||
| Random::getSystemRandom().nextInt (100) + 100)).toString(), 0); | |||
| const Rectangle<int> bounds (getDefaultSize().withPosition (Point<int> (Random::getSystemRandom().nextInt (100) + 100, | |||
| Random::getSystemRandom().nextInt (100) + 100))); | |||
| state.setProperty (compBoundsProperty, RectangleCoordinates (bounds).toString(), 0); | |||
| } | |||
| void ComponentTypeHandler::createPropertyEditors (ValueTree& state, ComponentDocument& document, | |||
| Array <PropertyComponent*>& props) | |||
| void ComponentTypeHandler::createPropertyEditors (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||
| { | |||
| props.add (new TextPropertyComponent (getValue (compBoundsProperty, state, document), "Bounds", 512, false)); | |||
| props.getLast()->setTooltip ("The component's position."); | |||
| @@ -100,7 +102,7 @@ public: | |||
| juce_DeclareSingleton_SingleThreaded_Minimal (ComponentTypeManager); | |||
| Component* createFromStoredType (const ValueTree& value) | |||
| Component* createFromStoredType (ComponentDocument& document, const ValueTree& value) | |||
| { | |||
| ComponentTypeHandler* handler = getHandlerFor (value.getType()); | |||
| if (handler == 0) | |||
| @@ -108,7 +110,7 @@ public: | |||
| Component* c = handler->createComponent(); | |||
| if (c != 0) | |||
| handler->updateComponent (c, value); | |||
| handler->updateComponent (document, c, value); | |||
| return c; | |||
| } | |||
| @@ -331,7 +333,7 @@ void ComponentDocument::performNewComponentMenuItem (int menuResultCode) | |||
| { | |||
| ValueTree state (handler->getXmlTag()); | |||
| state.setProperty (idProperty, createAlphaNumericUID(), 0); | |||
| handler->initialiseNewItem (state, *this); | |||
| handler->initialiseNewItem (*this, state); | |||
| getComponentGroup().addChild (state, -1, getUndoManager()); | |||
| } | |||
| @@ -368,13 +370,13 @@ const ValueTree ComponentDocument::getComponentWithMemberName (const String& nam | |||
| return ValueTree::invalid; | |||
| } | |||
| Component* ComponentDocument::createComponent (int index) const | |||
| Component* ComponentDocument::createComponent (int index) | |||
| { | |||
| const ValueTree v (getComponentGroup().getChild (index)); | |||
| if (v.isValid()) | |||
| { | |||
| Component* c = ComponentTypeManager::getInstance()->createFromStoredType (v); | |||
| Component* c = ComponentTypeManager::getInstance()->createFromStoredType (*this, v); | |||
| c->getProperties().set (idProperty, v[idProperty]); | |||
| jassert (c->getProperties()[idProperty].toString().isNotEmpty()); | |||
| return c; | |||
| @@ -383,7 +385,46 @@ Component* ComponentDocument::createComponent (int index) const | |||
| return 0; | |||
| } | |||
| void ComponentDocument::updateComponent (Component* comp) const | |||
| //============================================================================== | |||
| class ComponentMarkerResolver : public Coordinate::MarkerResolver | |||
| { | |||
| public: | |||
| ComponentMarkerResolver (ComponentDocument& doc, const ValueTree& state_, Component* parentComponent_) | |||
| : owner (doc), state (state_), parentComponent (parentComponent_) | |||
| {} | |||
| ~ComponentMarkerResolver() {} | |||
| const Coordinate findMarker (const String& name, bool isHorizontal) | |||
| { | |||
| if (name == "left") return RectangleCoordinates (state [compBoundsProperty]).left; | |||
| else if (name == "right") return RectangleCoordinates (state [compBoundsProperty]).right; | |||
| else if (name == "top") return RectangleCoordinates (state [compBoundsProperty]).top; | |||
| else if (name == "bottom") return RectangleCoordinates (state [compBoundsProperty]).bottom; | |||
| else if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) parentComponent->getWidth(), isHorizontal); | |||
| else if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) parentComponent->getHeight(), isHorizontal); | |||
| return Coordinate (isHorizontal); | |||
| } | |||
| private: | |||
| ComponentDocument& owner; | |||
| ValueTree state; | |||
| Component* parentComponent; | |||
| }; | |||
| const RectangleCoordinates ComponentDocument::getCoordsFor (const ValueTree& state) const | |||
| { | |||
| return RectangleCoordinates (state [compBoundsProperty]); | |||
| } | |||
| Coordinate::MarkerResolver* ComponentDocument::createMarkerResolver (const ValueTree& state, Component* parentComponent) | |||
| { | |||
| jassert (parentComponent != 0); | |||
| return new ComponentMarkerResolver (*this, state, parentComponent); | |||
| } | |||
| void ComponentDocument::updateComponent (Component* comp) | |||
| { | |||
| const ValueTree v (getComponentState (comp)); | |||
| @@ -393,7 +434,7 @@ void ComponentDocument::updateComponent (Component* comp) const | |||
| jassert (handler != 0); | |||
| if (handler != 0) | |||
| handler->updateComponent (comp, v); | |||
| handler->updateComponent (*this, comp, v); | |||
| } | |||
| } | |||
| @@ -431,7 +472,7 @@ void ComponentDocument::getComponentProperties (Array <PropertyComponent*>& prop | |||
| jassert (handler != 0); | |||
| if (handler != 0) | |||
| handler->createPropertyEditors (v, *this, props); | |||
| handler->createPropertyEditors (*this, v, props); | |||
| } | |||
| } | |||
| @@ -473,8 +514,10 @@ public: | |||
| DragHandler (ComponentDocument& document_, | |||
| const Array<Component*>& items, | |||
| const MouseEvent& e, | |||
| const ResizableBorderComponent::Zone& zone_) | |||
| : document (document_), | |||
| const ResizableBorderComponent::Zone& zone_, | |||
| Component* parentForOverlays) | |||
| : parentComponent (0), | |||
| document (document_), | |||
| zone (zone_) | |||
| { | |||
| for (int i = 0; i < items.size(); ++i) | |||
| @@ -482,14 +525,20 @@ public: | |||
| Component* comp = items.getUnchecked(i); | |||
| jassert (comp != 0); | |||
| parentComponentSize.setSize (comp->getParentWidth(), comp->getParentHeight()); | |||
| jassert (! parentComponentSize.isEmpty()); | |||
| if (parentComponent == 0) | |||
| parentComponent = comp->getParentComponent(); | |||
| const ValueTree v (document.getComponentState (comp)); | |||
| draggedComponents.add (v); | |||
| PositionedRectangle pos (v [compBoundsProperty].toString()); | |||
| originalPositions.add (pos.getRectangle (parentComponentSize)); | |||
| Rectangle<int> pos; | |||
| { | |||
| RectangleCoordinates relativePos (v [compBoundsProperty].toString()); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (v, parentComponent)); | |||
| pos = relativePos.resolve (*markers); | |||
| originalPositions.add (pos); | |||
| } | |||
| const Rectangle<float> floatPos ((float) pos.getX(), (float) pos.getY(), | |||
| (float) pos.getWidth(), (float) pos.getHeight()); | |||
| @@ -525,18 +574,42 @@ public: | |||
| { | |||
| document.getUndoManager()->undoCurrentTransactionOnly(); | |||
| for (int i = 0; i < draggedComponents.size(); ++i) | |||
| move (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i)); | |||
| for (int n = 50;;) | |||
| { | |||
| // Need to repeatedly apply the new positions until they all settle down, in case some of | |||
| // the coords are relative to each other.. | |||
| bool anyUpdated = false; | |||
| for (int i = 0; i < draggedComponents.size(); ++i) | |||
| if (dragItem (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i))) | |||
| anyUpdated = true; | |||
| if (! anyUpdated) | |||
| break; | |||
| if (--n == 0) | |||
| { | |||
| jassertfalse; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| void move (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos) | |||
| bool dragItem (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos) | |||
| { | |||
| Rectangle<int> newBounds (zone.resizeRectangleBy (originalPos, distance)); | |||
| const Rectangle<int> newBounds (zone.resizeRectangleBy (originalPos, distance)); | |||
| RectangleCoordinates pr (v [compBoundsProperty].toString()); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (v, parentComponent)); | |||
| PositionedRectangle pr (v [compBoundsProperty].toString()); | |||
| pr.updateFrom (newBounds, parentComponentSize); | |||
| pr.moveToAbsolute (newBounds, *markers); | |||
| const String newBoundsString (pr.toString()); | |||
| if (v[compBoundsProperty] == newBoundsString) | |||
| return false; | |||
| v.setProperty (compBoundsProperty, pr.toString(), document.getUndoManager()); | |||
| v.setProperty (compBoundsProperty, newBoundsString, document.getUndoManager()); | |||
| return true; | |||
| } | |||
| const Array<float> getVerticalSnapPositions (const Point<int>& distance) const | |||
| @@ -558,7 +631,7 @@ public: | |||
| } | |||
| private: | |||
| Rectangle<int> parentComponentSize; | |||
| Component* parentComponent; | |||
| ComponentDocument& document; | |||
| Array <ValueTree> draggedComponents; | |||
| Array <Rectangle<int> > originalPositions; | |||
| @@ -566,9 +639,10 @@ private: | |||
| const ResizableBorderComponent::Zone zone; | |||
| }; | |||
| void ComponentDocument::beginDrag (const Array<Component*>& items, const MouseEvent& e, const ResizableBorderComponent::Zone& zone) | |||
| void ComponentDocument::beginDrag (const Array<Component*>& items, const MouseEvent& e, | |||
| Component* parentForOverlays, const ResizableBorderComponent::Zone& zone) | |||
| { | |||
| dragger = new DragHandler (*this, items, e, zone); | |||
| dragger = new DragHandler (*this, items, e, zone, parentForOverlays); | |||
| } | |||
| void ComponentDocument::continueDrag (const MouseEvent& e) | |||
| @@ -55,19 +55,21 @@ public: | |||
| int getNumComponents() const; | |||
| const ValueTree getComponent (int index) const; | |||
| const ValueTree getComponentWithMemberName (const String& name) const; | |||
| Component* createComponent (int index) const; | |||
| void updateComponent (Component* comp) const; | |||
| Component* createComponent (int index); | |||
| void updateComponent (Component* comp); | |||
| bool containsComponent (Component* comp) const; | |||
| const ValueTree getComponentState (Component* comp) const; | |||
| void getComponentProperties (Array <PropertyComponent*>& props, Component* comp); | |||
| bool isStateForComponent (const ValueTree& storedState, Component* comp) const; | |||
| Coordinate::MarkerResolver* createMarkerResolver (const ValueTree& state, Component* parentComponent); | |||
| const RectangleCoordinates getCoordsFor (const ValueTree& state) const; | |||
| void addNewComponentMenuItems (PopupMenu& menu) const; | |||
| void performNewComponentMenuItem (int menuResultCode); | |||
| //============================================================================== | |||
| void beginDrag (const Array<Component*>& items, const MouseEvent& e, | |||
| const ResizableBorderComponent::Zone& zone); | |||
| Component* parentForOverlays, const ResizableBorderComponent::Zone& zone); | |||
| void continueDrag (const MouseEvent& e); | |||
| void endDrag (const MouseEvent& e); | |||
| @@ -114,9 +116,9 @@ public: | |||
| virtual Component* createComponent() = 0; | |||
| virtual const Rectangle<int> getDefaultSize() = 0; | |||
| virtual void updateComponent (Component* comp, const ValueTree& state); | |||
| virtual void initialiseNewItem (ValueTree& state, ComponentDocument& document); | |||
| virtual void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props); | |||
| virtual void updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state); | |||
| virtual void initialiseNewItem (ComponentDocument& document, ValueTree& state); | |||
| virtual void createPropertyEditors (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props); | |||
| Value getValue (const var::identifier& name, ValueTree& state, ComponentDocument& document) const; | |||
| @@ -27,6 +27,134 @@ | |||
| #include "jucer_ComponentEditor.h" | |||
| //============================================================================== | |||
| class SizeGuideComponent : public Component, | |||
| public ComponentListener | |||
| { | |||
| public: | |||
| enum Type | |||
| { | |||
| left, right, top, bottom | |||
| }; | |||
| SizeGuideComponent (ComponentDocument& document_, const ValueTree& state_, Component* component_, | |||
| Component* parentForOverlays, Type type_) | |||
| : document (document_), state (state_), component (component_), type (type_), | |||
| font (10.0f) | |||
| { | |||
| component->addComponentListener (this); | |||
| setAlwaysOnTop (true); | |||
| parentForOverlays->addAndMakeVisible (this); | |||
| updatePosition(); | |||
| } | |||
| ~SizeGuideComponent() | |||
| { | |||
| if (component != 0) | |||
| component->removeComponentListener (this); | |||
| } | |||
| void updatePosition() | |||
| { | |||
| RectangleCoordinates coords (document.getCoordsFor (state)); | |||
| Coordinate coord (false); | |||
| bool isHorizontal = false; | |||
| switch (type) | |||
| { | |||
| case left: coord = coords.left; isHorizontal = true; break; | |||
| case right: coord = coords.right; isHorizontal = true; break; | |||
| case top: coord = coords.top; break; | |||
| case bottom: coord = coords.bottom; break; | |||
| default: jassertfalse; break; | |||
| } | |||
| setName (coord.toString()); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (state, component->getParentComponent())); | |||
| int anchor1 = coord.getAnchorPoint1().resolve (*markers); | |||
| int anchor2 = coord.getAnchorPoint2().resolve (*markers); | |||
| Point<int> p1, p2; | |||
| switch (type) | |||
| { | |||
| case left: p1 = Point<int> (component->getX(), component->getY() + component->proportionOfHeight (0.33f)); | |||
| p2 = Point<int> (anchor1, p1.getY()); break; | |||
| case right: p1 = Point<int> (component->getRight(), component->getY() + component->proportionOfHeight (0.66f)); | |||
| p2 = Point<int> (anchor1, p1.getY()); break; | |||
| case top: p1 = Point<int> (component->getX() + component->proportionOfWidth (0.33f), component->getY()); | |||
| p2 = Point<int> (p1.getX(), anchor1); break; | |||
| case bottom: p1 = Point<int> (component->getX() + component->proportionOfWidth (0.66f), component->getBottom()); | |||
| p2 = Point<int> (p1.getX(), anchor1); break; | |||
| default: jassertfalse; break; | |||
| } | |||
| Rectangle<int> bounds (Rectangle<int> (p1, p2).expanded (4, 4)); | |||
| Point<int> textPos ((p1.getX() + p2.getX()) / 2, | |||
| (p1.getY() + p2.getY()) / 2); | |||
| int textW = (int) font.getStringWidth (getName()); | |||
| int textH = (int) font.getHeight(); | |||
| Rectangle<int> textRect (textPos.getX() - textW / 2, textPos.getY() - textH / 2, textW, textH); | |||
| if (isHorizontal) | |||
| textRect = textRect - Point<int> (0, textH / 2 + 4); | |||
| bounds = bounds.getUnion (textRect); | |||
| setBounds (bounds); | |||
| lineEnd1 = p1 - bounds.getPosition(); | |||
| lineEnd2 = p2 - bounds.getPosition(); | |||
| textArea = textRect - bounds.getPosition(); | |||
| repaint(); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| Path p; | |||
| p.addLineSegment (lineEnd1.getX(), lineEnd1.getY(), lineEnd2.getX(), lineEnd2.getY(), 1.6f); | |||
| const float startBlobSize = 2.0f; | |||
| p.addEllipse (lineEnd1.getX() - startBlobSize, lineEnd1.getY() - startBlobSize, startBlobSize * 2.0f, startBlobSize * 2.0f); | |||
| const float endBlobSize = 4.0f; | |||
| p.addEllipse (lineEnd2.getX() - endBlobSize, lineEnd2.getY() - endBlobSize, endBlobSize * 2.0f, endBlobSize * 2.0f); | |||
| g.setColour (Colours::black.withAlpha (0.3f)); | |||
| g.fillPath (p); | |||
| g.setFont (font); | |||
| g.setColour (Colours::white); | |||
| for (int y = -1; y <= 1; ++y) | |||
| for (int x = -1; x <= 1; ++x) | |||
| g.drawText (getName(), textArea.getX() + x, textArea.getY() + y, textArea.getWidth(), textArea.getHeight(), Justification::centred, 1); | |||
| g.setColour (Colours::black); | |||
| g.drawText (getName(), textArea.getX(), textArea.getY(), textArea.getWidth(), textArea.getHeight(), Justification::centred, 1); | |||
| } | |||
| void componentMovedOrResized (Component&, bool, bool) | |||
| { | |||
| updatePosition(); | |||
| } | |||
| void componentBeingDeleted (Component&) | |||
| { | |||
| setVisible (false); | |||
| component = 0; | |||
| } | |||
| private: | |||
| ComponentDocument& document; | |||
| ValueTree state; | |||
| Component* component; | |||
| Type type; | |||
| Font font; | |||
| Point<int> lineEnd1, lineEnd2; | |||
| Rectangle<int> textArea; | |||
| }; | |||
| //============================================================================== | |||
| class ComponentEditor::Canvas : public Component, | |||
| public ValueTree::Listener, | |||
| @@ -116,10 +244,8 @@ public: | |||
| c = doc.createComponent (i); | |||
| componentHolder->addAndMakeVisible (c); | |||
| } | |||
| else | |||
| { | |||
| doc.updateComponent (c); | |||
| } | |||
| doc.updateComponent (c); | |||
| } | |||
| startTimer (500); | |||
| @@ -175,9 +301,57 @@ public: | |||
| getDocument().beginNewTransaction(); | |||
| } | |||
| void mouseMove (const MouseEvent& e) | |||
| { | |||
| updateDragZone (e.getPosition()); | |||
| } | |||
| void mouseDown (const MouseEvent& e) | |||
| { | |||
| updateDragZone (e.getPosition()); | |||
| dragStartSize = getBounds(); | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if (dragZone.isDraggingRightEdge() || dragZone.isDraggingBottomEdge()) | |||
| { | |||
| showSizeGuides(); | |||
| setSize (jmax (0, dragStartSize.getWidth() + e.getDistanceFromDragStartX()), | |||
| jmax (0, dragStartSize.getHeight() + e.getDistanceFromDragStartY())); | |||
| } | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| hideSizeGuides(); | |||
| updateDragZone (e.getPosition()); | |||
| } | |||
| void updateDragZone (const Point<int>& p) | |||
| { | |||
| ResizableBorderComponent::Zone newZone | |||
| = ResizableBorderComponent::Zone::fromPositionOnBorder (getLocalBounds(), | |||
| BorderSize (borderThickness), p); | |||
| newZone = ResizableBorderComponent::Zone (newZone.getZoneFlags() | |||
| & (ResizableBorderComponent::Zone::right | ResizableBorderComponent::Zone::bottom)); | |||
| if (dragZone != newZone) | |||
| { | |||
| dragZone = newZone; | |||
| setMouseCursor (newZone.getMouseCursor()); | |||
| } | |||
| } | |||
| void showSizeGuides() { overlay->showSizeGuides(); } | |||
| void hideSizeGuides() { overlay->hideSizeGuides(); } | |||
| private: | |||
| ComponentEditor& editor; | |||
| const int borderThickness; | |||
| ResizableBorderComponent::Zone dragZone; | |||
| Rectangle<int> dragStartSize; | |||
| //============================================================================== | |||
| class ComponentResizeFrame : public Component, | |||
| @@ -230,18 +404,23 @@ private: | |||
| if (component != 0) | |||
| { | |||
| updateDragZone (e.getPosition()); | |||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, dragZone); | |||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(), dragZone); | |||
| } | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if (component != 0) | |||
| { | |||
| canvas.showSizeGuides(); | |||
| canvas.getDocument().continueDrag (e); | |||
| } | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| canvas.hideSizeGuides(); | |||
| if (component != 0) | |||
| canvas.getDocument().endDrag (e); | |||
| @@ -261,11 +440,29 @@ private: | |||
| uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); } | |||
| void showSizeGuides() | |||
| { | |||
| if (sizeGuides.size() == 0) | |||
| { | |||
| const ValueTree v (canvas.getDocument().getComponentState (component)); | |||
| sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, getParentComponent(), SizeGuideComponent::left)); | |||
| sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, getParentComponent(), SizeGuideComponent::right)); | |||
| sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, getParentComponent(), SizeGuideComponent::top)); | |||
| sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, getParentComponent(), SizeGuideComponent::bottom)); | |||
| } | |||
| } | |||
| void hideSizeGuides() | |||
| { | |||
| sizeGuides.clear(); | |||
| } | |||
| private: | |||
| Canvas& canvas; | |||
| Component::SafePointer<Component> component; | |||
| ResizableBorderComponent::Zone dragZone; | |||
| const int borderThickness; | |||
| OwnedArray <SizeGuideComponent> sizeGuides; | |||
| const Rectangle<int> getCentreArea() const | |||
| { | |||
| @@ -354,16 +551,19 @@ private: | |||
| { | |||
| isDraggingClickedComp = true; | |||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | |||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, | |||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(), | |||
| ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | |||
| } | |||
| canvas.getDocument().continueDrag (e); | |||
| showSizeGuides(); | |||
| } | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| hideSizeGuides(); | |||
| if (lasso != 0) | |||
| { | |||
| lasso->endLasso(); | |||
| @@ -377,6 +577,8 @@ private: | |||
| if (! isDraggingClickedComp) | |||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult); | |||
| } | |||
| canvas.getDocument().endDrag (e); | |||
| } | |||
| void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height) | |||
| @@ -403,6 +605,26 @@ private: | |||
| Desktop::getInstance().getMainMouseSource().triggerFakeMove(); | |||
| } | |||
| void showSizeGuides() | |||
| { | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i)); | |||
| if (resizer != 0) | |||
| resizer->showSizeGuides(); | |||
| } | |||
| } | |||
| void hideSizeGuides() | |||
| { | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i)); | |||
| if (resizer != 0) | |||
| resizer->hideSizeGuides(); | |||
| } | |||
| } | |||
| private: | |||
| Canvas& canvas; | |||
| ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso; | |||
| @@ -505,152 +505,326 @@ int indexOfLineStartingWith (const StringArray& lines, const String& text, int s | |||
| } | |||
| //============================================================================== | |||
| RelativePosition::RelativePosition() | |||
| : value (0), isRelative (false) | |||
| const char* Coordinate::parentLeftMarkerName = "parent.left"; | |||
| const char* Coordinate::parentRightMarkerName = "parent.right"; | |||
| const char* Coordinate::parentTopMarkerName = "parent.top"; | |||
| const char* Coordinate::parentBottomMarkerName = "parent.bottom"; | |||
| Coordinate::Coordinate (bool isHorizontal_) | |||
| : value (0), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal_) | |||
| : value (absoluteDistanceFromOrigin), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double absoluteDistance, const String& source, bool isHorizontal_) | |||
| : anchor1 (source), value (absoluteDistance), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double relativeProportion, const String& pos1, const String& pos2, bool isHorizontal_) | |||
| : anchor1 (pos1), anchor2 (pos2), value (relativeProportion), isProportion (true), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| RelativePosition::RelativePosition (double absoluteDistanceFromOrigin) | |||
| : value (absoluteDistanceFromOrigin), isRelative (false) | |||
| Coordinate::~Coordinate() | |||
| { | |||
| } | |||
| RelativePosition::RelativePosition (double absoluteDistance, const String& source) | |||
| : nameOfSource1 (source), value (absoluteDistance), isRelative (false) | |||
| const Coordinate Coordinate::getAnchorPoint1() const | |||
| { | |||
| return Coordinate (0.0, anchor1, isHorizontal); | |||
| } | |||
| RelativePosition::RelativePosition (double relativeProportion, const String& pos1, const String& pos2) | |||
| : nameOfSource1 (pos1), nameOfSource2 (pos2), value (relativeProportion), isRelative (true) | |||
| const Coordinate Coordinate::getAnchorPoint2() const | |||
| { | |||
| return Coordinate (0.0, anchor2, isHorizontal); | |||
| } | |||
| RelativePosition::~RelativePosition() | |||
| bool Coordinate::isOrigin (const String& name) | |||
| { | |||
| return name.isEmpty() || name == parentLeftMarkerName || name == parentTopMarkerName; | |||
| } | |||
| bool RelativePosition::isOrigin (const String& name) | |||
| const String Coordinate::getOriginMarkerName() const | |||
| { | |||
| return name.isEmpty() || name == parentOriginMarkerName; | |||
| return isHorizontal ? parentLeftMarkerName : parentTopMarkerName; | |||
| } | |||
| const String RelativePosition::checkName (const String& name) | |||
| const String Coordinate::getExtentMarkerName() const | |||
| { | |||
| return name.isEmpty() ? parentOriginMarkerName : name; | |||
| return isHorizontal ? parentRightMarkerName : parentBottomMarkerName; | |||
| } | |||
| double RelativePosition::getPosition (const String& name, PositionFinder& positionFinder) const | |||
| const String Coordinate::checkName (const String& name) const | |||
| { | |||
| return name.isEmpty() ? getOriginMarkerName() : name; | |||
| } | |||
| double Coordinate::getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const | |||
| { | |||
| if (isOrigin (name)) | |||
| return 0.0; | |||
| RelativePosition* const pos = positionFinder.findPosition (nameOfSource1); | |||
| return markerResolver.findMarker (name, isHorizontal) | |||
| .resolve (markerResolver, recursionCounter + 1); | |||
| } | |||
| if (pos != 0) | |||
| return pos->resolve (positionFinder); | |||
| struct RecursivePositionException | |||
| { | |||
| }; | |||
| double Coordinate::resolve (MarkerResolver& markerResolver, int recursionCounter) const | |||
| { | |||
| if (recursionCounter > 100) | |||
| { | |||
| jassertfalse | |||
| throw RecursivePositionException(); | |||
| } | |||
| const double pos1 = getPosition (anchor1, markerResolver, recursionCounter); | |||
| return isProportion ? pos1 + (getPosition (anchor2, markerResolver, recursionCounter) - pos1) * value | |||
| : pos1 + value; | |||
| } | |||
| double Coordinate::resolve (MarkerResolver& markerResolver) const | |||
| { | |||
| try | |||
| { | |||
| return resolve (markerResolver, 0); | |||
| } | |||
| catch (RecursivePositionException&) | |||
| {} | |||
| jassertfalse; | |||
| return 0.0; | |||
| } | |||
| double RelativePosition::resolve (PositionFinder& positionFinder) const | |||
| void Coordinate::moveToAbsolute (double newPos, MarkerResolver& markerResolver) | |||
| { | |||
| const double pos1 = getPos1 (positionFinder); | |||
| try | |||
| { | |||
| const double pos1 = getPosition (anchor1, markerResolver, 0); | |||
| if (isProportion) | |||
| { | |||
| const double size = getPosition (anchor2, markerResolver, 0) - pos1; | |||
| return isRelative ? pos1 + (getPos2 (positionFinder) - pos1) * value | |||
| : pos1 + value; | |||
| if (size != 0) | |||
| value = (newPos - pos1) / size; | |||
| } | |||
| else | |||
| { | |||
| value = newPos - pos1; | |||
| } | |||
| } | |||
| catch (RecursivePositionException&) | |||
| {} | |||
| } | |||
| void RelativePosition::moveToAbsolute (double newPos, PositionFinder& positionFinder) | |||
| bool Coordinate::isRecursive (MarkerResolver& markerResolver) const | |||
| { | |||
| const double pos1 = getPos1 (positionFinder); | |||
| try | |||
| { | |||
| resolve (markerResolver, 0); | |||
| } | |||
| catch (RecursivePositionException&) | |||
| { | |||
| return true; | |||
| } | |||
| if (isRelative) | |||
| value = (newPos - pos1) / (getPos2 (positionFinder) - pos1); | |||
| else | |||
| value = newPos - pos1; | |||
| return false; | |||
| } | |||
| RelativePosition::RelativePosition (const String& stringVersion) | |||
| void Coordinate::skipWhitespace (const String& s, int& i) | |||
| { | |||
| jassertfalse //todo | |||
| while (CharacterFunctions::isWhitespace (s[i])) | |||
| ++i; | |||
| } | |||
| const String RelativePosition::toString (int decimalPlaces) const | |||
| const String Coordinate::readMarkerName (const String& s, int& i) | |||
| { | |||
| if (isRelative) | |||
| skipWhitespace (s, i); | |||
| if (CharacterFunctions::isLetter (s[i]) || s[i] == '_') | |||
| { | |||
| const String percent (value * 100.0, 2); | |||
| int start = i; | |||
| while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.') | |||
| ++i; | |||
| if (isOrigin (nameOfSource1)) | |||
| return s.substring (start, i); | |||
| } | |||
| return String::empty; | |||
| } | |||
| double Coordinate::readNumber (const String& s, int& i) | |||
| { | |||
| skipWhitespace (s, i); | |||
| int start = i; | |||
| if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-') | |||
| ++i; | |||
| while (CharacterFunctions::isDigit (s[i]) || s[i] == '.') | |||
| ++i; | |||
| if ((s[i] == 'e' || s[i] == 'E') | |||
| && (CharacterFunctions::isDigit (s[i + 1]) | |||
| || s[i + 1] == '-' | |||
| || s[i + 1] == '+')) | |||
| { | |||
| i += 2; | |||
| while (CharacterFunctions::isDigit (s[i])) | |||
| ++i; | |||
| } | |||
| const double value = s.substring (start, i).getDoubleValue(); | |||
| while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',') | |||
| ++i; | |||
| return value; | |||
| } | |||
| Coordinate::Coordinate (const String& s, bool isHorizontal_) | |||
| : value (0), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| int i = 0; | |||
| anchor1 = readMarkerName (s, i); | |||
| if (anchor1.isNotEmpty()) | |||
| { | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '+') | |||
| value = readNumber (s, ++i); | |||
| else if (s[i] == '-') | |||
| value = -readNumber (s, ++i); | |||
| } | |||
| else | |||
| { | |||
| value = readNumber (s, i); | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '%') | |||
| { | |||
| isProportion = true; | |||
| value /= 100.0; | |||
| skipWhitespace (s, ++i); | |||
| if (s[i] == '*') | |||
| { | |||
| anchor1 = readMarkerName (s, ++i); | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '-' && s[i + 1] == '>') | |||
| { | |||
| i += 2; | |||
| anchor2 = readMarkerName (s, i); | |||
| } | |||
| else | |||
| { | |||
| anchor2 = anchor1; | |||
| anchor1 = getOriginMarkerName(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| anchor1 = getOriginMarkerName(); | |||
| anchor2 = getExtentMarkerName(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| const String Coordinate::toString() const | |||
| { | |||
| if (isProportion) | |||
| { | |||
| const String percent (value * 100.0); | |||
| if (isOrigin (anchor1)) | |||
| { | |||
| if (nameOfSource2 == parentExtentMarkerName) | |||
| if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName) | |||
| return percent + "%"; | |||
| else | |||
| return percent + "% of " + checkName (nameOfSource2); | |||
| return percent + "% * " + checkName (anchor2); | |||
| } | |||
| else | |||
| return percent + "% of " + checkName (nameOfSource1) + " to " + checkName (nameOfSource2); | |||
| return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2); | |||
| } | |||
| else | |||
| { | |||
| if (isOrigin (nameOfSource1)) | |||
| return String (value, decimalPlaces); | |||
| else if (value != 0) | |||
| return checkName (nameOfSource1) + " + " + String (value, decimalPlaces); | |||
| if (isOrigin (anchor1)) | |||
| return String (value); | |||
| else if (value > 0) | |||
| return checkName (anchor1) + " + " + String (value); | |||
| else if (value < 0) | |||
| return checkName (anchor1) + " - " + String (-value); | |||
| else | |||
| return checkName (nameOfSource1); | |||
| return checkName (anchor1); | |||
| } | |||
| } | |||
| const char* RelativePosition::parentOriginMarkerName = "origin"; | |||
| const char* RelativePosition::parentExtentMarkerName = "size"; | |||
| //============================================================================== | |||
| RelativeRectangle::RelativeRectangle() | |||
| RectangleCoordinates::RectangleCoordinates() | |||
| : left (true), right (true), top (false), bottom (false) | |||
| { | |||
| } | |||
| RelativeRectangle::RelativeRectangle (const Rectangle<int>& rect) | |||
| : left (rect.getX()), | |||
| right (rect.getRight()), | |||
| top (rect.getY()), | |||
| bottom (rect.getBottom()) | |||
| RectangleCoordinates::RectangleCoordinates (const Rectangle<int>& rect) | |||
| : left (rect.getX(), true), | |||
| right (rect.getWidth(), "left", true), | |||
| top (rect.getY(), false), | |||
| bottom (rect.getHeight(), "top", false) | |||
| { | |||
| } | |||
| RelativeRectangle::RelativeRectangle (const String& stringVersion) | |||
| RectangleCoordinates::RectangleCoordinates (const String& stringVersion) | |||
| : left (true), right (true), top (false), bottom (false) | |||
| { | |||
| jassertfalse // todo | |||
| StringArray tokens; | |||
| tokens.addTokens (stringVersion, ",", String::empty); | |||
| left = Coordinate (tokens [0], true); | |||
| top = Coordinate (tokens [1], false); | |||
| right = Coordinate (tokens [2], true); | |||
| bottom = Coordinate (tokens [3], false); | |||
| } | |||
| const Rectangle<int> RelativeRectangle::resolve (RelativePosition::PositionFinder& positionFinder) const | |||
| bool RectangleCoordinates::isRecursive (Coordinate::MarkerResolver& markerResolver) const | |||
| { | |||
| const int l = roundToInt (left.resolve (positionFinder)); | |||
| const int r = roundToInt (right.resolve (positionFinder)); | |||
| const int t = roundToInt (top.resolve (positionFinder)); | |||
| const int b = roundToInt (bottom.resolve (positionFinder)); | |||
| return left.isRecursive (markerResolver) || right.isRecursive (markerResolver) | |||
| || top.isRecursive (markerResolver) || bottom.isRecursive (markerResolver); | |||
| } | |||
| const Rectangle<int> RectangleCoordinates::resolve (Coordinate::MarkerResolver& markerResolver) const | |||
| { | |||
| const int l = roundToInt (left.resolve (markerResolver)); | |||
| const int r = roundToInt (right.resolve (markerResolver)); | |||
| const int t = roundToInt (top.resolve (markerResolver)); | |||
| const int b = roundToInt (bottom.resolve (markerResolver)); | |||
| return Rectangle<int> (l, t, r - l, b - t); | |||
| } | |||
| void RelativeRectangle::moveToAbsolute (const Rectangle<int>& newPos, RelativePosition::PositionFinder& positionFinder) | |||
| void RectangleCoordinates::moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver) | |||
| { | |||
| left.moveToAbsolute (newPos.getX(), positionFinder); | |||
| right.moveToAbsolute (newPos.getRight(), positionFinder); | |||
| top.moveToAbsolute (newPos.getY(), positionFinder); | |||
| bottom.moveToAbsolute (newPos.getBottom(), positionFinder); | |||
| // do it all again in case there were dependencies between some of the positions.. | |||
| left.moveToAbsolute (newPos.getX(), positionFinder); | |||
| right.moveToAbsolute (newPos.getRight(), positionFinder); | |||
| top.moveToAbsolute (newPos.getY(), positionFinder); | |||
| bottom.moveToAbsolute (newPos.getBottom(), positionFinder); | |||
| left.moveToAbsolute (newPos.getX(), markerResolver); | |||
| right.moveToAbsolute (newPos.getRight(), markerResolver); | |||
| top.moveToAbsolute (newPos.getY(), markerResolver); | |||
| bottom.moveToAbsolute (newPos.getBottom(), markerResolver); | |||
| } | |||
| const String RelativeRectangle::toString (int decimalPlaces) const | |||
| const String RectangleCoordinates::toString() const | |||
| { | |||
| return left.toString (decimalPlaces) + ", " + top.toString (decimalPlaces) | |||
| + ", " + right.toString (decimalPlaces) + ", " + bottom.toString (decimalPlaces); | |||
| return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString(); | |||
| } | |||
| @@ -111,56 +111,113 @@ private: | |||
| //============================================================================== | |||
| class RelativePosition | |||
| /** | |||
| Holds a co-ordinate along the x or y axis, expressed either as an absolute | |||
| position, or relative to other named marker positions. | |||
| */ | |||
| class Coordinate | |||
| { | |||
| public: | |||
| RelativePosition(); | |||
| explicit RelativePosition (const String& stringVersion); | |||
| explicit RelativePosition (double absoluteDistanceFromOrigin); | |||
| RelativePosition (double absoluteDistance, const String& source); | |||
| RelativePosition (double relativeProportion, const String& pos1, const String& pos2); | |||
| ~RelativePosition(); | |||
| class PositionFinder | |||
| { | |||
| public: | |||
| virtual ~PositionFinder() {} | |||
| virtual RelativePosition* findPosition (const String& name) = 0; | |||
| }; | |||
| //============================================================================== | |||
| /** Creates a zero coordinate. */ | |||
| Coordinate (bool isHorizontal); | |||
| /** Recreates a coordinate from its stringified version. */ | |||
| explicit Coordinate (const String& stringVersion, bool isHorizontal); | |||
| /** Creates an absolute position from the parent origin. */ | |||
| explicit Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal); | |||
| /** Creates an absolute position relative to a named marker. */ | |||
| Coordinate (double absolutePosition, const String& relativeToMarker, bool isHorizontal); | |||
| const String getName() const { return name; } | |||
| void setName (const String& newName) { name = newName; } | |||
| /** Creates a relative position between two named markers. */ | |||
| Coordinate (double relativePosition, const String& marker1, const String& marker2, bool isHorizontal); | |||
| double resolve (PositionFinder& positionFinder) const; | |||
| void moveToAbsolute (double newPos, PositionFinder& positionFinder); | |||
| /** Destructor. */ | |||
| ~Coordinate(); | |||
| const String toString (int decimalPlaces) const; | |||
| //============================================================================== | |||
| /** | |||
| Provides an interface for looking up the position of a named marker. | |||
| */ | |||
| class MarkerResolver | |||
| { | |||
| public: | |||
| virtual ~MarkerResolver() {} | |||
| virtual const Coordinate findMarker (const String& name, bool isHorizontal) = 0; | |||
| }; | |||
| static const char* parentOriginMarkerName; | |||
| static const char* parentExtentMarkerName; | |||
| /** Calculates the absolute position of this co-ordinate. */ | |||
| double resolve (MarkerResolver& markerResolver) const; | |||
| /** Returns true if this co-ordinate is expressed in terms of markers that form a recursive loop. */ | |||
| bool isRecursive (MarkerResolver& markerResolver) const; | |||
| /** Changes the value of this marker to make it resolve to the specified position. */ | |||
| void moveToAbsolute (double newPos, MarkerResolver& markerResolver); | |||
| const Coordinate getAnchorPoint1() const; | |||
| const Coordinate getAnchorPoint2() const; | |||
| //============================================================================== | |||
| /* | |||
| Position string formats: | |||
| 123 = absolute pixels from parent origin | |||
| marker | |||
| marker + 123 | |||
| marker - 123 | |||
| 50% = percentage between parent origin and parent extent | |||
| 50% * marker = percentage between parent origin and marker | |||
| 50% * marker1 -> marker2 = percentage between two markers | |||
| standard marker names: | |||
| "origin" = parent origin | |||
| "size" = parent right or bottom | |||
| "top", "left", "bottom", "right" = refer to the component's own left, right, top and bottom. | |||
| */ | |||
| const String toString() const; | |||
| //============================================================================== | |||
| static const char* parentLeftMarkerName; | |||
| static const char* parentRightMarkerName; | |||
| static const char* parentTopMarkerName; | |||
| static const char* parentBottomMarkerName; | |||
| private: | |||
| String name, nameOfSource1, nameOfSource2; | |||
| //============================================================================== | |||
| String anchor1, anchor2; | |||
| double value; | |||
| bool isRelative; | |||
| bool isProportion, isHorizontal; | |||
| double getPos1 (PositionFinder& positionFinder) const { return getPosition (nameOfSource1, positionFinder); } | |||
| double getPos2 (PositionFinder& positionFinder) const { return getPosition (nameOfSource2, positionFinder); } | |||
| double getPosition (const String& name, PositionFinder& positionFinder) const; | |||
| static const String checkName (const String& name); | |||
| double resolve (MarkerResolver& markerResolver, int recursionCounter) const; | |||
| double getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const; | |||
| const String checkName (const String& name) const; | |||
| const String getOriginMarkerName() const; | |||
| const String getExtentMarkerName() const; | |||
| static bool isOrigin (const String& name); | |||
| static void skipWhitespace (const String& s, int& i); | |||
| static const String readMarkerName (const String& s, int& i); | |||
| static double readNumber (const String& s, int& i); | |||
| }; | |||
| class RelativeRectangle | |||
| //============================================================================== | |||
| /** | |||
| Describes a rectangle as a set of Coordinate values. | |||
| */ | |||
| class RectangleCoordinates | |||
| { | |||
| public: | |||
| RelativeRectangle(); | |||
| explicit RelativeRectangle (const Rectangle<int>& rect); | |||
| explicit RelativeRectangle (const String& stringVersion); | |||
| const Rectangle<int> resolve (RelativePosition::PositionFinder& positionFinder) const; | |||
| void moveToAbsolute (const Rectangle<int>& newPos, RelativePosition::PositionFinder& positionFinder); | |||
| const String toString (int decimalPlaces) const; | |||
| RelativePosition left, right, top, bottom; | |||
| //============================================================================== | |||
| RectangleCoordinates(); | |||
| explicit RectangleCoordinates (const Rectangle<int>& rect); | |||
| explicit RectangleCoordinates (const String& stringVersion); | |||
| //============================================================================== | |||
| const Rectangle<int> resolve (Coordinate::MarkerResolver& markerResolver) const; | |||
| bool isRecursive (Coordinate::MarkerResolver& markerResolver) const; | |||
| void moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver); | |||
| const String toString() const; | |||
| Coordinate left, right, top, bottom; | |||
| }; | |||
| @@ -60932,13 +60932,13 @@ const Rectangle<int> ResizableBorderComponent::Zone::resizeRectangleBy (Rectangl | |||
| b.setLeft (b.getX() + offset.getX()); | |||
| if (isDraggingRightEdge()) | |||
| b.setWidth (b.getWidth() + offset.getX()); | |||
| b.setWidth (jmax (0, b.getWidth() + offset.getX())); | |||
| if (isDraggingTopEdge()) | |||
| b.setTop (b.getY() + offset.getY()); | |||
| if (isDraggingBottomEdge()) | |||
| b.setHeight (b.getHeight() + offset.getY()); | |||
| b.setHeight (jmax (0, b.getHeight() + offset.getY())); | |||
| return b; | |||
| } | |||
| @@ -211817,6 +211817,9 @@ void InterProcessLock::exit() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| // Trying to release the lock too many times! | |||
| jassert (pimpl != 0); | |||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | |||
| pimpl = 0; | |||
| } | |||
| @@ -228044,6 +228047,9 @@ void InterProcessLock::exit() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| // Trying to release the lock too many times! | |||
| jassert (pimpl != 0); | |||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | |||
| pimpl = 0; | |||
| } | |||
| @@ -238095,6 +238101,9 @@ void InterProcessLock::exit() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| // Trying to release the lock too many times! | |||
| jassert (pimpl != 0); | |||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | |||
| pimpl = 0; | |||
| } | |||
| @@ -8723,13 +8723,16 @@ public: | |||
| { | |||
| public: | |||
| inline explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lock.enter(); } | |||
| explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lockWasSuccessful = lock.enter(); } | |||
| inline ~ScopedLockType() { lock_.exit(); } | |||
| bool isLocked() const throw() { return lockWasSuccessful; } | |||
| private: | |||
| InterProcessLock& lock_; | |||
| bool lockWasSuccessful; | |||
| ScopedLockType (const ScopedLockType&); | |||
| ScopedLockType& operator= (const ScopedLockType&); | |||
| @@ -10062,6 +10065,11 @@ public: | |||
| return Rectangle (x + deltaPosition.getX(), y + deltaPosition.getY(), w, h); | |||
| } | |||
| const Rectangle operator- (const Point<ValueType>& deltaPosition) const throw() | |||
| { | |||
| return Rectangle (x - deltaPosition.getX(), y - deltaPosition.getY(), w, h); | |||
| } | |||
| void expand (const ValueType deltaX, | |||
| const ValueType deltaY) throw() | |||
| { | |||
| @@ -22888,6 +22896,8 @@ public: | |||
| const Rectangle<int> resizeRectangleBy (Rectangle<int> original, | |||
| const Point<int>& distance) const throw(); | |||
| int getZoneFlags() const throw() { return zone; } | |||
| private: | |||
| int zone; | |||
| @@ -99,13 +99,13 @@ const Rectangle<int> ResizableBorderComponent::Zone::resizeRectangleBy (Rectangl | |||
| b.setLeft (b.getX() + offset.getX()); | |||
| if (isDraggingRightEdge()) | |||
| b.setWidth (b.getWidth() + offset.getX()); | |||
| b.setWidth (jmax (0, b.getWidth() + offset.getX())); | |||
| if (isDraggingTopEdge()) | |||
| b.setTop (b.getY() + offset.getY()); | |||
| if (isDraggingBottomEdge()) | |||
| b.setHeight (b.getHeight() + offset.getY()); | |||
| b.setHeight (jmax (0, b.getHeight() + offset.getY())); | |||
| return b; | |||
| } | |||
| @@ -141,6 +141,9 @@ public: | |||
| const Rectangle<int> resizeRectangleBy (Rectangle<int> original, | |||
| const Point<int>& distance) const throw(); | |||
| /** Returns the raw flags for this zone. */ | |||
| int getZoneFlags() const throw() { return zone; } | |||
| private: | |||
| //============================================================================== | |||
| int zone; | |||
| @@ -221,6 +221,12 @@ public: | |||
| return Rectangle (x + deltaPosition.getX(), y + deltaPosition.getY(), w, h); | |||
| } | |||
| /** Returns a rectangle which is the same as this one moved by a given amount. */ | |||
| const Rectangle operator- (const Point<ValueType>& deltaPosition) const throw() | |||
| { | |||
| return Rectangle (x - deltaPosition.getX(), y - deltaPosition.getY(), w, h); | |||
| } | |||
| /** Expands the rectangle by a given amount. | |||
| Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). | |||
| @@ -570,6 +570,9 @@ void InterProcessLock::exit() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| // Trying to release the lock too many times! | |||
| jassert (pimpl != 0); | |||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | |||
| pimpl = 0; | |||
| } | |||
| @@ -418,6 +418,9 @@ void InterProcessLock::exit() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| // Trying to release the lock too many times! | |||
| jassert (pimpl != 0); | |||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | |||
| pimpl = 0; | |||
| } | |||
| @@ -86,11 +86,14 @@ public: | |||
| when the ScopedLockType object is deleted, the InterProcessLock will | |||
| be unlocked. | |||
| Note that since an InterprocessLock can fail due to errors, you should check | |||
| isLocked() to make sure that the lock was successful before using it. | |||
| Make sure this object is created and deleted by the same thread, | |||
| otherwise there are no guarantees what will happen! Best just to use it | |||
| as a local stack object, rather than creating one with the new() operator. | |||
| */ | |||
| inline explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lock.enter(); } | |||
| explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lockWasSuccessful = lock.enter(); } | |||
| /** Destructor. | |||
| @@ -101,9 +104,13 @@ public: | |||
| */ | |||
| inline ~ScopedLockType() { lock_.exit(); } | |||
| /** Returns true if the InterProcessLock was successfully locked. */ | |||
| bool isLocked() const throw() { return lockWasSuccessful; } | |||
| private: | |||
| //============================================================================== | |||
| InterProcessLock& lock_; | |||
| bool lockWasSuccessful; | |||
| ScopedLockType (const ScopedLockType&); | |||
| ScopedLockType& operator= (const ScopedLockType&); | |||
| @@ -86,10 +86,7 @@ public: | |||
| */ | |||
| inline ~ScopedTryLock() throw() { if (lockWasSuccessful) lock_.exit(); } | |||
| /** Lock state | |||
| @return True if the CriticalSection is locked. | |||
| */ | |||
| /** Returns true if the CriticalSection was successfully locked. */ | |||
| bool isLocked() const throw() { return lockWasSuccessful; } | |||
| private: | |||