diff --git a/extras/Jucer (experimental)/Source/model/Component Types/jucer_TextButton.h b/extras/Jucer (experimental)/Source/model/Component Types/jucer_TextButton.h index 1d1a067550..59c6b4ab68 100644 --- a/extras/Jucer (experimental)/Source/model/Component Types/jucer_TextButton.h +++ b/extras/Jucer (experimental)/Source/model/Component Types/jucer_TextButton.h @@ -36,24 +36,24 @@ public: Component* createComponent() { return new TextButton (String::empty); } const Rectangle getDefaultSize() { return Rectangle (0, 0, 150, 24); } - void updateComponent (Component* comp, const ValueTree& state) + void updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) { TextButton* tb = dynamic_cast (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 & props) + void createPropertyEditors (ComponentDocument& document, ValueTree& state, Array & 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."); } diff --git a/extras/Jucer (experimental)/Source/model/Component Types/jucer_ToggleButton.h b/extras/Jucer (experimental)/Source/model/Component Types/jucer_ToggleButton.h index 263c8513a4..c07bb2ea78 100644 --- a/extras/Jucer (experimental)/Source/model/Component Types/jucer_ToggleButton.h +++ b/extras/Jucer (experimental)/Source/model/Component Types/jucer_ToggleButton.h @@ -36,24 +36,24 @@ public: Component* createComponent() { return new ToggleButton (String::empty); } const Rectangle getDefaultSize() { return Rectangle (0, 0, 180, 24); } - void updateComponent (Component* comp, const ValueTree& state) + void updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) { ToggleButton* tb = dynamic_cast (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 & props) + void createPropertyEditors (ComponentDocument& document, ValueTree& state, Array & 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."); } diff --git a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp index d206237eaa..a4cf56cf88 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp @@ -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 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 (Random::getSystemRandom().nextInt (100) + 100, - Random::getSystemRandom().nextInt (100) + 100)).toString(), 0); + + const Rectangle bounds (getDefaultSize().withPosition (Point (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 & props) +void ComponentTypeHandler::createPropertyEditors (ComponentDocument& document, ValueTree& state, Array & 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 & 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& 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 pos; + + { + RectangleCoordinates relativePos (v [compBoundsProperty].toString()); + ScopedPointer markers (document.createMarkerResolver (v, parentComponent)); + pos = relativePos.resolve (*markers); + originalPositions.add (pos); + } const Rectangle 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& distance, const Rectangle& originalPos) + bool dragItem (ValueTree& v, const Point& distance, const Rectangle& originalPos) { - Rectangle newBounds (zone.resizeRectangleBy (originalPos, distance)); + const Rectangle newBounds (zone.resizeRectangleBy (originalPos, distance)); + + RectangleCoordinates pr (v [compBoundsProperty].toString()); + ScopedPointer 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 getVerticalSnapPositions (const Point& distance) const @@ -558,7 +631,7 @@ public: } private: - Rectangle parentComponentSize; + Component* parentComponent; ComponentDocument& document; Array draggedComponents; Array > originalPositions; @@ -566,9 +639,10 @@ private: const ResizableBorderComponent::Zone zone; }; -void ComponentDocument::beginDrag (const Array& items, const MouseEvent& e, const ResizableBorderComponent::Zone& zone) +void ComponentDocument::beginDrag (const Array& 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) diff --git a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h index b72e661237..1bb81896fa 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h +++ b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h @@ -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 & 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& 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 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 & 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 & props); Value getValue (const var::identifier& name, ValueTree& state, ComponentDocument& document) const; diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp index 963563b183..91e1b456ff 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp @@ -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 markers (document.createMarkerResolver (state, component->getParentComponent())); + int anchor1 = coord.getAnchorPoint1().resolve (*markers); + int anchor2 = coord.getAnchorPoint2().resolve (*markers); + + Point p1, p2; + + switch (type) + { + case left: p1 = Point (component->getX(), component->getY() + component->proportionOfHeight (0.33f)); + p2 = Point (anchor1, p1.getY()); break; + case right: p1 = Point (component->getRight(), component->getY() + component->proportionOfHeight (0.66f)); + p2 = Point (anchor1, p1.getY()); break; + case top: p1 = Point (component->getX() + component->proportionOfWidth (0.33f), component->getY()); + p2 = Point (p1.getX(), anchor1); break; + case bottom: p1 = Point (component->getX() + component->proportionOfWidth (0.66f), component->getBottom()); + p2 = Point (p1.getX(), anchor1); break; + default: jassertfalse; break; + } + + Rectangle bounds (Rectangle (p1, p2).expanded (4, 4)); + Point textPos ((p1.getX() + p2.getX()) / 2, + (p1.getY() + p2.getY()) / 2); + int textW = (int) font.getStringWidth (getName()); + int textH = (int) font.getHeight(); + Rectangle textRect (textPos.getX() - textW / 2, textPos.getY() - textH / 2, textW, textH); + + if (isHorizontal) + textRect = textRect - Point (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 lineEnd1, lineEnd2; + Rectangle 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& 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 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; ResizableBorderComponent::Zone dragZone; const int borderThickness; + OwnedArray sizeGuides; const Rectangle 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 & 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 (getChildComponent(i)); + if (resizer != 0) + resizer->showSizeGuides(); + } + } + + void hideSizeGuides() + { + for (int i = getNumChildComponents(); --i >= 0;) + { + ComponentResizeFrame* resizer = dynamic_cast (getChildComponent(i)); + if (resizer != 0) + resizer->hideSizeGuides(); + } + } + private: Canvas& canvas; ScopedPointer > lasso; diff --git a/extras/Jucer (experimental)/Source/utility/jucer_UtilityFunctions.cpp b/extras/Jucer (experimental)/Source/utility/jucer_UtilityFunctions.cpp index 3a93326562..5cb45168da 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_UtilityFunctions.cpp +++ b/extras/Jucer (experimental)/Source/utility/jucer_UtilityFunctions.cpp @@ -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& rect) - : left (rect.getX()), - right (rect.getRight()), - top (rect.getY()), - bottom (rect.getBottom()) +RectangleCoordinates::RectangleCoordinates (const Rectangle& 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 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 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 (l, t, r - l, b - t); } -void RelativeRectangle::moveToAbsolute (const Rectangle& newPos, RelativePosition::PositionFinder& positionFinder) +void RectangleCoordinates::moveToAbsolute (const Rectangle& 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(); } diff --git a/extras/Jucer (experimental)/Source/utility/jucer_UtilityFunctions.h b/extras/Jucer (experimental)/Source/utility/jucer_UtilityFunctions.h index 0bc12b1f92..8390aef2d9 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_UtilityFunctions.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_UtilityFunctions.h @@ -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& rect); - explicit RelativeRectangle (const String& stringVersion); - - const Rectangle resolve (RelativePosition::PositionFinder& positionFinder) const; - void moveToAbsolute (const Rectangle& newPos, RelativePosition::PositionFinder& positionFinder); - const String toString (int decimalPlaces) const; - - RelativePosition left, right, top, bottom; + //============================================================================== + RectangleCoordinates(); + explicit RectangleCoordinates (const Rectangle& rect); + explicit RectangleCoordinates (const String& stringVersion); + + //============================================================================== + const Rectangle resolve (Coordinate::MarkerResolver& markerResolver) const; + bool isRecursive (Coordinate::MarkerResolver& markerResolver) const; + void moveToAbsolute (const Rectangle& newPos, Coordinate::MarkerResolver& markerResolver); + const String toString() const; + + Coordinate left, right, top, bottom; }; diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 61fc1ac0a8..5f27749739 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -60932,13 +60932,13 @@ const Rectangle 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; } diff --git a/juce_amalgamated.h b/juce_amalgamated.h index cb1eee62b1..2b9a8e7122 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -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& 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 resizeRectangleBy (Rectangle original, const Point& distance) const throw(); + int getZoneFlags() const throw() { return zone; } + private: int zone; diff --git a/src/gui/components/layout/juce_ResizableBorderComponent.cpp b/src/gui/components/layout/juce_ResizableBorderComponent.cpp index 11e3d123a8..c9a0a80ba6 100644 --- a/src/gui/components/layout/juce_ResizableBorderComponent.cpp +++ b/src/gui/components/layout/juce_ResizableBorderComponent.cpp @@ -99,13 +99,13 @@ const Rectangle 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; } diff --git a/src/gui/components/layout/juce_ResizableBorderComponent.h b/src/gui/components/layout/juce_ResizableBorderComponent.h index 25d7db2eae..afab936ecb 100644 --- a/src/gui/components/layout/juce_ResizableBorderComponent.h +++ b/src/gui/components/layout/juce_ResizableBorderComponent.h @@ -141,6 +141,9 @@ public: const Rectangle resizeRectangleBy (Rectangle original, const Point& distance) const throw(); + /** Returns the raw flags for this zone. */ + int getZoneFlags() const throw() { return zone; } + private: //============================================================================== int zone; diff --git a/src/gui/graphics/geometry/juce_Rectangle.h b/src/gui/graphics/geometry/juce_Rectangle.h index 769494eff7..81a1b6a32b 100644 --- a/src/gui/graphics/geometry/juce_Rectangle.h +++ b/src/gui/graphics/geometry/juce_Rectangle.h @@ -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& 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). diff --git a/src/native/common/juce_posix_SharedCode.h b/src/native/common/juce_posix_SharedCode.h index cad1cbd894..87d0291fa9 100644 --- a/src/native/common/juce_posix_SharedCode.h +++ b/src/native/common/juce_posix_SharedCode.h @@ -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; } diff --git a/src/native/windows/juce_win32_Threads.cpp b/src/native/windows/juce_win32_Threads.cpp index 843f1e0a53..7c23a6ad62 100644 --- a/src/native/windows/juce_win32_Threads.cpp +++ b/src/native/windows/juce_win32_Threads.cpp @@ -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; } diff --git a/src/threads/juce_InterProcessLock.h b/src/threads/juce_InterProcessLock.h index 8e5c1e0653..eff54cd6d3 100644 --- a/src/threads/juce_InterProcessLock.h +++ b/src/threads/juce_InterProcessLock.h @@ -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&); diff --git a/src/threads/juce_ScopedTryLock.h b/src/threads/juce_ScopedTryLock.h index 17c3711313..890f84e6b7 100644 --- a/src/threads/juce_ScopedTryLock.h +++ b/src/threads/juce_ScopedTryLock.h @@ -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: