diff --git a/extras/Jucer (experimental)/Builds/Linux/Makefile b/extras/Jucer (experimental)/Builds/Linux/Makefile index d37c763f10..e348fc5946 100644 --- a/extras/Jucer (experimental)/Builds/Linux/Makefile +++ b/extras/Jucer (experimental)/Builds/Linux/Makefile @@ -52,6 +52,7 @@ OBJECTS := \ $(OBJDIR)/jucer_ProjectExporter.o \ $(OBJDIR)/jucer_ProjectWizard.o \ $(OBJDIR)/jucer_ResourceFile.o \ + $(OBJDIR)/jucer_ComponentEditorCanvas.o \ $(OBJDIR)/jucer_ComponentEditor.o \ $(OBJDIR)/jucer_DrawableEditor.o \ $(OBJDIR)/jucer_DocumentEditorComponent.o \ @@ -134,6 +135,11 @@ $(OBJDIR)/jucer_ResourceFile.o: ../../Source/model/jucer_ResourceFile.cpp @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" +$(OBJDIR)/jucer_ComponentEditorCanvas.o: ../../Source/ui/Component\ Editor/jucer_ComponentEditorCanvas.cpp + -@mkdir -p $(OBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + $(OBJDIR)/jucer_ComponentEditor.o: ../../Source/ui/Component\ Editor/jucer_ComponentEditor.cpp -@mkdir -p $(OBJDIR) @echo $(notdir $<) diff --git a/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj b/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj index bf05c190b2..406084abb4 100644 --- a/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj +++ b/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 60CDC1358E84801B6526E434 = { isa = PBXBuildFile; fileRef = FE9A53032395E717F54AE85B; }; EEC6FC8E546C88C825411DB2 = { isa = PBXBuildFile; fileRef = 0AD266A3E698D40DCD88A432; }; B2B821DE12F1679A3ADA597A = { isa = PBXBuildFile; fileRef = DBE9A3BB502125C5D3433AE7; }; + CDB5102BEFD7C18BD28709F3 = { isa = PBXBuildFile; fileRef = 81D9A078702EFE0A32C037A1; }; D706C3B5016318D85FE452C2 = { isa = PBXBuildFile; fileRef = 3263F4099F45D6FACD28F08D; }; E43D00B370F289420379B759 = { isa = PBXBuildFile; fileRef = 82F91CF84A296665177CB79A; }; 944CE0EADAD951F48EC77071 = { isa = PBXBuildFile; fileRef = 3B2C45064E85B3B631D4F921; }; @@ -94,6 +95,9 @@ 91C67387B525014760F514C2 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_ProjectWizard.h; path = ../../Source/model/jucer_ProjectWizard.h; sourceTree = SOURCE_ROOT; }; DBE9A3BB502125C5D3433AE7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_ResourceFile.cpp; path = ../../Source/model/jucer_ResourceFile.cpp; sourceTree = SOURCE_ROOT; }; 2DEE6D9FE17874DAB301648C = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_ResourceFile.h; path = ../../Source/model/jucer_ResourceFile.h; sourceTree = SOURCE_ROOT; }; + BAA4B3ECAA1344B0B4052542 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_ComponentDragOperation.h; path = "../../Source/ui/Component Editor/jucer_ComponentDragOperation.h"; sourceTree = SOURCE_ROOT; }; + 81D9A078702EFE0A32C037A1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_ComponentEditorCanvas.cpp; path = "../../Source/ui/Component Editor/jucer_ComponentEditorCanvas.cpp"; sourceTree = SOURCE_ROOT; }; + 3B77C7A8D9A2386088D75CFE = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_ComponentEditorCanvas.h; path = "../../Source/ui/Component Editor/jucer_ComponentEditorCanvas.h"; sourceTree = SOURCE_ROOT; }; 3263F4099F45D6FACD28F08D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_ComponentEditor.cpp; path = "../../Source/ui/Component Editor/jucer_ComponentEditor.cpp"; sourceTree = SOURCE_ROOT; }; 16328135EAE8536A5E2AB454 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_ComponentEditor.h; path = "../../Source/ui/Component Editor/jucer_ComponentEditor.h"; sourceTree = SOURCE_ROOT; }; 82F91CF84A296665177CB79A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_DrawableEditor.cpp; path = "../../Source/ui/Drawable Editor/jucer_DrawableEditor.cpp"; sourceTree = SOURCE_ROOT; }; @@ -191,6 +195,9 @@ DBE9A3BB502125C5D3433AE7, 2DEE6D9FE17874DAB301648C ); name = Model; sourceTree = ""; }; 3A0BE83502CB509D623C2C07 = { isa = PBXGroup; children = ( + BAA4B3ECAA1344B0B4052542, + 81D9A078702EFE0A32C037A1, + 3B77C7A8D9A2386088D75CFE, 3263F4099F45D6FACD28F08D, 16328135EAE8536A5E2AB454 ); name = "Component Editor"; sourceTree = ""; }; E6053BD673F80E900DDA3593 = { isa = PBXGroup; children = ( @@ -357,6 +364,7 @@ 60CDC1358E84801B6526E434, EEC6FC8E546C88C825411DB2, B2B821DE12F1679A3ADA597A, + CDB5102BEFD7C18BD28709F3, D706C3B5016318D85FE452C2, E43D00B370F289420379B759, 944CE0EADAD951F48EC77071, diff --git a/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj b/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj index 8f9c6650ae..fef4486a97 100644 --- a/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj +++ b/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj @@ -172,6 +172,9 @@ + + + diff --git a/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj b/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj index b90d01f837..1d1c6f7bfd 100644 --- a/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj +++ b/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj @@ -172,6 +172,9 @@ + + + diff --git a/extras/Jucer (experimental)/Jucer.jucer b/extras/Jucer (experimental)/Jucer.jucer index f8d1ffeb51..be9986fa81 100644 --- a/extras/Jucer (experimental)/Jucer.jucer +++ b/extras/Jucer (experimental)/Jucer.jucer @@ -100,6 +100,12 @@ + + + markers (document.createMarkerResolver (compState)); + ScopedPointer markers (document.createComponentMarkerResolver (compState)); if (button == proportionButton) { @@ -265,7 +265,7 @@ Value ComponentTypeHandler::getValue (const var::identifier& name, ValueTree& st void ComponentTypeHandler::updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) { RectangleCoordinates pos (state [ComponentDocument::compBoundsProperty].toString()); - ScopedPointer markers (document.createMarkerResolver (state)); + ScopedPointer markers (document.createComponentMarkerResolver (state)); comp->setBounds (pos.resolve (*markers)); comp->setName (state [ComponentDocument::compNameProperty]); diff --git a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp index 302c4eb113..ea0dba722a 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp @@ -30,6 +30,9 @@ //============================================================================== static const char* const componentDocumentTag = "COMPONENT"; static const char* const componentGroupTag = "COMPONENTS"; +static const char* const markersGroupXTag = "MARKERS_X"; +static const char* const markersGroupYTag = "MARKERS_Y"; +static const char* const markerTag = "MARKER"; static const char* const metadataTagStart = "JUCER_" "COMPONENT_METADATA_START"; // written like this to avoid thinking this file is a component! static const char* const metadataTagEnd = "JUCER_" "COMPONENT_METADATA_END"; @@ -38,16 +41,22 @@ const char* const ComponentDocument::idProperty = "id"; const char* const ComponentDocument::compBoundsProperty = "position"; const char* const ComponentDocument::memberNameProperty = "memberName"; const char* const ComponentDocument::compNameProperty = "name"; +const char* const ComponentDocument::markerNameProperty = "name"; +const char* const ComponentDocument::markerPosProperty = "position"; //============================================================================== ComponentDocument::ComponentDocument (Project* project_, const File& cppFile_) - : project (project_), cppFile (cppFile_), root (componentDocumentTag), + : project (project_), cppFile (cppFile_), + root (componentDocumentTag), changedSinceSaved (false) { reload(); checkRootObject(); + markersX = new MarkerList (*this, true); + markersY = new MarkerList (*this, false); + root.addListener (this); } @@ -195,12 +204,19 @@ bool ComponentDocument::hasChangedSinceLastSave() return changedSinceSaved; } +void ComponentDocument::createSubTreeIfNotThere (const String& name) +{ + if (! root.getChildWithName (name).isValid()) + root.addChild (ValueTree (name), -1, 0); +} + void ComponentDocument::checkRootObject() { jassert (root.hasType (componentDocumentTag)); - if (! getComponentGroup().isValid()) - root.addChild (ValueTree (componentGroupTag), -1, 0); + createSubTreeIfNotThere (componentGroupTag); + createSubTreeIfNotThere (markersGroupXTag); + createSubTreeIfNotThere (markersGroupYTag); if (getClassName().toString().isEmpty()) getClassName() = "NewComponent"; @@ -334,7 +350,7 @@ bool ComponentDocument::setCoordsFor (ValueTree& state, const RectangleCoordinat return true; } -Coordinate::MarkerResolver* ComponentDocument::createMarkerResolver (const ValueTree& state) +Coordinate::MarkerResolver* ComponentDocument::createComponentMarkerResolver (const ValueTree& state) { return new ComponentMarkerResolver (*this, state, getCanvasWidth().getValue(), getCanvasHeight().getValue()); } @@ -438,6 +454,85 @@ const String ComponentDocument::getNonExistentMemberName (String suggestedName) return suggestedName; } +//============================================================================== +ComponentDocument::MarkerList::MarkerList (ComponentDocument& document_, const bool isX_) + : document (document_), + group (document_.getRoot().getChildWithName (isX_ ? markersGroupXTag : markersGroupYTag)), + isX (isX_) +{ + jassert (group.isValid()); +} + +ValueTree ComponentDocument::MarkerList::getGroup() const +{ + return group; +} + +int ComponentDocument::MarkerList::size() const +{ + return group.getNumChildren(); +} + +ValueTree ComponentDocument::MarkerList::getMarker (int index) const +{ + return group.getChild (index); +} + +ValueTree ComponentDocument::MarkerList::getMarkerNamed (const String& name) const +{ + return group.getChildWithProperty (markerNameProperty, name); +} + +bool ComponentDocument::MarkerList::contains (const ValueTree& markerState) const +{ + return markerState.isAChildOf (group); +} + +const Coordinate ComponentDocument::MarkerList::getCoordinate (const ValueTree& markerState) const +{ + return Coordinate (markerState [markerPosProperty].toString(), isX); +} + +void ComponentDocument::MarkerList::setCoordinate (ValueTree& markerState, const Coordinate& newCoord) +{ + markerState.setProperty (markerPosProperty, newCoord.toString(), document.getUndoManager()); +} + +void ComponentDocument::MarkerList::createMarker (const String& name, int position) +{ + ValueTree marker (markerTag); + marker.setProperty (markerNameProperty, document.getNonexistentMarkerName (name), 0); + marker.setProperty (markerPosProperty, Coordinate (position, isX).toString(), 0); + group.addChild (marker, -1, document.getUndoManager()); +} + +void ComponentDocument::MarkerList::deleteMarker (ValueTree& markerState) +{ + group.removeChild (markerState, document.getUndoManager()); +} + +const Coordinate ComponentDocument::MarkerList::findMarker (const String& name, bool isHorizontal) +{ + if (isHorizontal == isX) + { + if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) document.getCanvasWidth().getValue(), isHorizontal); + else if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) document.getCanvasHeight().getValue(), isHorizontal); + } + + return Coordinate (isX); +} + +const String ComponentDocument::getNonexistentMarkerName (const String& name) +{ + String n (makeValidCppIdentifier (name, false, true, false)); + int suffix = 2; + + while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid()) + n = n.trimCharactersAtEnd ("0123456789") + String (suffix++); + + return n; +} + //============================================================================== UndoManager* ComponentDocument::getUndoManager() { diff --git a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h index efe70147c6..b5bb6d301f 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h +++ b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h @@ -68,7 +68,7 @@ public: 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); + Coordinate::MarkerResolver* createComponentMarkerResolver (const ValueTree& state); const StringArray getComponentMarkers (bool horizontal) const; const RectangleCoordinates getCoordsFor (const ValueTree& componentState) const; bool setCoordsFor (ValueTree& componentState, const RectangleCoordinates& newSize); @@ -76,6 +76,37 @@ public: void addNewComponentMenuItems (PopupMenu& menu) const; void performNewComponentMenuItem (int menuResultCode); + //============================================================================== + class MarkerList : public Coordinate::MarkerResolver + { + public: + MarkerList (ComponentDocument& document, bool isX); + + ValueTree getGroup() const; + int size() const; + ValueTree getMarker (int index) const; + ValueTree getMarkerNamed (const String& name) const; + bool contains (const ValueTree& markerState) const; + const Coordinate getCoordinate (const ValueTree& markerState) const; + void setCoordinate (ValueTree& markerState, const Coordinate& newCoord); + void createMarker (const String& name, int position); + void deleteMarker (ValueTree& markerState); + + // for Coordinate::MarkerResolver: + const Coordinate findMarker (const String& name, bool isHorizontal); + + private: + ComponentDocument& document; + ValueTree group; + const bool isX; + }; + + MarkerList& getMarkerListX() { return *markersX; } + MarkerList& getMarkerListY() { return *markersY; } + MarkerList& getMarkerList (bool isX) { return isX ? *markersX : *markersY; } + + const String getNonexistentMarkerName (const String& name); + //============================================================================== void beginDrag (const Array& items, const MouseEvent& e, Component* parentForOverlays, const ResizableBorderComponent::Zone& zone); @@ -95,15 +126,19 @@ public: static const char* const compBoundsProperty; static const char* const memberNameProperty; static const char* const compNameProperty; + static const char* const markerNameProperty; + static const char* const markerPosProperty; private: Project* project; File cppFile; ValueTree root; + ScopedPointer markersX, markersY; UndoManager undoManager; bool changedSinceSaved; void checkRootObject(); + void createSubTreeIfNotThere (const String& name); ValueTree getComponentGroup() const; Value getRootValueUndoable (const var::identifier& name) { return root.getPropertyAsValue (name, getUndoManager()); } diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentDragOperation.h b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentDragOperation.h new file mode 100644 index 0000000000..4999d72867 --- /dev/null +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentDragOperation.h @@ -0,0 +1,334 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-9 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + + +//============================================================================== +class ComponentEditorCanvas::DragOperation +{ +public: + DragOperation (ComponentEditorCanvas& canvas_, + const Array& items, + const Array& itemsToSnapTo, + const MouseEvent& e, + Component* snapGuideParentComp_, + const ResizableBorderComponent::Zone& zone_) + : canvas (canvas_), + snapGuideParentComp (snapGuideParentComp_), + zone (zone_) + { + int i; + for (i = 0; i < items.size(); ++i) + { + jassert (items.getUnchecked(i) != 0); + const ValueTree v (getDocument().getComponentState (items.getUnchecked(i))); + draggedComponents.add (v); + const Rectangle floatPos (getComponentPosition (v)); + + if (zone.isDraggingWholeObject() || zone.isDraggingLeftEdge()) + verticalSnapPositions.add (SnapLine (floatPos.getX(), floatPos.getY(), floatPos.getBottom())); + + if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge())) + verticalSnapPositions.add (SnapLine (floatPos.getCentreX(), floatPos.getY(), floatPos.getBottom())); + + if (zone.isDraggingWholeObject() || zone.isDraggingRightEdge()) + verticalSnapPositions.add (SnapLine (floatPos.getRight(), floatPos.getY(), floatPos.getBottom())); + + if (zone.isDraggingWholeObject() || zone.isDraggingTopEdge()) + horizontalSnapPositions.add (SnapLine (floatPos.getY(), floatPos.getX(), floatPos.getRight())); + + if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge())) + horizontalSnapPositions.add (SnapLine (floatPos.getCentreY(), floatPos.getX(), floatPos.getRight())); + + if (zone.isDraggingWholeObject() || zone.isDraggingBottomEdge()) + horizontalSnapPositions.add (SnapLine (floatPos.getBottom(), floatPos.getX(), floatPos.getRight())); + } + + if (isDraggingLeftRight()) + { + verticalSnapTargets.add (SnapLine (0, -100.0f, 10000.0f)); + verticalSnapTargets.add (SnapLine (getDocument().getCanvasWidth().getValue(), -100.0f, 10000.0f)); + + if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge())) + verticalSnapTargets.add (SnapLine ((float) getDocument().getCanvasWidth().getValue() / 2.0f, 0, 10000.0f)); + } + + if (isDraggingUpDown()) + { + horizontalSnapTargets.add (SnapLine (0, -100.0f, 10000.0f)); + horizontalSnapTargets.add (SnapLine (getDocument().getCanvasHeight().getValue(), -100.0f, 10000.0f)); + + if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge())) + horizontalSnapTargets.add (SnapLine ((float) getDocument().getCanvasHeight().getValue() / 2.0f, 0, 10000.0f)); + } + + for (i = 0; i < itemsToSnapTo.size(); ++i) + { + jassert (itemsToSnapTo.getUnchecked(i) != 0); + const ValueTree v (getDocument().getComponentState (itemsToSnapTo.getUnchecked(i))); + const Rectangle floatPos (getComponentPosition (v)); + + if (isDraggingLeftRight()) + { + verticalSnapTargets.add (SnapLine (floatPos.getX(), floatPos.getY(), floatPos.getBottom())); + verticalSnapTargets.add (SnapLine (floatPos.getRight(), floatPos.getY(), floatPos.getBottom())); + } + + if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge())) + verticalSnapTargets.add (SnapLine (floatPos.getCentreX(), floatPos.getY(), floatPos.getBottom())); + + if (isDraggingUpDown()) + { + horizontalSnapTargets.add (SnapLine (floatPos.getY(), floatPos.getX(), floatPos.getRight())); + horizontalSnapTargets.add (SnapLine (floatPos.getBottom(), floatPos.getX(), floatPos.getRight())); + } + + if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge())) + horizontalSnapTargets.add (SnapLine (floatPos.getCentreY(), floatPos.getX(), floatPos.getRight())); + } + + mergeSnapLines (verticalSnapTargets); + mergeSnapLines (horizontalSnapTargets); + + getDocument().beginNewTransaction(); + } + + ~DragOperation() + { + getDocument().beginNewTransaction(); + } + + //============================================================================== + struct SnapLine + { + SnapLine() : position (0), start (0), end (0) {} + + SnapLine (const float position_, const float start_, const float end_) + : position (position_), start (start_), end (end_) + {} + + float position, start, end; + }; + + //============================================================================== + class AlignmentHintComponent : public OverlayItemComponent + { + public: + AlignmentHintComponent (ComponentEditorCanvas& canvas_, const SnapLine& line_, bool isVertical_, Component* parent) + : OverlayItemComponent (canvas_), line (line_), isVertical (isVertical_) + { + const int extraEndLength = 5; + setAlwaysOnTop (true); + parent->addAndMakeVisible (this); + + if (isVertical) + setBoundsInTargetSpace (Rectangle (roundToInt (line.position), roundToInt (line.start) - extraEndLength, + 1, roundToInt (line.end - line.start) + extraEndLength * 2)); + else + setBoundsInTargetSpace (Rectangle (roundToInt (line.start) - extraEndLength, roundToInt (line.position), + roundToInt (line.end - line.start) + extraEndLength * 2, 1)); + } + + void paint (Graphics& g) + { + g.fillAll (alignmentMarkerColour); + } + + private: + const SnapLine line; + const bool isVertical; + + AlignmentHintComponent (const AlignmentHintComponent&); + AlignmentHintComponent& operator= (const AlignmentHintComponent&); + }; + + //============================================================================== + void drag (const MouseEvent& e) + { + getDocument().getUndoManager()->undoCurrentTransactionOnly(); + + Point distance (e.getOffsetFromDragStart()); + if (! isDraggingLeftRight()) + distance = Point (0, distance.getY()); + + if (! isDraggingUpDown()) + distance = Point (distance.getX(), 0); + + snapGuides.clear(); + + performSnap (verticalSnapTargets, getVerticalSnapPositions (distance), true, distance); + performSnap (horizontalSnapTargets, getHorizontalSnapPositions (distance), false, distance); + + 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), distance, originalPositions.getReference(i))) + anyUpdated = true; + + if (! anyUpdated) + break; + + if (--n == 0) + { + jassertfalse; + break; + } + } + } + + bool dragItem (ValueTree& v, const Point& distance, const Rectangle& originalPos) + { + const Rectangle newBounds (zone.resizeRectangleBy (originalPos, distance)); + + RectangleCoordinates pr (getDocument().getCoordsFor (v)); + ScopedPointer markers (getDocument().createComponentMarkerResolver (v)); + + pr.moveToAbsolute (newBounds, *markers); + + return getDocument().setCoordsFor (v, pr); + } + + //============================================================================== +private: + ComponentEditorCanvas& canvas; + Array draggedComponents; + Array > originalPositions; + Array verticalSnapPositions, horizontalSnapPositions; + Array verticalSnapTargets, horizontalSnapTargets; + const ResizableBorderComponent::Zone zone; + OwnedArray snapGuides; + Component* snapGuideParentComp; + + ComponentDocument& getDocument() throw() { return canvas.getDocument(); } + + const Rectangle getComponentPosition (const ValueTree& state) + { + RectangleCoordinates relativePos (getDocument().getCoordsFor (state)); + ScopedPointer markers (getDocument().createComponentMarkerResolver (state)); + const Rectangle intPos (relativePos.resolve (*markers)); + originalPositions.add (intPos); + + return intPos.toFloat(); + } + + static void mergeSnapLines (Array & lines) + { + for (int i = lines.size(); --i > 0;) + { + SnapLine& s1 = lines.getReference(i); + + for (int j = i; --j >= 0;) + { + SnapLine& s2 = lines.getReference(j); + + if (s1.position == s2.position) + { + s2.start = jmin (s1.start, s2.start); + s2.end = jmax (s1.end, s2.end); + lines.remove (i); + } + } + } + } + + void performSnap (const Array& targets, const Array& sources, bool isVertical, Point& distance) + { + if (targets.size() == 0 || sources.size() == 0) + return; + + float best = std::numeric_limits::max(); + float absBest = fabsf (best); + Array lines; + + for (int i = 0; i < targets.size(); ++i) + { + const SnapLine& target = targets.getReference(i); + + for (int j = 0; j < sources.size(); ++j) + { + const SnapLine& source = sources.getReference(j); + const float diff = target.position - source.position; + const float absDiff = fabsf (diff); + + if (absDiff <= absBest) + { + if (absDiff < absBest) + lines.clearQuick(); + + lines.add (SnapLine (target.position, jmin (target.start, source.start), jmax (target.end, source.end))); + best = diff; + absBest = absDiff; + } + } + } + + jassert (absBest < std::numeric_limits::max()); + + if (absBest < snapDistance) + { + distance += isVertical ? Point (roundToInt (best), 0) : Point (0, roundToInt (best)); + + for (int i = lines.size(); --i >= 0;) + snapGuides.add (new AlignmentHintComponent (canvas, lines.getReference(i), isVertical, snapGuideParentComp)); + } + } + + const Array getVerticalSnapPositions (const Point& distance) const + { + Array p (verticalSnapPositions); + for (int i = p.size(); --i >= 0;) + { + SnapLine& s = p.getReference(i); + s.position += distance.getX(); + s.start += distance.getY(); + s.end += distance.getY(); + } + + return p; + } + + const Array getHorizontalSnapPositions (const Point& distance) const + { + Array p (horizontalSnapPositions); + for (int i = p.size(); --i >= 0;) + { + SnapLine& s = p.getReference(i); + s.position += distance.getY(); + s.start += distance.getX(); + s.end += distance.getX(); + } + + return p; + } + + bool isDraggingLeftRight() const { return zone.isDraggingWholeObject() || zone.isDraggingLeftEdge() || zone.isDraggingRightEdge(); } + bool isDraggingUpDown() const { return zone.isDraggingWholeObject() || zone.isDraggingTopEdge() || zone.isDraggingBottomEdge(); } + + DragOperation (const DragOperation&); + DragOperation& operator= (const DragOperation&); +}; 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 627fb04252..4756def4d5 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp @@ -27,1064 +27,6 @@ #include "jucer_ComponentEditor.h" -const float snapDistance = 8.0f; -static const Colour resizableBorderColour (0x7066aaff); -static const Colour alignmentMarkerColour (0x77ff0000); - - -//============================================================================== -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 paint (Graphics& g) - { - const float dashes[] = { 4.0f, 3.0f }; - - g.setColour (resizableBorderColour); - g.drawDashedLine (lineEnd1.getX() + 0.5f, lineEnd1.getY() + 0.5f, - lineEnd2.getX() + 0.5f, lineEnd2.getY() + 0.5f, - dashes, 2, 1.0f); - - 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; - } - - //============================================================================== - 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()); - - int textW = (int) font.getStringWidth (getName()); - int textH = (int) font.getHeight(); - - Point p1, p2; - - switch (type) - { - case left: - p1 = Point (component->getX(), 0); - p2 = Point (component->getX(), component->getY()); - textArea.setBounds (p1.getX() - textW - 2, 4, textW, textH); - break; - - case right: - p1 = Point (component->getRight(), 0); - p2 = Point (component->getRight(), component->getY()); - textArea.setBounds (p1.getX() + 2, 4, textW, textH); - break; - - case top: - p1 = Point (0, component->getY()); - p2 = Point (component->getX(), component->getY()); - textArea.setBounds (4, p1.getY() - textH - 2, textW, textH); - break; - - case bottom: - p1 = Point (0, component->getBottom()); - p2 = Point (component->getX(), component->getBottom()); - textArea.setBounds (4, p1.getY() + 2, textW, textH); - break; - - default: - jassertfalse; - break; - } - - Rectangle bounds (Rectangle (p1, p2).expanded (2, 2).getUnion (textArea)); - bounds.setPosition (component->getParentComponent()->relativePositionToOtherComponent (getParentComponent(), bounds.getPosition())); - setBounds (bounds); - - lineEnd1 = component->getParentComponent()->relativePositionToOtherComponent (this, p1); - lineEnd2 = component->getParentComponent()->relativePositionToOtherComponent (this, p2); - textArea.setPosition (component->getParentComponent()->relativePositionToOtherComponent (this, textArea.getPosition())); - repaint(); - } - -private: - ComponentDocument& document; - ValueTree state; - Component* component; - Type type; - Font font; - Point lineEnd1, lineEnd2; - Rectangle textArea; -}; - -//============================================================================== -class ComponentEditor::Canvas : public Component, - public ValueTree::Listener, - public Timer -{ -public: - //============================================================================== - Canvas (ComponentEditor& editor_) - : editor (editor_), border (14), resizerThickness (4), - dragStartWidth (0), dragStartHeight (0) - { - setOpaque (true); - addAndMakeVisible (componentHolder = new Component()); - addAndMakeVisible (overlay = new OverlayComponent (*this)); - - setSize (500, 500); - - getDocument().getRoot().addListener (this); - updateComponents(); - } - - ~Canvas() - { - dragger = 0; - getDocument().getRoot().removeListener (this); - componentHolder->deleteAllChildren(); - deleteAllChildren(); - } - - //============================================================================== - ComponentEditor& getEditor() { return editor; } - ComponentDocument& getDocument() { return editor.getDocument(); } - ComponentDocument::SelectedItems& getSelection() { return selection; } - Component* getComponentHolder() const { return componentHolder; } - - void timerCallback() - { - stopTimer(); - - if (! Component::isMouseButtonDownAnywhere()) - getDocument().beginNewTransaction(); - } - - //============================================================================== - void paint (Graphics& g) - { - g.fillAll (Colours::white); - g.setColour (Colour::greyLevel (0.9f)); - - g.drawRect (getContentArea().expanded (resizerThickness, resizerThickness), resizerThickness); - - g.setFont (border.getBottom() - 5.0f); - g.setColour (Colours::grey); - g.drawText (String (componentHolder->getWidth()) + " x " + String (componentHolder->getHeight()), - 0, 0, jmax (getWidth() - border.getRight(), jmin (60, getWidth())), getHeight(), Justification::bottomRight, false); - - g.setFont (border.getTop() - 5.0f); - g.setColour (Colours::darkgrey); - - g.drawHorizontalLine (border.getTop() - 1, 2.0f, (float) getWidth() - border.getRight()); - g.drawVerticalLine (border.getLeft() - 1, 2.0f, (float) getHeight() - border.getBottom()); - drawXAxis (g, Rectangle (border.getLeft(), 0, componentHolder->getWidth(), border.getTop())); - drawYAxis (g, Rectangle (0, border.getTop(), border.getLeft(), componentHolder->getHeight())); - } - - void drawXAxis (Graphics& g, const Rectangle& r) - { - TickIterator ticks (0, r.getWidth(), 1.0, 10, 50); - float pos, tickLength; - String label; - - while (ticks.getNextTick (pos, tickLength, label)) - { - if (pos > 0) - { - g.drawVerticalLine (r.getX() + (int) pos, r.getBottom() - tickLength * r.getHeight(), (float) r.getBottom()); - g.drawSingleLineText (label, r.getX() + (int) pos + 2, (int) r.getBottom() - 6); - } - } - } - - void drawYAxis (Graphics& g, const Rectangle& r) - { - TickIterator ticks (0, r.getHeight(), 1.0, 10, 80); - float pos, tickLength; - String label; - - while (ticks.getNextTick (pos, tickLength, label)) - { - if (pos > 0) - { - g.drawHorizontalLine (r.getY() + (int) pos, r.getRight() - tickLength * r.getWidth(), (float) r.getRight()); - g.drawTextAsPath (label, AffineTransform::rotation (float_Pi / -2.0f) - .translated (r.getRight() - 6.0f, r.getY() + pos - 2.0f)); - } - } - } - - const Rectangle getContentArea() const - { - return border.subtractedFrom (getLocalBounds()); - } - - //============================================================================== - void resized() - { - componentHolder->setBounds (getContentArea()); - overlay->setBounds (componentHolder->getBounds()); - updateComponents(); - } - - void updateComponents() - { - ComponentDocument& doc = getDocument(); - setSize ((int) doc.getCanvasWidth().getValue() + border.getLeftAndRight(), - (int) doc.getCanvasHeight().getValue() + border.getTopAndBottom()); - - int i; - for (i = componentHolder->getNumChildComponents(); --i >= 0;) - { - Component* c = componentHolder->getChildComponent (i); - - if (! doc.containsComponent (c)) - { - selection.deselect (c->getComponentUID()); - delete c; - } - } - - const int num = doc.getNumComponents(); - for (i = 0; i < num; ++i) - { - const ValueTree v (doc.getComponent (i)); - Component* c = getComponentForState (v); - - if (c == 0) - { - c = doc.createComponent (i); - componentHolder->addAndMakeVisible (c); - } - - doc.updateComponent (c); - } - - startTimer (500); - } - - void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property) - { - updateComponents(); - } - - void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) - { - updateComponents(); - } - - void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) - { - } - - //============================================================================== - void getSelectedItemProperties (Array & props) - { - //xxx needs to handle multiple selections.. - - if (selection.getNumSelected() == 1) - { - Component* c = getComponentForUID (selection.getSelectedItem (0)); - jassert (c != 0); - getDocument().getComponentProperties (props, c); - } - } - - //============================================================================== - void mouseMove (const MouseEvent& e) - { - updateDragZone (e.getPosition()); - } - - void mouseDown (const MouseEvent& e) - { - updateDragZone (e.getPosition()); - dragStartWidth = getDocument().getCanvasWidth().getValue(); - dragStartHeight = getDocument().getCanvasHeight().getValue(); - showSizeGuides(); - } - - void mouseDrag (const MouseEvent& e) - { - if (dragZone.isDraggingRightEdge()) - getDocument().getCanvasWidth() = jmax (1, dragStartWidth + e.getDistanceFromDragStartX()); - - if (dragZone.isDraggingBottomEdge()) - getDocument().getCanvasHeight() = jmax (1, dragStartHeight + e.getDistanceFromDragStartY()); - } - - void mouseUp (const MouseEvent& e) - { - hideSizeGuides(); - updateDragZone (e.getPosition()); - } - - void updateDragZone (const Point& p) - { - ResizableBorderComponent::Zone newZone - = ResizableBorderComponent::Zone::fromPositionOnBorder (getContentArea().expanded (resizerThickness, resizerThickness), - BorderSize (0, 0, resizerThickness, resizerThickness), p); - - if (dragZone != newZone) - { - dragZone = newZone; - setMouseCursor (newZone.getMouseCursor()); - } - } - - void showSizeGuides() { overlay->showSizeGuides(); } - void hideSizeGuides() { overlay->hideSizeGuides(); } - - //============================================================================== - class DragOperation - { - public: - DragOperation (Canvas& canvas_, - const Array& items, - const Array& itemsToSnapTo, - const MouseEvent& e, - const ResizableBorderComponent::Zone& zone_) - : canvas (canvas_), - zone (zone_) - { - int i; - for (i = 0; i < items.size(); ++i) - { - jassert (items.getUnchecked(i) != 0); - const ValueTree v (getDocument().getComponentState (items.getUnchecked(i))); - draggedComponents.add (v); - const Rectangle floatPos (getComponentPosition (v)); - - if (zone.isDraggingWholeObject() || zone.isDraggingLeftEdge()) - verticalSnapPositions.add (SnapLine (floatPos.getX(), floatPos.getY(), floatPos.getBottom())); - - if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge())) - verticalSnapPositions.add (SnapLine (floatPos.getCentreX(), floatPos.getY(), floatPos.getBottom())); - - if (zone.isDraggingWholeObject() || zone.isDraggingRightEdge()) - verticalSnapPositions.add (SnapLine (floatPos.getRight(), floatPos.getY(), floatPos.getBottom())); - - if (zone.isDraggingWholeObject() || zone.isDraggingTopEdge()) - horizontalSnapPositions.add (SnapLine (floatPos.getY(), floatPos.getX(), floatPos.getRight())); - - if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge())) - horizontalSnapPositions.add (SnapLine (floatPos.getCentreY(), floatPos.getX(), floatPos.getRight())); - - if (zone.isDraggingWholeObject() || zone.isDraggingBottomEdge()) - horizontalSnapPositions.add (SnapLine (floatPos.getBottom(), floatPos.getX(), floatPos.getRight())); - } - - if (isDraggingLeftRight()) - { - verticalSnapTargets.add (SnapLine (0, 0, 10000.0f)); - verticalSnapTargets.add (SnapLine (getDocument().getCanvasWidth().getValue(), 0, 10000.0f)); - - if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge())) - verticalSnapTargets.add (SnapLine ((float) getDocument().getCanvasWidth().getValue() / 2.0f, 0, 10000.0f)); - } - - if (isDraggingUpDown()) - { - horizontalSnapTargets.add (SnapLine (0, 0, 10000.0f)); - horizontalSnapTargets.add (SnapLine (getDocument().getCanvasHeight().getValue(), 0, 10000.0f)); - - if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge())) - horizontalSnapTargets.add (SnapLine ((float) getDocument().getCanvasHeight().getValue() / 2.0f, 0, 10000.0f)); - } - - for (i = 0; i < itemsToSnapTo.size(); ++i) - { - jassert (itemsToSnapTo.getUnchecked(i) != 0); - const ValueTree v (getDocument().getComponentState (itemsToSnapTo.getUnchecked(i))); - const Rectangle floatPos (getComponentPosition (v)); - - if (isDraggingLeftRight()) - { - verticalSnapTargets.add (SnapLine (floatPos.getX(), floatPos.getY(), floatPos.getBottom())); - verticalSnapTargets.add (SnapLine (floatPos.getRight(), floatPos.getY(), floatPos.getBottom())); - } - - if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge())) - verticalSnapTargets.add (SnapLine (floatPos.getCentreX(), floatPos.getY(), floatPos.getBottom())); - - if (isDraggingUpDown()) - { - horizontalSnapTargets.add (SnapLine (floatPos.getY(), floatPos.getX(), floatPos.getRight())); - horizontalSnapTargets.add (SnapLine (floatPos.getBottom(), floatPos.getX(), floatPos.getRight())); - } - - if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge())) - horizontalSnapTargets.add (SnapLine (floatPos.getCentreY(), floatPos.getX(), floatPos.getRight())); - } - - mergeSnapLines (verticalSnapTargets); - mergeSnapLines (horizontalSnapTargets); - - getDocument().beginNewTransaction(); - } - - ~DragOperation() - { - getDocument().beginNewTransaction(); - } - - //============================================================================== - struct SnapLine - { - SnapLine() : position (0), start (0), end (0) {} - - SnapLine (const float position_, const float start_, const float end_) - : position (position_), start (start_), end (end_) - {} - - float position, start, end; - }; - - //============================================================================== - class AlignmentHintComponent : public Component - { - public: - AlignmentHintComponent (const SnapLine& line_, bool isVertical_, Component* parent) - : line (line_), isVertical (isVertical_) - { - const int extraEndLength = 5; - setAlwaysOnTop (true); - - if (isVertical) - setBounds (roundToInt (line.position), roundToInt (line.start) - extraEndLength, 1, roundToInt (line.end - line.start) + extraEndLength * 2); - else - setBounds (roundToInt (line.start) - extraEndLength, roundToInt (line.position), roundToInt (line.end - line.start) + extraEndLength * 2, 1); - - parent->addAndMakeVisible (this); - } - - void paint (Graphics& g) - { - g.fillAll (alignmentMarkerColour); - } - - private: - const SnapLine line; - const bool isVertical; - - AlignmentHintComponent (const AlignmentHintComponent&); - AlignmentHintComponent& operator= (const AlignmentHintComponent&); - }; - - //============================================================================== - void drag (const MouseEvent& e) - { - getDocument().getUndoManager()->undoCurrentTransactionOnly(); - - Point distance (e.getOffsetFromDragStart()); - if (! isDraggingLeftRight()) - distance = Point (0, distance.getY()); - - if (! isDraggingUpDown()) - distance = Point (distance.getX(), 0); - - snapGuides.clear(); - - performSnap (verticalSnapTargets, getVerticalSnapPositions (distance), true, distance); - performSnap (horizontalSnapTargets, getHorizontalSnapPositions (distance), false, distance); - - 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), distance, originalPositions.getReference(i))) - anyUpdated = true; - - if (! anyUpdated) - break; - - if (--n == 0) - { - jassertfalse; - break; - } - } - } - - bool dragItem (ValueTree& v, const Point& distance, const Rectangle& originalPos) - { - const Rectangle newBounds (zone.resizeRectangleBy (originalPos, distance)); - - RectangleCoordinates pr (getDocument().getCoordsFor (v)); - ScopedPointer markers (getDocument().createMarkerResolver (v)); - - pr.moveToAbsolute (newBounds, *markers); - - return getDocument().setCoordsFor (v, pr); - } - - //============================================================================== - private: - Canvas& canvas; - Array draggedComponents; - Array > originalPositions; - Array verticalSnapPositions, horizontalSnapPositions; - Array verticalSnapTargets, horizontalSnapTargets; - const ResizableBorderComponent::Zone zone; - OwnedArray snapGuides; - - ComponentDocument& getDocument() throw() { return canvas.getDocument(); } - - const Rectangle getComponentPosition (const ValueTree& state) - { - RectangleCoordinates relativePos (getDocument().getCoordsFor (state)); - ScopedPointer markers (getDocument().createMarkerResolver (state)); - const Rectangle intPos (relativePos.resolve (*markers)); - originalPositions.add (intPos); - - return Rectangle ((float) intPos.getX(), (float) intPos.getY(), - (float) intPos.getWidth(), (float) intPos.getHeight()); - } - - static void mergeSnapLines (Array & lines) - { - for (int i = lines.size(); --i > 0;) - { - SnapLine& s1 = lines.getReference(i); - - for (int j = i; --j >= 0;) - { - SnapLine& s2 = lines.getReference(j); - - if (s1.position == s2.position) - { - s2.start = jmin (s1.start, s2.start); - s2.end = jmax (s1.end, s2.end); - lines.remove (i); - } - } - } - } - - void performSnap (const Array& targets, const Array& sources, bool isVertical, Point& distance) - { - if (targets.size() == 0 || sources.size() == 0) - return; - - float best = std::numeric_limits::max(); - float absBest = fabsf (best); - Array lines; - - for (int i = 0; i < targets.size(); ++i) - { - const SnapLine& target = targets.getReference(i); - - for (int j = 0; j < sources.size(); ++j) - { - const SnapLine& source = sources.getReference(j); - const float diff = target.position - source.position; - const float absDiff = fabsf (diff); - - if (absDiff <= absBest) - { - if (absDiff < absBest) - lines.clearQuick(); - - lines.add (SnapLine (target.position, jmin (target.start, source.start), jmax (target.end, source.end))); - best = diff; - absBest = absDiff; - } - } - } - - jassert (absBest < std::numeric_limits::max()); - - if (absBest < snapDistance) - { - distance += isVertical ? Point (roundToInt (best), 0) : Point (0, roundToInt (best)); - - for (int i = lines.size(); --i >= 0;) - if (lines.getReference(i).position != 0) - snapGuides.add (new AlignmentHintComponent (lines.getReference(i), isVertical, canvas.overlay)); - } - } - - const Array getVerticalSnapPositions (const Point& distance) const - { - Array p (verticalSnapPositions); - for (int i = p.size(); --i >= 0;) - { - SnapLine& s = p.getReference(i); - s.position += distance.getX(); - s.start += distance.getY(); - s.end += distance.getY(); - } - - return p; - } - - const Array getHorizontalSnapPositions (const Point& distance) const - { - Array p (horizontalSnapPositions); - for (int i = p.size(); --i >= 0;) - { - SnapLine& s = p.getReference(i); - s.position += distance.getY(); - s.start += distance.getX(); - s.end += distance.getX(); - } - - return p; - } - - bool isDraggingLeftRight() const { return zone.isDraggingWholeObject() || zone.isDraggingLeftEdge() || zone.isDraggingRightEdge(); } - bool isDraggingUpDown() const { return zone.isDraggingWholeObject() || zone.isDraggingTopEdge() || zone.isDraggingBottomEdge(); } - - DragOperation (const DragOperation&); - DragOperation& operator= (const DragOperation&); - }; - - //============================================================================== - void beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone) - { - dragger = new DragOperation (*this, getSelectedComps(), getUnselectedComps(), e, zone); - } - - void continueDrag (const MouseEvent& e) - { - if (dragger != 0) - dragger->drag (e); - } - - void endDrag (const MouseEvent& e) - { - if (dragger != 0) - { - dragger->drag (e); - dragger = 0; - } - } - -private: - ComponentEditor& editor; - const BorderSize border; - const int resizerThickness; - ScopedPointer dragger; - ResizableBorderComponent::Zone dragZone; - int dragStartWidth, dragStartHeight; - - //============================================================================== - class ComponentResizeFrame : public Component, - public ComponentListener - { - public: - ComponentResizeFrame (Canvas& canvas_, - Component* componentToAttachTo) - : canvas (canvas_), - component (componentToAttachTo), - borderThickness (4) - { - componentMovedOrResized (*componentToAttachTo, true, true); - componentToAttachTo->addComponentListener (this); - } - - ~ComponentResizeFrame() - { - if (component != 0) - component->removeComponentListener (this); - } - - void paint (Graphics& g) - { - g.setColour (resizableBorderColour); - g.drawRect (0, 0, getWidth(), getHeight(), borderThickness); - } - - void mouseEnter (const MouseEvent& e) { updateDragZone (e.getPosition()); } - void mouseExit (const MouseEvent& e) { updateDragZone (e.getPosition()); } - void mouseMove (const MouseEvent& e) { updateDragZone (e.getPosition()); } - - void mouseDown (const MouseEvent& e) - { - jassert (component != 0); - - if (component != 0) - { - updateDragZone (e.getPosition()); - canvas.beginDrag (e, dragZone); - canvas.showSizeGuides(); - } - } - - void mouseDrag (const MouseEvent& e) - { - if (component != 0) - canvas.continueDrag (e); - } - - void mouseUp (const MouseEvent& e) - { - canvas.hideSizeGuides(); - - if (component != 0) - canvas.endDrag (e); - - updateDragZone (e.getPosition()); - } - - void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) - { - if (component != 0) - setBounds (component->getBounds().expanded (borderThickness, borderThickness)); - } - - bool hitTest (int x, int y) - { - return ! getCentreArea().contains (x, y); - } - - 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, canvas, SizeGuideComponent::left)); - sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, canvas, SizeGuideComponent::right)); - sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, canvas, SizeGuideComponent::top)); - sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, canvas, SizeGuideComponent::bottom)); - } - } - - void hideSizeGuides() - { - sizeGuides.clear(); - } - - private: - Canvas& canvas; - Component::SafePointer component; - ResizableBorderComponent::Zone dragZone; - const int borderThickness; - OwnedArray sizeGuides; - - const Rectangle getCentreArea() const - { - return getLocalBounds().reduced (borderThickness, borderThickness); - } - - void updateDragZone (const Point& p) - { - ResizableBorderComponent::Zone newZone - = ResizableBorderComponent::Zone::fromPositionOnBorder (getLocalBounds(), - BorderSize (borderThickness), p); - - if (dragZone != newZone) - { - dragZone = newZone; - setMouseCursor (newZone.getMouseCursor()); - } - } - }; - - //============================================================================== - class OverlayComponent : public Component, - public LassoSource , - public ChangeListener - { - public: - OverlayComponent (Canvas& canvas_) - : canvas (canvas_) - { - setAlwaysOnTop (true); - setWantsKeyboardFocus (true); - canvas.getSelection().addChangeListener (this); - } - - ~OverlayComponent() - { - canvas.getSelection().removeChangeListener (this); - - lasso = 0; - deleteAllChildren(); - } - - //============================================================================== - void mouseDown (const MouseEvent& e) - { - lasso = 0; - mouseDownCompUID = 0; - isDraggingClickedComp = false; - - if (e.mods.isPopupMenu()) - { - PopupMenu m; - canvas.getDocument().addNewComponentMenuItems (m); - - const int r = m.show(); - canvas.getDocument().performNewComponentMenuItem (r); - } - else - { - Component* underMouse = 0; - - for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;) - { - Component* const c = canvas.getComponentHolder()->getChildComponent(i); - if (c->getBounds().contains (e.getPosition())) - { - underMouse = c; - break; - } - } - - if (underMouse == 0 || e.mods.isAltDown()) - { - addAndMakeVisible (lasso = new LassoComponent ()); - lasso->beginLasso (e, this); - } - else - { - mouseDownCompUID = underMouse->getComponentUID(); - mouseDownResult = canvas.getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods); - - updateSelectedComponentResizeFrames(); - hideSizeGuides(); - showSizeGuides(); - } - } - } - - void mouseDrag (const MouseEvent& e) - { - if (lasso != 0) - { - lasso->dragLasso (e); - } - else if (mouseDownCompUID != 0 && (! e.mouseWasClicked()) && (! e.mods.isPopupMenu())) - { - if (! isDraggingClickedComp) - { - isDraggingClickedComp = true; - canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); - canvas.beginDrag (e, ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); - } - - canvas.continueDrag (e); - showSizeGuides(); - } - } - - void mouseUp (const MouseEvent& e) - { - hideSizeGuides(); - - if (lasso != 0) - { - lasso->endLasso(); - lasso = 0; - - if (e.mouseWasClicked()) - canvas.getSelection().deselectAll(); - } - else if (! e.mods.isPopupMenu()) - { - if (! isDraggingClickedComp) - canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult); - } - - canvas.endDrag (e); - } - - void findLassoItemsInArea (Array & itemsFound, int x, int y, int width, int height) - { - const Rectangle lassoArea (x, y, width, height); - - for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;) - { - Component* c = canvas.getComponentHolder()->getChildComponent(i); - if (c->getBounds().intersects (lassoArea)) - itemsFound.add (c->getComponentUID()); - } - } - - ComponentDocument::SelectedItems& getLassoSelection() { return canvas.getSelection(); } - - void changeListenerCallback (void*) - { - updateSelectedComponentResizeFrames(); - } - - void modifierKeysChanged (const ModifierKeys&) - { - 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; - bool mouseDownResult, isDraggingClickedComp; - uint32 mouseDownCompUID; - - ComponentResizeFrame* getSelectorFrameFor (Component* c) const - { - for (int i = getNumChildComponents(); --i >= 0;) - { - ComponentResizeFrame* resizer = dynamic_cast (getChildComponent(i)); - if (resizer != 0 && resizer->getTargetComponentUID() == c->getComponentUID()) - return resizer; - } - - return 0; - } - - void updateSelectedComponentResizeFrames() - { - ComponentDocument::SelectedItems& selection = canvas.getSelection(); - - int i; - for (i = getNumChildComponents(); --i >= 0;) - { - ComponentResizeFrame* resizer = dynamic_cast (getChildComponent(i)); - - if (resizer != 0 && ! selection.isSelected (resizer->getTargetComponentUID())) - delete resizer; - } - - for (i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;) - { - Component* c = canvas.getComponentHolder()->getChildComponent(i); - - if (c != this && selection.isSelected (c->getComponentUID()) && getSelectorFrameFor (c) == 0) - addAndMakeVisible (new ComponentResizeFrame (canvas, c)); - } - } - }; - - //============================================================================== - Component* componentHolder; - OverlayComponent* overlay; - ComponentDocument::SelectedItems selection; - - Component* getComponentForUID (const uint32 uid) const - { - for (int i = componentHolder->getNumChildComponents(); --i >= 0;) - if (componentHolder->getChildComponent (i)->getComponentUID() == uid) - return componentHolder->getChildComponent (i); - - return 0; - } - - Component* getComponentForState (const ValueTree& state) - { - ComponentDocument& doc = getDocument(); - - for (int i = componentHolder->getNumChildComponents(); --i >= 0;) - { - Component* const c = componentHolder->getChildComponent (i); - - if (doc.isStateForComponent (state, c)) - return c; - } - - return 0; - } - - const Array getSelectedComps() const - { - Array comps; - - for (int i = 0; i < selection.getNumSelected(); ++i) - { - Component* c = getComponentForUID (selection.getSelectedItem (i)); - jassert (c != 0); - if (c != 0) - comps.add (c); - } - - return comps; - } - - const Array getUnselectedComps() const - { - Array comps; - - for (int i = componentHolder->getNumChildComponents(); --i >= 0;) - if (! selection.isSelected (componentHolder->getChildComponent(i)->getComponentUID())) - comps.add (componentHolder->getChildComponent(i)); - - return comps; - } -}; - //============================================================================== class ComponentEditor::ClassInfoHolder : public Component { @@ -1132,7 +74,7 @@ public: void createCanvas() { - viewport->setViewedComponent (new Canvas (editor)); + viewport->setViewedComponent (new ComponentEditorCanvas (editor)); addAndMakeVisible (infoPanel = new InfoPanel (editor)); } @@ -1281,9 +223,9 @@ void ComponentEditor::resized() tabs->setBounds (getLocalBounds()); } -ComponentEditor::Canvas* ComponentEditor::getCanvas() const +ComponentEditorCanvas* ComponentEditor::getCanvas() const { - return dynamic_cast (getViewport()->getViewedComponent()); + return dynamic_cast (getViewport()->getViewedComponent()); } Viewport* ComponentEditor::getViewport() const diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.h b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.h index 8c8074a870..510201c55f 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.h +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.h @@ -26,8 +26,7 @@ #ifndef __JUCE_COMPONENTEDITOR_H_6CAE6B7E__ #define __JUCE_COMPONENTEDITOR_H_6CAE6B7E__ -#include "../../model/jucer_ComponentDocument.h" -#include "../jucer_DocumentEditorComponent.h" +#include "jucer_ComponentEditorCanvas.h" //============================================================================== @@ -55,8 +54,7 @@ public: Viewport* getViewport() const; - class Canvas; - Canvas* getCanvas() const; + ComponentEditorCanvas* getCanvas() const; private: class ClassInfoHolder; @@ -72,6 +70,9 @@ private: LayoutEditorHolder* layoutEditorHolder; BackgroundEditorHolder* backgroundEditorHolder; CodeEditorHolder* codeEditorHolder; + + ComponentEditor (const ComponentEditor&); + ComponentEditor& operator= (const ComponentEditor&); }; diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.cpp b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.cpp new file mode 100644 index 0000000000..febb5001df --- /dev/null +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.cpp @@ -0,0 +1,1018 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-9 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#include "../../jucer_Headers.h" +#include "jucer_ComponentEditorCanvas.h" +#include "jucer_ComponentEditor.h" + +const float snapDistance = 8.0f; +static const Colour alignmentMarkerColour (0x77ff0000); +static const Colour resizableBorderColour (0x7066aaff); + +#include "jucer_ComponentDragOperation.h" + + +//============================================================================== +class ComponentEditorCanvas::ComponentResizeFrame : public ComponentEditorCanvas::OverlayItemComponent, + public ComponentListener +{ +public: + ComponentResizeFrame (ComponentEditorCanvas& canvas_, Component* componentToAttachTo) + : OverlayItemComponent (canvas_), + component (componentToAttachTo), + borderThickness (4) + { + component->addComponentListener (this); + } + + ~ComponentResizeFrame() + { + if (component != 0) + component->removeComponentListener (this); + } + + void paint (Graphics& g) + { + g.setColour (resizableBorderColour); + g.drawRect (0, 0, getWidth(), getHeight(), borderThickness); + } + + void componentMovedOrResized (Component&, bool, bool) { updatePosition(); } + + void mouseEnter (const MouseEvent& e) { updateDragZone (e.getPosition()); } + void mouseExit (const MouseEvent& e) { updateDragZone (e.getPosition()); } + void mouseMove (const MouseEvent& e) { updateDragZone (e.getPosition()); } + + void mouseDown (const MouseEvent& e) + { + jassert (component != 0); + + if (component != 0) + { + updateDragZone (e.getPosition()); + canvas.beginDrag (e, dragZone); + canvas.showSizeGuides(); + } + } + + void mouseDrag (const MouseEvent& e) + { + if (component != 0) + canvas.continueDrag (e); + } + + void mouseUp (const MouseEvent& e) + { + canvas.hideSizeGuides(); + + if (component != 0) + canvas.endDrag (e); + + updateDragZone (e.getPosition()); + } + + bool hitTest (int x, int y) + { + return ! getCentreArea().contains (x, y); + } + + void updatePosition() + { + if (component != 0) + setBoundsInTargetSpace (component->getBounds().expanded (borderThickness, borderThickness)); + } + + uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); } + + //============================================================================== + class SizeGuideComponent : public OverlayItemComponent, + public ComponentListener + { + public: + enum Type { left, right, top, bottom }; + + //============================================================================== + SizeGuideComponent (ComponentEditorCanvas& canvas_, const ValueTree& state_, Component* component_, Type type_) + : OverlayItemComponent (canvas_), state (state_), component (component_), type (type_), + font (10.0f) + { + component->addComponentListener (this); + + setAlwaysOnTop (true); + canvas.addAndMakeVisible (this); + updatePosition(); + } + + ~SizeGuideComponent() + { + if (component != 0) + component->removeComponentListener (this); + } + + //============================================================================== + void paint (Graphics& g) + { + const float dashes[] = { 4.0f, 3.0f }; + + g.setColour (resizableBorderColour); + g.drawDashedLine (lineEnd1.getX() + 0.5f, lineEnd1.getY() + 0.5f, + lineEnd2.getX() + 0.5f, lineEnd2.getY() + 0.5f, + dashes, 2, 1.0f); + + 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; + } + + //============================================================================== + void updatePosition() + { + RectangleCoordinates coords (getDocument().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()); + + int textW = (int) font.getStringWidth (getName()); + int textH = (int) font.getHeight(); + + Point p1, p2; + + switch (type) + { + case left: + p1 = Point (component->getX(), 0); + p2 = Point (component->getX(), component->getY()); + textArea.setBounds (p1.getX() - textW - 2, 4, textW, textH); + break; + + case right: + p1 = Point (component->getRight(), 0); + p2 = Point (component->getRight(), component->getY()); + textArea.setBounds (p1.getX() + 2, 4, textW, textH); + break; + + case top: + p1 = Point (0, component->getY()); + p2 = Point (component->getX(), component->getY()); + textArea.setBounds (4, p1.getY() - textH - 2, textW, textH); + break; + + case bottom: + p1 = Point (0, component->getBottom()); + p2 = Point (component->getX(), component->getBottom()); + textArea.setBounds (4, p1.getY() + 2, textW, textH); + break; + + default: + jassertfalse; + break; + } + + setBoundsInTargetSpace (Rectangle (p1, p2).expanded (2, 2).getUnion (textArea)); + + lineEnd1 = component->getParentComponent()->relativePositionToOtherComponent (this, p1); + lineEnd2 = component->getParentComponent()->relativePositionToOtherComponent (this, p2); + textArea.setPosition (component->getParentComponent()->relativePositionToOtherComponent (this, textArea.getPosition())); + repaint(); + } + + private: + ValueTree state; + Component* component; + Type type; + Font font; + Point lineEnd1, lineEnd2; + Rectangle textArea; + }; + + void showSizeGuides() + { + if (sizeGuides.size() == 0) + { + const ValueTree v (getDocument().getComponentState (component)); + sizeGuides.add (new SizeGuideComponent (canvas, v, component, SizeGuideComponent::left)); + sizeGuides.add (new SizeGuideComponent (canvas, v, component, SizeGuideComponent::right)); + sizeGuides.add (new SizeGuideComponent (canvas, v, component, SizeGuideComponent::top)); + sizeGuides.add (new SizeGuideComponent (canvas, v, component, SizeGuideComponent::bottom)); + } + } + + void hideSizeGuides() + { + sizeGuides.clear(); + } + +private: + Component::SafePointer component; + ResizableBorderComponent::Zone dragZone; + const int borderThickness; + OwnedArray sizeGuides; + + const Rectangle getCentreArea() const + { + return getLocalBounds().reduced (borderThickness, borderThickness); + } + + void updateDragZone (const Point& p) + { + ResizableBorderComponent::Zone newZone + = ResizableBorderComponent::Zone::fromPositionOnBorder (getLocalBounds(), + BorderSize (borderThickness), p); + + if (dragZone != newZone) + { + dragZone = newZone; + setMouseCursor (newZone.getMouseCursor()); + } + } +}; + +//============================================================================== +class ComponentEditorCanvas::MarkerComponent : public ComponentEditorCanvas::OverlayItemComponent, + public ValueTree::Listener +{ +public: + MarkerComponent (ComponentEditorCanvas& canvas_, const ValueTree& marker_, bool isX_, int headSize_) + : OverlayItemComponent (canvas_), marker (marker_), isX (isX_), headSize (headSize_ - 2), + dragStartPos (0), isDragging (false) + { + marker.addListener (this); + } + + ~MarkerComponent() + { + marker.removeListener (this); + } + + void paint (Graphics& g) + { + g.setColour (Colours::darkgreen); + g.fillPath (path); + } + + void updatePosition() + { + ComponentDocument& doc = getDocument(); + Coordinate coord (doc.getMarkerList (isX).getCoordinate (marker)); + const int pos = roundToInt (coord.resolve (doc.getMarkerList (isX))); + const int width = 10; + + if (isX) + setBoundsInTargetSpace (Rectangle (pos - width, -headSize, width * 2, getParentHeight())); + else + setBoundsInTargetSpace (Rectangle (-headSize, pos - width, getParentWidth(), width * 2)); + } + + bool hitTest (int x, int y) + { + return (isX ? y : x) < headSize; + } + + void resized() + { + const float lineThickness = 1.0f; + path.clear(); + + if (isX) + { + const float centre = getWidth() / 2 + 0.5f; + path.addLineSegment (centre, 2.0f, centre, getHeight() + 1.0f, lineThickness); + path.addTriangle (1.0f, 0.0f, centre * 2.0f - 1.0f, 0.0f, centre, headSize + 1.0f); + } + else + { + const float centre = getHeight() / 2 + 0.5f; + path.addLineSegment (2.0f, centre, getWidth() + 1.0f, centre, lineThickness); + path.addTriangle (0.0f, centre * 2.0f - 1.0f, 0.0f, 1.0f, headSize + 1.0f, centre); + } + } + + void mouseDown (const MouseEvent& e) + { + if (e.mods.isPopupMenu()) + { + isDragging = false; + } + else + { + isDragging = true; + getDocument().beginNewTransaction(); + + ComponentDocument& doc = getDocument(); + Coordinate coord (doc.getMarkerList(isX).getCoordinate (marker)); + dragStartPos = coord.resolve (doc.getMarkerList (isX)); + } + } + + void mouseDrag (const MouseEvent& e) + { + if (isDragging) + { + ComponentDocument& doc = getDocument(); + doc.getUndoManager()->undoCurrentTransactionOnly(); + + Coordinate coord (doc.getMarkerList (isX).getCoordinate (marker)); + coord.moveToAbsolute (jmax (0.0, dragStartPos + (isX ? e.getDistanceFromDragStartX() + : e.getDistanceFromDragStartY())), + doc.getMarkerList (isX)); + doc.getMarkerList(isX).setCoordinate (marker, coord); + } + } + + void mouseUp (const MouseEvent& e) + { + getDocument().beginNewTransaction(); + } + + void mouseEnter (const MouseEvent& e) + { + } + + void mouseExit (const MouseEvent& e) + { + } + + void valueTreePropertyChanged (ValueTree&, const var::identifier&) { updatePosition(); } + void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) {} + void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {} + + ValueTree marker; + const bool isX; + +private: + const int headSize; + Path path; + double dragStartPos; + bool isDragging; +}; + + +//============================================================================== +class ComponentEditorCanvas::OverlayComponent : public Component, + public LassoSource , + public ChangeListener, + public ValueTree::Listener +{ +public: + OverlayComponent (ComponentEditorCanvas& canvas_) + : canvas (canvas_) + { + setWantsKeyboardFocus (true); + canvas.getSelection().addChangeListener (this); + + markerRootX = getDocument().getMarkerListX().getGroup(); + markerRootY = getDocument().getMarkerListY().getGroup(); + markerRootX.addListener (this); + markerRootY.addListener (this); + } + + ~OverlayComponent() + { + markerRootX.removeListener (this); + markerRootY.removeListener (this); + + canvas.getSelection().removeChangeListener (this); + + lasso = 0; + deleteAllChildren(); + } + + //============================================================================== + void mouseDown (const MouseEvent& e) + { + lasso = 0; + mouseDownCompUID = 0; + isDraggingClickedComp = false; + + if (e.mods.isPopupMenu()) + { + PopupMenu m; + getDocument().addNewComponentMenuItems (m); + + const int r = m.show(); + getDocument().performNewComponentMenuItem (r); + } + else + { + Component* underMouse = 0; + + for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;) + { + Component* const c = canvas.getComponentHolder()->getChildComponent(i); + if (c->getBounds().contains (e.getPosition())) + { + underMouse = c; + break; + } + } + + if (underMouse == 0 || e.mods.isAltDown()) + { + addAndMakeVisible (lasso = new LassoComponent ()); + lasso->beginLasso (e, this); + } + else + { + mouseDownCompUID = underMouse->getComponentUID(); + mouseDownResult = canvas.getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods); + + updateResizeFrames(); + hideSizeGuides(); + showSizeGuides(); + } + } + } + + void mouseDrag (const MouseEvent& e) + { + if (lasso != 0) + { + lasso->dragLasso (e); + } + else if (mouseDownCompUID != 0 && (! e.mouseWasClicked()) && (! e.mods.isPopupMenu())) + { + if (! isDraggingClickedComp) + { + isDraggingClickedComp = true; + canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); + canvas.beginDrag (e, ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); + } + + canvas.continueDrag (e); + showSizeGuides(); + } + } + + void mouseUp (const MouseEvent& e) + { + hideSizeGuides(); + + if (lasso != 0) + { + lasso->endLasso(); + lasso = 0; + + if (e.mouseWasClicked()) + canvas.getSelection().deselectAll(); + } + else if (! e.mods.isPopupMenu()) + { + if (! isDraggingClickedComp) + canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult); + } + + canvas.endDrag (e); + } + + void mouseDoubleClick (const MouseEvent& e) + { + const BorderSize& border = canvas.border; + const Rectangle xAxis (border.getLeft(), 0, getWidth() - border.getLeftAndRight(), border.getTop()); + const Rectangle yAxis (0, border.getTop(), border.getLeft(), getHeight() - border.getTopAndBottom()); + + if (xAxis.contains (e.x, e.y)) + { + getDocument().getMarkerListX().createMarker ("Marker", e.x - xAxis.getX()); + } + else if (yAxis.contains (e.x, e.y)) + { + getDocument().getMarkerListY().createMarker ("Marker", e.y - yAxis.getY()); + } + } + + void findLassoItemsInArea (Array & itemsFound, int x, int y, int width, int height) + { + const Rectangle lassoArea (x, y, width, height); + + for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;) + { + Component* c = canvas.getComponentHolder()->getChildComponent(i); + if (c->getBounds().intersects (lassoArea)) + itemsFound.add (c->getComponentUID()); + } + } + + ComponentDocument::SelectedItems& getLassoSelection() { return canvas.getSelection(); } + + void resized() + { + updateMarkers(); + } + + void changeListenerCallback (void*) + { + updateResizeFrames(); + } + + void modifierKeysChanged (const ModifierKeys&) + { + 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(); + } + } + + void valueTreePropertyChanged (ValueTree&, const var::identifier&) { updateMarkers(); } + void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { updateMarkers(); } + void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {} + +private: + //============================================================================== + ComponentEditorCanvas& canvas; + ValueTree markerRootX, markerRootY; + ScopedPointer > lasso; + bool mouseDownResult, isDraggingClickedComp; + uint32 mouseDownCompUID; + + ComponentDocument& getDocument() { return canvas.getDocument(); } + + Component* getComponentWithUID (const int uid) const + { + for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;) + { + Component* c = canvas.getComponentHolder()->getChildComponent(i); + + if (c->getComponentUID() == uid) + return c; + } + + return 0; + } + + void updateResizeFrames() + { + ComponentDocument::SelectedItems& selection = canvas.getSelection(); + + Array requiredIds; + int i; + for (i = selection.getNumSelected(); --i >= 0;) + requiredIds.add (selection.getSelectedItem(i)); + + for (i = getNumChildComponents(); --i >= 0;) + { + ComponentResizeFrame* resizer = dynamic_cast (getChildComponent(i)); + + if (resizer != 0) + { + if (selection.isSelected (resizer->getTargetComponentUID())) + requiredIds.removeValue (resizer->getTargetComponentUID()); + else + delete resizer; + } + } + + for (i = requiredIds.size(); --i >= 0;) + { + Component* c = getComponentWithUID (requiredIds.getUnchecked(i)); + + if (c != 0) + { + ComponentResizeFrame* frame = new ComponentResizeFrame (canvas, c); + addAndMakeVisible (frame); + frame->updatePosition(); + } + } + } + + void updateMarkers (bool isX) + { + ComponentDocument& doc = getDocument(); + Array requiredMarkers; + + int i; + for (i = doc.getMarkerList (isX).size(); --i >= 0;) + requiredMarkers.add (doc.getMarkerList (isX).getMarker (i)); + + for (i = getNumChildComponents(); --i >= 0;) + { + MarkerComponent* marker = dynamic_cast (getChildComponent(i)); + + if (marker != 0 && marker->isX == isX) + { + if (requiredMarkers.contains (marker->marker)) + { + marker->updatePosition(); + requiredMarkers.removeValue (marker->marker); + } + else + { + delete marker; + } + } + } + + for (i = requiredMarkers.size(); --i >= 0;) + { + MarkerComponent* marker = new MarkerComponent (canvas, requiredMarkers.getReference(i), + isX, isX ? canvas.border.getTop() + : canvas.border.getLeft()); + addAndMakeVisible (marker); + marker->updatePosition(); + } + } + + void updateMarkers() + { + updateMarkers (true); + updateMarkers (false); + } +}; + +//============================================================================== +class ComponentEditorCanvas::ComponentHolder : public Component +{ +public: + ComponentHolder() {} + ~ComponentHolder() {} + + void updateComponents (ComponentDocument& doc, ComponentDocument::SelectedItems& selection) + { + int i; + for (i = getNumChildComponents(); --i >= 0;) + { + Component* c = getChildComponent (i); + + if (! doc.containsComponent (c)) + { + selection.deselect (c->getComponentUID()); + delete c; + } + } + + const int num = doc.getNumComponents(); + for (i = 0; i < num; ++i) + { + const ValueTree v (doc.getComponent (i)); + Component* c = getComponentForState (doc, v); + + if (c == 0) + { + c = doc.createComponent (i); + addAndMakeVisible (c); + } + + doc.updateComponent (c); + } + } + + Component* getComponentForState (ComponentDocument& doc, const ValueTree& state) + { + for (int i = getNumChildComponents(); --i >= 0;) + { + Component* const c = getChildComponent (i); + + if (doc.isStateForComponent (state, c)) + return c; + } + + return 0; + } +}; + +//============================================================================== +class ComponentEditorCanvas::WholeComponentResizer : public Component +{ +public: + WholeComponentResizer (ComponentEditorCanvas& canvas_) + : canvas (canvas_), dragStartWidth (0), dragStartHeight (0), resizerThickness (4) + { + } + + ~WholeComponentResizer() + { + } + + void paint (Graphics& g) + { + const Rectangle content (getContentArea()); + + g.setColour (Colour::greyLevel (0.7f).withAlpha (0.4f)); + g.drawRect (content.expanded (resizerThickness, resizerThickness), resizerThickness); + + const int bottomGap = getHeight() - content.getBottom(); + g.setFont (bottomGap - 5.0f); + + g.setColour (Colours::grey); + g.drawText (String (content.getWidth()) + " x " + String (content.getHeight()), + 0, 0, jmax (content.getRight(), jmin (60, getWidth())), getHeight(), Justification::bottomRight, false); + } + + void mouseMove (const MouseEvent& e) + { + updateDragZone (e.getPosition()); + } + + void mouseDown (const MouseEvent& e) + { + updateDragZone (e.getPosition()); + dragStartWidth = getDocument().getCanvasWidth().getValue(); + dragStartHeight = getDocument().getCanvasHeight().getValue(); + canvas.showSizeGuides(); + } + + void mouseDrag (const MouseEvent& e) + { + if (dragZone.isDraggingRightEdge()) + getDocument().getCanvasWidth() = jmax (1, dragStartWidth + e.getDistanceFromDragStartX()); + + if (dragZone.isDraggingBottomEdge()) + getDocument().getCanvasHeight() = jmax (1, dragStartHeight + e.getDistanceFromDragStartY()); + } + + void mouseUp (const MouseEvent& e) + { + canvas.hideSizeGuides(); + updateDragZone (e.getPosition()); + } + + void updateDragZone (const Point& p) + { + ResizableBorderComponent::Zone newZone + = ResizableBorderComponent::Zone::fromPositionOnBorder (getContentArea().expanded (resizerThickness, resizerThickness), + BorderSize (0, 0, resizerThickness, resizerThickness), p); + + if (dragZone != newZone) + { + dragZone = newZone; + setMouseCursor (newZone.getMouseCursor()); + } + } + + bool hitTest (int x, int y) + { + const Rectangle content (getContentArea()); + + return (x >= content.getRight() || y >= content.getBottom()) + && (! content.contains (x, y)) + && content.expanded (resizerThickness, resizerThickness).contains (x, y); + } + +private: + ComponentEditorCanvas& canvas; + ResizableBorderComponent::Zone dragZone; + int dragStartWidth, dragStartHeight; + const int resizerThickness; + + ComponentDocument& getDocument() { return canvas.getDocument(); } + const Rectangle getContentArea() const { return canvas.getContentArea(); } +}; + + +//============================================================================== +ComponentEditorCanvas::ComponentEditorCanvas (ComponentEditor& editor_) + : editor (editor_), border (14) +{ + setOpaque (true); + addAndMakeVisible (componentHolder = new ComponentHolder()); + addAndMakeVisible (overlay = new OverlayComponent (*this)); + overlay->addAndMakeVisible (resizeFrame = new WholeComponentResizer (*this)); + + setSize (500, 500); + + getDocument().getRoot().addListener (this); + updateComponents(); +} + +ComponentEditorCanvas::~ComponentEditorCanvas() +{ + dragger = 0; + getDocument().getRoot().removeListener (this); + componentHolder->deleteAllChildren(); + deleteAllChildren(); +} + +//============================================================================== +ComponentEditor& ComponentEditorCanvas::getEditor() { return editor; } +ComponentDocument& ComponentEditorCanvas::getDocument() { return editor.getDocument(); } +ComponentDocument::SelectedItems& ComponentEditorCanvas::getSelection() { return selection; } +Component* ComponentEditorCanvas::getComponentHolder() const { return componentHolder; } + +void ComponentEditorCanvas::timerCallback() +{ + stopTimer(); + + if (! Component::isMouseButtonDownAnywhere()) + getDocument().beginNewTransaction(); +} + +//============================================================================== +void ComponentEditorCanvas::paint (Graphics& g) +{ + g.fillAll (Colours::white); + + g.setFont (border.getTop() - 5.0f); + g.setColour (Colours::darkgrey); + + g.drawHorizontalLine (border.getTop() - 1, 2.0f, (float) getWidth() - border.getRight()); + g.drawVerticalLine (border.getLeft() - 1, 2.0f, (float) getHeight() - border.getBottom()); + drawXAxis (g, Rectangle (border.getLeft(), 0, componentHolder->getWidth(), border.getTop())); + drawYAxis (g, Rectangle (0, border.getTop(), border.getLeft(), componentHolder->getHeight())); +} + +void ComponentEditorCanvas::drawXAxis (Graphics& g, const Rectangle& r) +{ + TickIterator ticks (0, r.getWidth(), 1.0, 10, 50); + float pos, tickLength; + String label; + + while (ticks.getNextTick (pos, tickLength, label)) + { + if (pos > 0) + { + g.drawVerticalLine (r.getX() + (int) pos, r.getBottom() - tickLength * r.getHeight(), (float) r.getBottom()); + g.drawSingleLineText (label, r.getX() + (int) pos + 2, (int) r.getBottom() - 6); + } + } +} + +void ComponentEditorCanvas::drawYAxis (Graphics& g, const Rectangle& r) +{ + TickIterator ticks (0, r.getHeight(), 1.0, 10, 80); + float pos, tickLength; + String label; + + while (ticks.getNextTick (pos, tickLength, label)) + { + if (pos > 0) + { + g.drawHorizontalLine (r.getY() + (int) pos, r.getRight() - tickLength * r.getWidth(), (float) r.getRight()); + g.drawTextAsPath (label, AffineTransform::rotation (float_Pi / -2.0f) + .translated (r.getRight() - 6.0f, r.getY() + pos - 2.0f)); + } + } +} + +const Rectangle ComponentEditorCanvas::getContentArea() const +{ + return border.subtractedFrom (getLocalBounds()); +} + +//============================================================================== +void ComponentEditorCanvas::resized() +{ + componentHolder->setBounds (getContentArea()); + overlay->setBounds (getLocalBounds()); + resizeFrame->setBounds (getLocalBounds()); + updateComponents(); +} + +void ComponentEditorCanvas::updateComponents() +{ + setSize ((int) getDocument().getCanvasWidth().getValue() + border.getLeftAndRight(), + (int) getDocument().getCanvasHeight().getValue() + border.getTopAndBottom()); + + componentHolder->updateComponents (getDocument(), selection); + startTimer (500); +} + +//============================================================================== +void ComponentEditorCanvas::getSelectedItemProperties (Array & props) +{ + //xxx needs to handle multiple selections.. + + if (selection.getNumSelected() == 1) + { + Component* c = getComponentForUID (selection.getSelectedItem (0)); + jassert (c != 0); + getDocument().getComponentProperties (props, c); + } +} + +//============================================================================== +void ComponentEditorCanvas::showSizeGuides() { overlay->showSizeGuides(); } +void ComponentEditorCanvas::hideSizeGuides() { overlay->hideSizeGuides(); } + + +//============================================================================== +Component* ComponentEditorCanvas::getComponentForUID (const uint32 uid) const +{ + for (int i = componentHolder->getNumChildComponents(); --i >= 0;) + if (componentHolder->getChildComponent (i)->getComponentUID() == uid) + return componentHolder->getChildComponent (i); + + return 0; +} + +const Array ComponentEditorCanvas::getSelectedComps() const +{ + Array comps; + + for (int i = 0; i < selection.getNumSelected(); ++i) + { + Component* c = getComponentForUID (selection.getSelectedItem (i)); + jassert (c != 0); + if (c != 0) + comps.add (c); + } + + return comps; +} + +const Array ComponentEditorCanvas::getUnselectedComps() const +{ + Array comps; + + for (int i = componentHolder->getNumChildComponents(); --i >= 0;) + if (! selection.isSelected (componentHolder->getChildComponent(i)->getComponentUID())) + comps.add (componentHolder->getChildComponent(i)); + + return comps; +} + +//============================================================================== +void ComponentEditorCanvas::beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone) +{ + dragger = new DragOperation (*this, getSelectedComps(), getUnselectedComps(), e, overlay, zone); +} + +void ComponentEditorCanvas::continueDrag (const MouseEvent& e) +{ + if (dragger != 0) + dragger->drag (e); +} + +void ComponentEditorCanvas::endDrag (const MouseEvent& e) +{ + if (dragger != 0) + { + dragger->drag (e); + dragger = 0; + } +} + +//============================================================================== +ComponentEditorCanvas::OverlayItemComponent::OverlayItemComponent (ComponentEditorCanvas& canvas_) + : canvas (canvas_) +{ +} + +void ComponentEditorCanvas::OverlayItemComponent::setBoundsInTargetSpace (const Rectangle& r) +{ + setBounds (r + canvas.getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point())); +} diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h new file mode 100644 index 0000000000..7d59f33e82 --- /dev/null +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h @@ -0,0 +1,117 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-9 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCER_COMPONENTEDITORCANVAS_H_37C33B56__ +#define __JUCER_COMPONENTEDITORCANVAS_H_37C33B56__ + +#include "../../model/jucer_ComponentDocument.h" +#include "../jucer_DocumentEditorComponent.h" +class ComponentEditor; + + +//============================================================================== +class ComponentEditorCanvas : public Component, + public ValueTree::Listener, + public Timer +{ +public: + //============================================================================== + ComponentEditorCanvas (ComponentEditor& editor_); + ~ComponentEditorCanvas(); + + //============================================================================== + ComponentEditor& getEditor(); + ComponentDocument& getDocument(); + ComponentDocument::SelectedItems& getSelection(); + Component* getComponentHolder() const; + + //============================================================================== + void timerCallback(); + void paint (Graphics& g); + void resized(); + + void updateComponents(); + const Rectangle getContentArea() const; + void drawXAxis (Graphics& g, const Rectangle& r); + void drawYAxis (Graphics& g, const Rectangle& r); + + //============================================================================== + void valueTreePropertyChanged (ValueTree&, const var::identifier&) { updateComponents(); } + void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { updateComponents(); } + void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {} + + //============================================================================== + void getSelectedItemProperties (Array & props); + + //============================================================================== + void showSizeGuides(); + void hideSizeGuides(); + + //============================================================================== + class DragOperation; + + void beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone); + void continueDrag (const MouseEvent& e); + void endDrag (const MouseEvent& e); + +private: + ComponentEditor& editor; + const BorderSize border; + ScopedPointer dragger; + + //============================================================================== + class OverlayItemComponent : public Component + { + public: + OverlayItemComponent (ComponentEditorCanvas& canvas_); + + void setBoundsInTargetSpace (const Rectangle& r); + + ComponentDocument& getDocument() { return canvas.getDocument(); } + + protected: + ComponentEditorCanvas& canvas; + }; + + friend class OverlayItemComponent; + class ComponentResizeFrame; + class MarkerComponent; + class ComponentHolder; + class WholeComponentResizer; + class OverlayComponent; + + //============================================================================== + ComponentHolder* componentHolder; + OverlayComponent* overlay; + WholeComponentResizer* resizeFrame; + ComponentDocument::SelectedItems selection; + + Component* getComponentForUID (const uint32 uid) const; + const Array getSelectedComps() const; + const Array getUnselectedComps() const; +}; + + +#endif // __JUCER_COMPONENTEDITORCANVAS_H_37C33B56__ diff --git a/extras/Jucer (experimental)/Source/ui/jucer_DocumentEditorComponent.h b/extras/Jucer (experimental)/Source/ui/jucer_DocumentEditorComponent.h index 1c24eb8255..a0e59daaad 100644 --- a/extras/Jucer (experimental)/Source/ui/jucer_DocumentEditorComponent.h +++ b/extras/Jucer (experimental)/Source/ui/jucer_DocumentEditorComponent.h @@ -54,6 +54,10 @@ public: protected: OpenDocumentManager::Document* document; + +private: + DocumentEditorComponent (const DocumentEditorComponent&); + DocumentEditorComponent& operator= (const DocumentEditorComponent&); }; diff --git a/extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp b/extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp index 3e37fadbcf..761d231621 100644 --- a/extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp +++ b/extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp @@ -56,6 +56,10 @@ StandaloneFilterWindow::StandaloneFilterWindow (const String& title, if (filter != 0) { + filter->setPlayConfigDetails (JucePlugin_MaxNumInputChannels, + JucePlugin_MaxNumOutputChannels, + 44100, 512); + PropertySet* const globalSettings = getGlobalSettings(); deviceManager = new AudioFilterStreamingDeviceManager(); diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 30194b1c4e..b67408f3d1 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -15785,9 +15785,16 @@ public: bool undo() { if (isDeleting) + { target->addChild (child, childIndex, 0); + } else + { + // If you hit this, it seems that your object's state is getting confused - probably + // because you've interleaved some undoable and non-undoable operations? + jassert (childIndex < target->children.size()); target->removeChild (childIndex, 0); + } return true; } @@ -16024,6 +16031,9 @@ void ValueTree::SharedObject::addChild (SharedObject* child, int index, UndoMana } else { + if (index < 0) + index = children.size(); + undoManager->perform (new ValueTreeChildChangeAction (this, index, child)); } } @@ -16062,7 +16072,12 @@ void ValueTree::SharedObject::removeAllChildren (UndoManager* const undoManager) removeChild (children.size() - 1, undoManager); } -ValueTree ValueTree::invalid (static_cast (0)); +ValueTree::ValueTree() throw() + : object (0) +{ +} + +const ValueTree ValueTree::invalid; ValueTree::ValueTree (const String& type_) : object (new ValueTree::SharedObject (type_)) @@ -71243,8 +71258,8 @@ private: SimpleDeviceManagerInputLevelMeter& operator= (const SimpleDeviceManagerInputLevelMeter&); }; -class MidiInputSelectorComponentListBox : public ListBox, - public ListBoxModel +class AudioDeviceSelectorComponent::MidiInputSelectorComponentListBox : public ListBox, + public ListBoxModel { public: diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 16a4b94f8c..567dd6a759 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -6698,6 +6698,8 @@ class JUCE_API ValueTree { public: + ValueTree() throw(); + explicit ValueTree (const String& type); ValueTree (const ValueTree& other); @@ -6794,7 +6796,7 @@ public: } } - static ValueTree invalid; + static const ValueTree invalid; juce_UseDebuggingNewOperator @@ -6805,7 +6807,7 @@ private: class JUCE_API SharedObject : public ReferenceCountedObject { public: - SharedObject (const String& type); + explicit SharedObject (const String& type); SharedObject (const SharedObject& other); ~SharedObject(); @@ -10282,6 +10284,12 @@ public: return Rectangle (x1, y1, x2 - x1, y2 - y1); } + const Rectangle toFloat() const throw() + { + return Rectangle (static_cast (x), static_cast (y), + static_cast (w), static_cast (h)); + } + static bool intersectRectangles (ValueType& x1, ValueType& y1, ValueType& w1, ValueType& h1, const ValueType x2, const ValueType y2, const ValueType w2, const ValueType h2) throw() { @@ -26234,8 +26242,6 @@ private: #ifndef __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ #define __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ -class MidiInputSelectorComponentListBox; - class JUCE_API AudioDeviceSelectorComponent : public Component, public ComboBoxListener, public ButtonListener, @@ -26273,6 +26279,7 @@ private: const bool showChannelsAsStereoPairs; const bool hideAdvancedOptionsWithButton; + class MidiInputSelectorComponentListBox; MidiInputSelectorComponentListBox* midiInputsList; Label* midiInputsLabel; ComboBox* midiOutputSelector; diff --git a/src/containers/juce_ValueTree.cpp b/src/containers/juce_ValueTree.cpp index 139d7b9cbf..2fccc9f9dc 100644 --- a/src/containers/juce_ValueTree.cpp +++ b/src/containers/juce_ValueTree.cpp @@ -113,9 +113,16 @@ public: bool undo() { if (isDeleting) + { target->addChild (child, childIndex, 0); + } else + { + // If you hit this, it seems that your object's state is getting confused - probably + // because you've interleaved some undoable and non-undoable operations? + jassert (childIndex < target->children.size()); target->removeChild (childIndex, 0); + } return true; } @@ -356,6 +363,9 @@ void ValueTree::SharedObject::addChild (SharedObject* child, int index, UndoMana } else { + if (index < 0) + index = children.size(); + undoManager->perform (new ValueTreeChildChangeAction (this, index, child)); } } @@ -396,7 +406,12 @@ void ValueTree::SharedObject::removeAllChildren (UndoManager* const undoManager) //============================================================================== -ValueTree ValueTree::invalid (static_cast (0)); +ValueTree::ValueTree() throw() + : object (0) +{ +} + +const ValueTree ValueTree::invalid; ValueTree::ValueTree (const String& type_) : object (new ValueTree::SharedObject (type_)) diff --git a/src/containers/juce_ValueTree.h b/src/containers/juce_ValueTree.h index fedc851c0f..c3dde9fdee 100644 --- a/src/containers/juce_ValueTree.h +++ b/src/containers/juce_ValueTree.h @@ -73,6 +73,16 @@ class JUCE_API ValueTree { public: //============================================================================== + /** Creates an empty, invalid ValueTree. + + A ValueTree that is created with this constructor can't actually be used for anything, + it's just a default 'null' ValueTree that can be returned to indicate some sort of failure. + To create a real one, use the constructor that takes a string. + + @see ValueTree::invalid + */ + ValueTree() throw(); + /** Creates an empty ValueTree with the given type name. Like an XmlElement, each ValueTree node has a type, which you can access with getType() and hasType(). @@ -384,8 +394,10 @@ public: } } - /** An invalid ValueTree that can be used if you need to return one as an error condition, etc. */ - static ValueTree invalid; + /** An invalid ValueTree that can be used if you need to return one as an error condition, etc. + This invalid object is equivalent to ValueTree created with its default constructor. + */ + static const ValueTree invalid; //============================================================================== juce_UseDebuggingNewOperator @@ -397,7 +409,7 @@ private: class JUCE_API SharedObject : public ReferenceCountedObject { public: - SharedObject (const String& type); + explicit SharedObject (const String& type); SharedObject (const SharedObject& other); ~SharedObject(); diff --git a/src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp b/src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp index 875c7103bf..53841f454b 100644 --- a/src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp +++ b/src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp @@ -80,8 +80,8 @@ private: //============================================================================== -class MidiInputSelectorComponentListBox : public ListBox, - public ListBoxModel +class AudioDeviceSelectorComponent::MidiInputSelectorComponentListBox : public ListBox, + public ListBoxModel { public: //============================================================================== diff --git a/src/gui/components/special/juce_AudioDeviceSelectorComponent.h b/src/gui/components/special/juce_AudioDeviceSelectorComponent.h index 2ab38a3d50..e6be2c8ce1 100644 --- a/src/gui/components/special/juce_AudioDeviceSelectorComponent.h +++ b/src/gui/components/special/juce_AudioDeviceSelectorComponent.h @@ -29,7 +29,6 @@ #include "../controls/juce_ComboBox.h" #include "../controls/juce_ListBox.h" #include "../../../audio/devices/juce_AudioDeviceManager.h" -class MidiInputSelectorComponentListBox; //============================================================================== /** @@ -104,6 +103,7 @@ private: const bool showChannelsAsStereoPairs; const bool hideAdvancedOptionsWithButton; + class MidiInputSelectorComponentListBox; MidiInputSelectorComponentListBox* midiInputsList; Label* midiInputsLabel; ComboBox* midiOutputSelector; diff --git a/src/gui/graphics/geometry/juce_Rectangle.h b/src/gui/graphics/geometry/juce_Rectangle.h index 81a1b6a32b..f41e353551 100644 --- a/src/gui/graphics/geometry/juce_Rectangle.h +++ b/src/gui/graphics/geometry/juce_Rectangle.h @@ -466,6 +466,7 @@ public: /** Returns the smallest integer-aligned rectangle that completely contains this one. This is only relevent for floating-point rectangles, of course. + @see toFloat() */ const Rectangle getSmallestIntegerContainer() const throw() { @@ -477,6 +478,16 @@ public: return Rectangle (x1, y1, x2 - x1, y2 - y1); } + /** Casts this rectangle to a Rectangle. + Obviously this is mainly useful for rectangles that use integer types. + @see getSmallestIntegerContainer + */ + const Rectangle toFloat() const throw() + { + return Rectangle (static_cast (x), static_cast (y), + static_cast (w), static_cast (h)); + } + //============================================================================== /** Static utility to intersect two sets of rectangular co-ordinates. diff --git a/src/native/windows/juce_win32_NativeIncludes.h b/src/native/windows/juce_win32_NativeIncludes.h index d70850694b..69be25a8d3 100644 --- a/src/native/windows/juce_win32_NativeIncludes.h +++ b/src/native/windows/juce_win32_NativeIncludes.h @@ -132,6 +132,14 @@ Microsoft's suggested fix for this is to hack their qedit.h file! See: http://social.msdn.microsoft.com/Forums/en-US/windowssdk/thread/ed097d2c-3d68-4f48-8448-277eaaf68252 .. which is a bit of a bodge, but a lot less hassle than installing the full DShow SDK. + + An alternative workaround is to create a dummy dxtrans.h file and put it in your include path. + The dummy file just needs to contain the following content: + #define __IDxtCompositor_INTERFACE_DEFINED__ + #define __IDxtAlphaSetter_INTERFACE_DEFINED__ + #define __IDxtJpeg_INTERFACE_DEFINED__ + #define __IDxtKey_INTERFACE_DEFINED__ + ..and that should be enough to convince qedit.h that you have the SDK! */ #include #include