| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := Jucer | |||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -O3 | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := Jucer | |||
| @@ -44,6 +44,7 @@ endif | |||
| OBJECTS := \ | |||
| $(OBJDIR)/jucer_ComponentDocument.o \ | |||
| $(OBJDIR)/jucer_Coordinate.o \ | |||
| $(OBJDIR)/jucer_DrawableDocument.o \ | |||
| $(OBJDIR)/jucer_NewFileWizard.o \ | |||
| $(OBJDIR)/jucer_Project.o \ | |||
| @@ -92,6 +93,11 @@ $(OBJDIR)/jucer_ComponentDocument.o: ../../Source/model/jucer_ComponentDocument. | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/jucer_Coordinate.o: ../../Source/model/jucer_Coordinate.cpp | |||
| -@mkdir -p $(OBJDIR) | |||
| @echo $(notdir $<) | |||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
| $(OBJDIR)/jucer_DrawableDocument.o: ../../Source/model/jucer_DrawableDocument.cpp | |||
| -@mkdir -p $(OBJDIR) | |||
| @echo $(notdir $<) | |||
| @@ -18,6 +18,7 @@ | |||
| 93C9F3F27602A33DDC9C2250 = { isa = PBXBuildFile; fileRef = 2767E1D082874D301D5D5F43; }; | |||
| 2E6836738CE7EB452FDC7E9A = { isa = PBXBuildFile; fileRef = D9FB1A5365FEEB854A0FF7BF; }; | |||
| CD4226951C3F7FE19CF8A7CE = { isa = PBXBuildFile; fileRef = 2D6D6985B452EA0B67A18914; }; | |||
| 1DF9688E29753A0459E6C32A = { isa = PBXBuildFile; fileRef = 45C80436FD5A8438D0E6BE17; }; | |||
| 1174D3512AF8207950094C56 = { isa = PBXBuildFile; fileRef = FF625CB50FB5C3536BA40604; }; | |||
| 087CCE9E7146F1EC4F241254 = { isa = PBXBuildFile; fileRef = DA142548FCADFAC50648ED3C; }; | |||
| E9935BFB0EFA8CCCD41DC08E = { isa = PBXBuildFile; fileRef = D47A40CB3CF6AAE14B3C7796; }; | |||
| @@ -62,6 +63,8 @@ | |||
| E18C99BDD4EF3DFD767F3770 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_TextButton.h; path = "../../Source/model/Component Types/jucer_TextButton.h"; sourceTree = SOURCE_ROOT; }; | |||
| 2D6D6985B452EA0B67A18914 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_ComponentDocument.cpp; path = ../../Source/model/jucer_ComponentDocument.cpp; sourceTree = SOURCE_ROOT; }; | |||
| E6CC3A04349F6B227FDAB26F = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_ComponentDocument.h; path = ../../Source/model/jucer_ComponentDocument.h; sourceTree = SOURCE_ROOT; }; | |||
| 45C80436FD5A8438D0E6BE17 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_Coordinate.cpp; path = ../../Source/model/jucer_Coordinate.cpp; sourceTree = SOURCE_ROOT; }; | |||
| 420E4189E7DE25E9D0D8E5B8 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_Coordinate.h; path = ../../Source/model/jucer_Coordinate.h; sourceTree = SOURCE_ROOT; }; | |||
| FF625CB50FB5C3536BA40604 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_DrawableDocument.cpp; path = ../../Source/model/jucer_DrawableDocument.cpp; sourceTree = SOURCE_ROOT; }; | |||
| A490098DA6400B3881F336D0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_DrawableDocument.h; path = ../../Source/model/jucer_DrawableDocument.h; sourceTree = SOURCE_ROOT; }; | |||
| DA142548FCADFAC50648ED3C = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_NewFileWizard.cpp; path = ../../Source/model/jucer_NewFileWizard.cpp; sourceTree = SOURCE_ROOT; }; | |||
| @@ -144,6 +147,8 @@ | |||
| D3D6EC2C17524688F2E803EB, | |||
| 2D6D6985B452EA0B67A18914, | |||
| E6CC3A04349F6B227FDAB26F, | |||
| 45C80436FD5A8438D0E6BE17, | |||
| 420E4189E7DE25E9D0D8E5B8, | |||
| FF625CB50FB5C3536BA40604, | |||
| A490098DA6400B3881F336D0, | |||
| DA142548FCADFAC50648ED3C, | |||
| @@ -319,6 +324,7 @@ | |||
| 87CCE4CB1FAB40B6F21DEACE = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; | |||
| 5362E03ADF975A126C1F2F7B = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( | |||
| CD4226951C3F7FE19CF8A7CE, | |||
| 1DF9688E29753A0459E6C32A, | |||
| 1174D3512AF8207950094C56, | |||
| 087CCE9E7146F1EC4F241254, | |||
| E9935BFB0EFA8CCCD41DC08E, | |||
| @@ -139,6 +139,8 @@ | |||
| </Filter> | |||
| <File RelativePath="..\..\Source\model\jucer_ComponentDocument.cpp"/> | |||
| <File RelativePath="..\..\Source\model\jucer_ComponentDocument.h"/> | |||
| <File RelativePath="..\..\Source\model\jucer_Coordinate.cpp"/> | |||
| <File RelativePath="..\..\Source\model\jucer_Coordinate.h"/> | |||
| <File RelativePath="..\..\Source\model\jucer_DrawableDocument.cpp"/> | |||
| <File RelativePath="..\..\Source\model\jucer_DrawableDocument.h"/> | |||
| <File RelativePath="..\..\Source\model\jucer_NewFileWizard.cpp"/> | |||
| @@ -139,6 +139,8 @@ | |||
| </Filter> | |||
| <File RelativePath="..\..\Source\model\jucer_ComponentDocument.cpp"/> | |||
| <File RelativePath="..\..\Source\model\jucer_ComponentDocument.h"/> | |||
| <File RelativePath="..\..\Source\model\jucer_Coordinate.cpp"/> | |||
| <File RelativePath="..\..\Source\model\jucer_Coordinate.h"/> | |||
| <File RelativePath="..\..\Source\model\jucer_DrawableDocument.cpp"/> | |||
| <File RelativePath="..\..\Source\model\jucer_DrawableDocument.h"/> | |||
| <File RelativePath="..\..\Source\model\jucer_NewFileWizard.cpp"/> | |||
| @@ -37,6 +37,10 @@ | |||
| resource="0" file="Source/model/jucer_ComponentDocument.cpp"/> | |||
| <FILE id="u5n6JTb4z" name="jucer_ComponentDocument.h" compile="0" resource="0" | |||
| file="Source/model/jucer_ComponentDocument.h"/> | |||
| <FILE id="lxuyJGZcw" name="jucer_Coordinate.cpp" compile="1" resource="0" | |||
| file="Source/model/jucer_Coordinate.cpp"/> | |||
| <FILE id="CyKbwNlwe" name="jucer_Coordinate.h" compile="0" resource="0" | |||
| file="Source/model/jucer_Coordinate.h"/> | |||
| <FILE id="XxIww5OtT" name="jucer_DrawableDocument.cpp" compile="1" | |||
| resource="0" file="Source/model/jucer_DrawableDocument.cpp"/> | |||
| <FILE id="lbB698APl" name="jucer_DrawableDocument.h" compile="0" resource="0" | |||
| @@ -27,6 +27,7 @@ | |||
| #include "Component Types/jucer_TextButton.h" | |||
| #include "Component Types/jucer_ToggleButton.h" | |||
| //============================================================================== | |||
| static const char* const componentDocumentTag = "COMPONENT"; | |||
| static const char* const componentGroupTag = "COMPONENTS"; | |||
| @@ -42,7 +43,8 @@ static const char* const metadataTagEnd = "JUCER_" "COMPONENT_METADATA_E | |||
| //============================================================================== | |||
| class ComponentBoundsEditor : public PropertyComponent, | |||
| public ButtonListener | |||
| public ButtonListener, | |||
| public Value::Listener | |||
| { | |||
| public: | |||
| enum Type | |||
| @@ -68,15 +70,21 @@ public: | |||
| addAndMakeVisible (anchorButton1 = new TextButton (String::empty)); | |||
| anchorButton1->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); | |||
| anchorButton1->setTriggeredOnMouseDown (true); | |||
| anchorButton1->addButtonListener (this); | |||
| addAndMakeVisible (anchorButton2 = new TextButton (String::empty)); | |||
| anchorButton2->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); | |||
| anchorButton2->setTriggeredOnMouseDown (true); | |||
| anchorButton2->addButtonListener (this); | |||
| boundsValue.addListener (this); | |||
| valueChanged (boundsValue); | |||
| } | |||
| ~ComponentBoundsEditor() | |||
| { | |||
| boundsValue.removeListener (this); | |||
| deleteAllChildren(); | |||
| } | |||
| @@ -87,8 +95,16 @@ public: | |||
| label->setBounds (r.getX(), r.getY(), r.getWidth() / 2, r.getHeight() / 2); | |||
| proportionButton->setBounds (r.getX() + r.getWidth() / 2, r.getY(), | |||
| r.getWidth() / 2, r.getHeight() / 2); | |||
| anchorButton1->setBounds (r.getX(), r.getY() + r.getHeight() / 2, r.getWidth() / 2, r.getHeight() / 2); | |||
| anchorButton2->setBounds (r.getX() + r.getWidth() / 2, r.getY() + r.getHeight() / 2, r.getWidth() / 2, r.getHeight() / 2); | |||
| if (anchorButton2->isVisible()) | |||
| { | |||
| anchorButton1->setBounds (r.getX(), r.getY() + r.getHeight() / 2, r.getWidth() / 2, r.getHeight() / 2); | |||
| anchorButton2->setBounds (r.getX() + r.getWidth() / 2, r.getY() + r.getHeight() / 2, r.getWidth() / 2, r.getHeight() / 2); | |||
| } | |||
| else | |||
| { | |||
| anchorButton1->setBounds (r.getX(), r.getY() + r.getHeight() / 2, r.getWidth(), r.getHeight() / 2); | |||
| } | |||
| } | |||
| void refresh() | |||
| @@ -97,24 +113,49 @@ public: | |||
| void buttonClicked (Button* button) | |||
| { | |||
| RectangleCoordinates r (boundsValue.toString()); | |||
| Coordinate& coord = getCoord (r); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (compState)); | |||
| if (button == proportionButton) | |||
| { | |||
| RectangleCoordinates r (boundsValue.toString()); | |||
| Coordinate& coord = getCoord (r); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (compState)); | |||
| coord.toggleProportionality (*markers); | |||
| boundsValue = r.toString(); | |||
| } | |||
| else if (button == anchorButton1) | |||
| { | |||
| const String marker (pickMarker (anchorButton1, coord.getAnchor1())); | |||
| if (marker.isNotEmpty()) | |||
| { | |||
| coord.changeAnchor1 (marker, *markers); | |||
| boundsValue = r.toString(); | |||
| } | |||
| } | |||
| else if (button == anchorButton2) | |||
| { | |||
| const String marker (pickMarker (anchorButton2, coord.getAnchor2())); | |||
| if (marker.isNotEmpty()) | |||
| { | |||
| coord.changeAnchor2 (marker, *markers); | |||
| boundsValue = r.toString(); | |||
| } | |||
| } | |||
| } | |||
| void valueChanged (Value&) | |||
| { | |||
| RectangleCoordinates r (boundsValue.toString()); | |||
| Coordinate& coord = getCoord (r); | |||
| anchorButton1->setButtonText (coord.getAnchor1()); | |||
| anchorButton2->setVisible (coord.isProportional()); | |||
| anchorButton2->setButtonText (coord.getAnchor2()); | |||
| resized(); | |||
| } | |||
| //============================================================================== | |||
| class BoundsCoordValueSource : public Value::ValueSource, | |||
| public Value::Listener | |||
| @@ -186,6 +227,22 @@ public: | |||
| return r.left; | |||
| } | |||
| const String pickMarker (Component* button, const String& currentMarker) | |||
| { | |||
| const StringArray markers (document.getComponentMarkers (type == left || type == right)); | |||
| PopupMenu m; | |||
| for (int i = 0; i < markers.size(); ++i) | |||
| m.addItem (i + 1, markers[i], true, currentMarker == markers[i]); | |||
| const int r = m.showAt (button); | |||
| if (r > 0) | |||
| return markers [r - 1]; | |||
| return String::empty; | |||
| } | |||
| private: | |||
| ComponentDocument& document; | |||
| Type type; | |||
| @@ -588,11 +645,44 @@ const RectangleCoordinates ComponentDocument::getCoordsFor (const ValueTree& sta | |||
| return RectangleCoordinates (state [compBoundsProperty]); | |||
| } | |||
| bool ComponentDocument::setCoordsFor (ValueTree& state, const RectangleCoordinates& pr) | |||
| { | |||
| const String newBoundsString (pr.toString()); | |||
| if (state[compBoundsProperty] == newBoundsString) | |||
| return false; | |||
| state.setProperty (compBoundsProperty, newBoundsString, getUndoManager()); | |||
| return true; | |||
| } | |||
| Coordinate::MarkerResolver* ComponentDocument::createMarkerResolver (const ValueTree& state) | |||
| { | |||
| return new ComponentMarkerResolver (*this, state, getCanvasWidth().getValue(), getCanvasHeight().getValue()); | |||
| } | |||
| const StringArray ComponentDocument::getComponentMarkers (bool horizontal) const | |||
| { | |||
| StringArray s; | |||
| if (horizontal) | |||
| { | |||
| s.add (Coordinate::parentLeftMarkerName); | |||
| s.add (Coordinate::parentRightMarkerName); | |||
| s.add ("left"); | |||
| s.add ("right"); | |||
| } | |||
| else | |||
| { | |||
| s.add (Coordinate::parentTopMarkerName); | |||
| s.add (Coordinate::parentBottomMarkerName); | |||
| s.add ("top"); | |||
| s.add ("bottom"); | |||
| } | |||
| return s; | |||
| } | |||
| void ComponentDocument::updateComponent (Component* comp) | |||
| { | |||
| const ValueTree v (getComponentState (comp)); | |||
| @@ -675,151 +765,3 @@ UndoManager* ComponentDocument::getUndoManager() | |||
| { | |||
| return &undoManager; | |||
| } | |||
| //============================================================================== | |||
| class ComponentDocument::DragHandler | |||
| { | |||
| public: | |||
| DragHandler (ComponentDocument& document_, | |||
| const Array<Component*>& items, | |||
| const MouseEvent& e, | |||
| const ResizableBorderComponent::Zone& zone_, | |||
| Component* parentForOverlays) | |||
| : document (document_), | |||
| zone (zone_) | |||
| { | |||
| for (int i = 0; i < items.size(); ++i) | |||
| { | |||
| Component* comp = items.getUnchecked(i); | |||
| jassert (comp != 0); | |||
| const ValueTree v (document.getComponentState (comp)); | |||
| draggedComponents.add (v); | |||
| Rectangle<int> pos; | |||
| { | |||
| RectangleCoordinates relativePos (v [compBoundsProperty].toString()); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (v)); | |||
| pos = relativePos.resolve (*markers); | |||
| originalPositions.add (pos); | |||
| } | |||
| const Rectangle<float> floatPos ((float) pos.getX(), (float) pos.getY(), | |||
| (float) pos.getWidth(), (float) pos.getHeight()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingLeftEdge()) | |||
| verticalSnapPositions.add (floatPos.getX()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingLeftEdge() || zone.isDraggingRightEdge()) | |||
| verticalSnapPositions.add (floatPos.getCentreX()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingRightEdge()) | |||
| verticalSnapPositions.add (floatPos.getRight()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingTopEdge()) | |||
| horizontalSnapPositions.add (floatPos.getY()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingTopEdge() || zone.isDraggingBottomEdge()) | |||
| verticalSnapPositions.add (floatPos.getCentreY()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingBottomEdge()) | |||
| horizontalSnapPositions.add (floatPos.getBottom()); | |||
| } | |||
| document.beginNewTransaction(); | |||
| } | |||
| ~DragHandler() | |||
| { | |||
| document.beginNewTransaction(); | |||
| } | |||
| void drag (const MouseEvent& e) | |||
| { | |||
| document.getUndoManager()->undoCurrentTransactionOnly(); | |||
| for (int n = 50;;) | |||
| { | |||
| // Need to repeatedly apply the new positions until they all settle down, in case some of | |||
| // the coords are relative to each other.. | |||
| bool anyUpdated = false; | |||
| for (int i = 0; i < draggedComponents.size(); ++i) | |||
| if (dragItem (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i))) | |||
| anyUpdated = true; | |||
| if (! anyUpdated) | |||
| break; | |||
| if (--n == 0) | |||
| { | |||
| jassertfalse; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| bool dragItem (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos) | |||
| { | |||
| const Rectangle<int> newBounds (zone.resizeRectangleBy (originalPos, distance)); | |||
| RectangleCoordinates pr (v [compBoundsProperty].toString()); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (v)); | |||
| pr.moveToAbsolute (newBounds, *markers); | |||
| const String newBoundsString (pr.toString()); | |||
| if (v[compBoundsProperty] == newBoundsString) | |||
| return false; | |||
| v.setProperty (compBoundsProperty, newBoundsString, document.getUndoManager()); | |||
| return true; | |||
| } | |||
| const Array<float> getVerticalSnapPositions (const Point<int>& distance) const | |||
| { | |||
| Array<float> p (verticalSnapPositions); | |||
| for (int i = p.size(); --i >= 0;) | |||
| p.set (i, p.getUnchecked(i) + distance.getX()); | |||
| return p; | |||
| } | |||
| const Array<float> getHorizontalSnapPositions (const Point<int>& distance) const | |||
| { | |||
| Array<float> p (horizontalSnapPositions); | |||
| for (int i = p.size(); --i >= 0;) | |||
| p.set (i, p.getUnchecked(i) + distance.getY()); | |||
| return p; | |||
| } | |||
| private: | |||
| ComponentDocument& document; | |||
| Array <ValueTree> draggedComponents; | |||
| Array <Rectangle<int> > originalPositions; | |||
| Array <float> verticalSnapPositions, horizontalSnapPositions; | |||
| const ResizableBorderComponent::Zone zone; | |||
| }; | |||
| void ComponentDocument::beginDrag (const Array<Component*>& items, const MouseEvent& e, | |||
| Component* parentForOverlays, const ResizableBorderComponent::Zone& zone) | |||
| { | |||
| dragger = new DragHandler (*this, items, e, zone, parentForOverlays); | |||
| } | |||
| void ComponentDocument::continueDrag (const MouseEvent& e) | |||
| { | |||
| if (dragger != 0) | |||
| dragger->drag (e); | |||
| } | |||
| void ComponentDocument::endDrag (const MouseEvent& e) | |||
| { | |||
| if (dragger != 0) | |||
| { | |||
| dragger->drag (e); | |||
| dragger = 0; | |||
| } | |||
| } | |||
| @@ -28,6 +28,7 @@ | |||
| #include "../jucer_Headers.h" | |||
| #include "jucer_Project.h" | |||
| #include "jucer_Coordinate.h" | |||
| //============================================================================== | |||
| @@ -66,7 +67,9 @@ public: | |||
| void getComponentProperties (Array <PropertyComponent*>& props, Component* comp); | |||
| bool isStateForComponent (const ValueTree& storedState, Component* comp) const; | |||
| Coordinate::MarkerResolver* createMarkerResolver (const ValueTree& state); | |||
| const RectangleCoordinates getCoordsFor (const ValueTree& state) const; | |||
| const StringArray getComponentMarkers (bool horizontal) const; | |||
| const RectangleCoordinates getCoordsFor (const ValueTree& componentState) const; | |||
| bool setCoordsFor (ValueTree& componentState, const RectangleCoordinates& newSize); | |||
| void addNewComponentMenuItems (PopupMenu& menu) const; | |||
| void performNewComponentMenuItem (int menuResultCode); | |||
| @@ -93,9 +96,6 @@ private: | |||
| UndoManager undoManager; | |||
| bool changedSinceSaved; | |||
| class DragHandler; | |||
| ScopedPointer <DragHandler> dragger; | |||
| void checkRootObject(); | |||
| ValueTree getComponentGroup() const; | |||
| Value getRootValue (const var::identifier& name) { return root.getPropertyAsValue (name, getUndoManager()); } | |||
| @@ -0,0 +1,392 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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_Coordinate.h" | |||
| //============================================================================== | |||
| const char* Coordinate::parentLeftMarkerName = "parent.left"; | |||
| const char* Coordinate::parentRightMarkerName = "parent.right"; | |||
| const char* Coordinate::parentTopMarkerName = "parent.top"; | |||
| const char* Coordinate::parentBottomMarkerName = "parent.bottom"; | |||
| Coordinate::Coordinate (bool isHorizontal_) | |||
| : value (0), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal_) | |||
| : value (absoluteDistanceFromOrigin), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double absoluteDistance, const String& source, bool isHorizontal_) | |||
| : anchor1 (source), value (absoluteDistance), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double relativeProportion, const String& pos1, const String& pos2, bool isHorizontal_) | |||
| : anchor1 (pos1), anchor2 (pos2), value (relativeProportion), isProportion (true), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::~Coordinate() | |||
| { | |||
| } | |||
| const Coordinate Coordinate::getAnchorPoint1() const | |||
| { | |||
| return Coordinate (0.0, anchor1, isHorizontal); | |||
| } | |||
| const Coordinate Coordinate::getAnchorPoint2() const | |||
| { | |||
| return Coordinate (0.0, anchor2, isHorizontal); | |||
| } | |||
| bool Coordinate::isOrigin (const String& name) | |||
| { | |||
| return name.isEmpty() || name == parentLeftMarkerName || name == parentTopMarkerName; | |||
| } | |||
| const String Coordinate::getOriginMarkerName() const | |||
| { | |||
| return isHorizontal ? parentLeftMarkerName : parentTopMarkerName; | |||
| } | |||
| const String Coordinate::getExtentMarkerName() const | |||
| { | |||
| return isHorizontal ? parentRightMarkerName : parentBottomMarkerName; | |||
| } | |||
| const String Coordinate::checkName (const String& name) const | |||
| { | |||
| return name.isEmpty() ? getOriginMarkerName() : name; | |||
| } | |||
| double Coordinate::getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const | |||
| { | |||
| if (isOrigin (name)) | |||
| return 0.0; | |||
| return markerResolver.findMarker (name, isHorizontal) | |||
| .resolve (markerResolver, recursionCounter + 1); | |||
| } | |||
| struct RecursivePositionException | |||
| { | |||
| }; | |||
| double Coordinate::resolve (MarkerResolver& markerResolver, int recursionCounter) const | |||
| { | |||
| if (recursionCounter > 100) | |||
| { | |||
| jassertfalse | |||
| throw RecursivePositionException(); | |||
| } | |||
| const double pos1 = getPosition (anchor1, markerResolver, recursionCounter); | |||
| return isProportion ? pos1 + (getPosition (anchor2, markerResolver, recursionCounter) - pos1) * value | |||
| : pos1 + value; | |||
| } | |||
| double Coordinate::resolve (MarkerResolver& markerResolver) const | |||
| { | |||
| try | |||
| { | |||
| return resolve (markerResolver, 0); | |||
| } | |||
| catch (RecursivePositionException&) | |||
| {} | |||
| return 0.0; | |||
| } | |||
| void Coordinate::moveToAbsolute (double newPos, MarkerResolver& markerResolver) | |||
| { | |||
| try | |||
| { | |||
| const double pos1 = getPosition (anchor1, markerResolver, 0); | |||
| if (isProportion) | |||
| { | |||
| const double size = getPosition (anchor2, markerResolver, 0) - pos1; | |||
| if (size != 0) | |||
| value = (newPos - pos1) / size; | |||
| } | |||
| else | |||
| { | |||
| value = newPos - pos1; | |||
| } | |||
| } | |||
| catch (RecursivePositionException&) | |||
| {} | |||
| } | |||
| bool Coordinate::isRecursive (MarkerResolver& markerResolver) const | |||
| { | |||
| try | |||
| { | |||
| resolve (markerResolver, 0); | |||
| } | |||
| catch (RecursivePositionException&) | |||
| { | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void Coordinate::skipWhitespace (const String& s, int& i) | |||
| { | |||
| while (CharacterFunctions::isWhitespace (s[i])) | |||
| ++i; | |||
| } | |||
| const String Coordinate::readMarkerName (const String& s, int& i) | |||
| { | |||
| skipWhitespace (s, i); | |||
| if (CharacterFunctions::isLetter (s[i]) || s[i] == '_') | |||
| { | |||
| int start = i; | |||
| while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.') | |||
| ++i; | |||
| return s.substring (start, i); | |||
| } | |||
| return String::empty; | |||
| } | |||
| double Coordinate::readNumber (const String& s, int& i) | |||
| { | |||
| skipWhitespace (s, i); | |||
| int start = i; | |||
| if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-') | |||
| ++i; | |||
| while (CharacterFunctions::isDigit (s[i]) || s[i] == '.') | |||
| ++i; | |||
| if ((s[i] == 'e' || s[i] == 'E') | |||
| && (CharacterFunctions::isDigit (s[i + 1]) | |||
| || s[i + 1] == '-' | |||
| || s[i + 1] == '+')) | |||
| { | |||
| i += 2; | |||
| while (CharacterFunctions::isDigit (s[i])) | |||
| ++i; | |||
| } | |||
| const double value = s.substring (start, i).getDoubleValue(); | |||
| while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',') | |||
| ++i; | |||
| return value; | |||
| } | |||
| Coordinate::Coordinate (const String& s, bool isHorizontal_) | |||
| : value (0), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| int i = 0; | |||
| anchor1 = readMarkerName (s, i); | |||
| if (anchor1.isNotEmpty()) | |||
| { | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '+') | |||
| value = readNumber (s, ++i); | |||
| else if (s[i] == '-') | |||
| value = -readNumber (s, ++i); | |||
| } | |||
| else | |||
| { | |||
| value = readNumber (s, i); | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '%') | |||
| { | |||
| isProportion = true; | |||
| value /= 100.0; | |||
| skipWhitespace (s, ++i); | |||
| if (s[i] == '*') | |||
| { | |||
| anchor1 = readMarkerName (s, ++i); | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '-' && s[i + 1] == '>') | |||
| { | |||
| i += 2; | |||
| anchor2 = readMarkerName (s, i); | |||
| } | |||
| else | |||
| { | |||
| anchor2 = anchor1; | |||
| anchor1 = getOriginMarkerName(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| anchor1 = getOriginMarkerName(); | |||
| anchor2 = getExtentMarkerName(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| static const String limitedAccuracyString (const double n) | |||
| { | |||
| return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd ("."); | |||
| } | |||
| const String Coordinate::toString() const | |||
| { | |||
| if (isProportion) | |||
| { | |||
| const String percent (limitedAccuracyString (value * 100.0)); | |||
| if (isOrigin (anchor1)) | |||
| { | |||
| if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName) | |||
| return percent + "%"; | |||
| else | |||
| return percent + "% * " + checkName (anchor2); | |||
| } | |||
| else | |||
| return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2); | |||
| } | |||
| else | |||
| { | |||
| if (isOrigin (anchor1)) | |||
| return limitedAccuracyString (value); | |||
| else if (value > 0) | |||
| return checkName (anchor1) + " + " + limitedAccuracyString (value); | |||
| else if (value < 0) | |||
| return checkName (anchor1) + " - " + limitedAccuracyString (-value); | |||
| else | |||
| return checkName (anchor1); | |||
| } | |||
| } | |||
| const double Coordinate::getEditableValue() const | |||
| { | |||
| return isProportion ? value * 100.0 : value; | |||
| } | |||
| void Coordinate::setEditableValue (const double newValue) | |||
| { | |||
| value = isProportion ? newValue / 100.0 : newValue; | |||
| } | |||
| void Coordinate::toggleProportionality (MarkerResolver& markerResolver) | |||
| { | |||
| const double oldValue = resolve (markerResolver); | |||
| isProportion = ! isProportion; | |||
| anchor1 = getOriginMarkerName(); | |||
| anchor2 = getExtentMarkerName(); | |||
| moveToAbsolute (oldValue, markerResolver); | |||
| } | |||
| void Coordinate::changeAnchor1 (const String& newMarkerName, MarkerResolver& markerResolver) | |||
| { | |||
| const double oldValue = resolve (markerResolver); | |||
| anchor1 = newMarkerName; | |||
| moveToAbsolute (oldValue, markerResolver); | |||
| } | |||
| void Coordinate::changeAnchor2 (const String& newMarkerName, MarkerResolver& markerResolver) | |||
| { | |||
| const double oldValue = resolve (markerResolver); | |||
| anchor2 = newMarkerName; | |||
| moveToAbsolute (oldValue, markerResolver); | |||
| } | |||
| //============================================================================== | |||
| RectangleCoordinates::RectangleCoordinates() | |||
| : left (true), right (true), top (false), bottom (false) | |||
| { | |||
| } | |||
| RectangleCoordinates::RectangleCoordinates (const Rectangle<int>& rect) | |||
| : left (rect.getX(), true), | |||
| right (rect.getWidth(), "left", true), | |||
| top (rect.getY(), false), | |||
| bottom (rect.getHeight(), "top", false) | |||
| { | |||
| } | |||
| RectangleCoordinates::RectangleCoordinates (const String& stringVersion) | |||
| : left (true), right (true), top (false), bottom (false) | |||
| { | |||
| StringArray tokens; | |||
| tokens.addTokens (stringVersion, ",", String::empty); | |||
| left = Coordinate (tokens [0], true); | |||
| top = Coordinate (tokens [1], false); | |||
| right = Coordinate (tokens [2], true); | |||
| bottom = Coordinate (tokens [3], false); | |||
| } | |||
| bool RectangleCoordinates::isRecursive (Coordinate::MarkerResolver& markerResolver) const | |||
| { | |||
| return left.isRecursive (markerResolver) || right.isRecursive (markerResolver) | |||
| || top.isRecursive (markerResolver) || bottom.isRecursive (markerResolver); | |||
| } | |||
| const Rectangle<int> RectangleCoordinates::resolve (Coordinate::MarkerResolver& markerResolver) const | |||
| { | |||
| const int l = roundToInt (left.resolve (markerResolver)); | |||
| const int r = roundToInt (right.resolve (markerResolver)); | |||
| const int t = roundToInt (top.resolve (markerResolver)); | |||
| const int b = roundToInt (bottom.resolve (markerResolver)); | |||
| return Rectangle<int> (l, t, r - l, b - t); | |||
| } | |||
| void RectangleCoordinates::moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver) | |||
| { | |||
| left.moveToAbsolute (newPos.getX(), markerResolver); | |||
| right.moveToAbsolute (newPos.getRight(), markerResolver); | |||
| top.moveToAbsolute (newPos.getY(), markerResolver); | |||
| bottom.moveToAbsolute (newPos.getBottom(), markerResolver); | |||
| } | |||
| const String RectangleCoordinates::toString() const | |||
| { | |||
| return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString(); | |||
| } | |||
| @@ -0,0 +1,157 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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_COORDINATE_H_EF56ACFA__ | |||
| #define __JUCER_COORDINATE_H_EF56ACFA__ | |||
| #include "../jucer_Headers.h" | |||
| //============================================================================== | |||
| /** | |||
| Holds a co-ordinate along the x or y axis, expressed either as an absolute | |||
| position, or relative to other named marker positions. | |||
| */ | |||
| class Coordinate | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a zero coordinate. */ | |||
| explicit Coordinate (bool isHorizontal); | |||
| /** Recreates a coordinate from its stringified version. */ | |||
| Coordinate (const String& stringVersion, bool isHorizontal); | |||
| /** Creates an absolute position from the parent origin. */ | |||
| Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal); | |||
| /** Creates an absolute position relative to a named marker. */ | |||
| Coordinate (double absolutePosition, const String& relativeToMarker, bool isHorizontal); | |||
| /** Creates a relative position between two named markers. */ | |||
| Coordinate (double relativePosition, const String& marker1, const String& marker2, bool isHorizontal); | |||
| /** Destructor. */ | |||
| ~Coordinate(); | |||
| //============================================================================== | |||
| /** | |||
| Provides an interface for looking up the position of a named marker. | |||
| */ | |||
| class MarkerResolver | |||
| { | |||
| public: | |||
| virtual ~MarkerResolver() {} | |||
| virtual const Coordinate findMarker (const String& name, bool isHorizontal) = 0; | |||
| }; | |||
| /** Calculates the absolute position of this co-ordinate. */ | |||
| double resolve (MarkerResolver& markerResolver) const; | |||
| /** Returns true if this co-ordinate is expressed in terms of markers that form a recursive loop. */ | |||
| bool isRecursive (MarkerResolver& markerResolver) const; | |||
| /** Changes the value of this marker to make it resolve to the specified position. */ | |||
| void moveToAbsolute (double newPos, MarkerResolver& markerResolver); | |||
| const Coordinate getAnchorPoint1() const; | |||
| const Coordinate getAnchorPoint2() const; | |||
| const double getEditableValue() const; | |||
| void setEditableValue (const double newValue); | |||
| bool isProportional() const throw() { return isProportion; } | |||
| void toggleProportionality (MarkerResolver& markerResolver); | |||
| const String getAnchor1() const { return checkName (anchor1); } | |||
| void changeAnchor1 (const String& newMarkerName, MarkerResolver& markerResolver); | |||
| const String getAnchor2() const { return checkName (anchor2); } | |||
| void changeAnchor2 (const String& newMarkerName, MarkerResolver& markerResolver); | |||
| //============================================================================== | |||
| /* | |||
| Position string formats: | |||
| 123 = absolute pixels from parent origin | |||
| marker | |||
| marker + 123 | |||
| marker - 123 | |||
| 50% = percentage between parent origin and parent extent | |||
| 50% * marker = percentage between parent origin and marker | |||
| 50% * marker1 -> marker2 = percentage between two markers | |||
| standard marker names: | |||
| "origin" = parent origin | |||
| "size" = parent right or bottom | |||
| "top", "left", "bottom", "right" = refer to the component's own left, right, top and bottom. | |||
| */ | |||
| const String toString() const; | |||
| //============================================================================== | |||
| static const char* parentLeftMarkerName; | |||
| static const char* parentRightMarkerName; | |||
| static const char* parentTopMarkerName; | |||
| static const char* parentBottomMarkerName; | |||
| private: | |||
| //============================================================================== | |||
| String anchor1, anchor2; | |||
| double value; | |||
| bool isProportion, isHorizontal; | |||
| double resolve (MarkerResolver& markerResolver, int recursionCounter) const; | |||
| double getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const; | |||
| const String checkName (const String& name) const; | |||
| const String getOriginMarkerName() const; | |||
| const String getExtentMarkerName() const; | |||
| static bool isOrigin (const String& name); | |||
| static void skipWhitespace (const String& s, int& i); | |||
| static const String readMarkerName (const String& s, int& i); | |||
| static double readNumber (const String& s, int& i); | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Describes a rectangle as a set of Coordinate values. | |||
| */ | |||
| class RectangleCoordinates | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| RectangleCoordinates(); | |||
| explicit RectangleCoordinates (const Rectangle<int>& rect); | |||
| explicit RectangleCoordinates (const String& stringVersion); | |||
| //============================================================================== | |||
| const Rectangle<int> resolve (Coordinate::MarkerResolver& markerResolver) const; | |||
| bool isRecursive (Coordinate::MarkerResolver& markerResolver) const; | |||
| void moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver); | |||
| const String toString() const; | |||
| Coordinate left, right, top, bottom; | |||
| }; | |||
| #endif // __JUCER_COORDINATE_H_EF56ACFA__ | |||
| @@ -164,7 +164,7 @@ private: | |||
| void writeLinkerFlags (OutputStream& out, const Project::BuildConfiguration& config) | |||
| { | |||
| out << " LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows"; | |||
| out << " LDFLAGS += -L$(BINDIR) -L$(LIBDIR)"; | |||
| { | |||
| Array<RelativePath> libraryPaths; | |||
| @@ -159,112 +159,6 @@ private: | |||
| Rectangle<int> textArea; | |||
| }; | |||
| //============================================================================== | |||
| static const double tickSizes[] = { 1.0, 2.0, 5.0, | |||
| 10.0, 20.0, 50.0, | |||
| 100.0, 200.0, 500.0, 1000.0 }; | |||
| class TickIterator | |||
| { | |||
| public: | |||
| TickIterator (const double startValue_, const double endValue_, const double valuePerPixel_, | |||
| int minPixelsPerTick, int minWidthForLabels) | |||
| : startValue (startValue_), | |||
| endValue (endValue_), | |||
| valuePerPixel (valuePerPixel_) | |||
| { | |||
| tickLevelIndex = findLevelIndexForValue (valuePerPixel * minPixelsPerTick); | |||
| labelLevelIndex = findLevelIndexForValue (valuePerPixel * minWidthForLabels); | |||
| tickPosition = pixelsToValue (-minWidthForLabels); | |||
| tickPosition = snapValueDown (tickPosition, tickLevelIndex); | |||
| } | |||
| bool getNextTick (float& pixelX, float& tickLength, String& label) | |||
| { | |||
| const double tickUnits = tickSizes [tickLevelIndex]; | |||
| tickPosition += tickUnits; | |||
| const int totalLevels = sizeof (tickSizes) / sizeof (*tickSizes); | |||
| int highestIndex = tickLevelIndex; | |||
| while (++highestIndex < totalLevels) | |||
| { | |||
| const double ticksAtThisLevel = tickPosition / tickSizes [highestIndex]; | |||
| if (fabs (ticksAtThisLevel - floor (ticksAtThisLevel + 0.5)) > 0.000001) | |||
| break; | |||
| } | |||
| --highestIndex; | |||
| if (highestIndex >= labelLevelIndex) | |||
| label = getDescriptionOfValue (tickPosition, labelLevelIndex); | |||
| else | |||
| label = String::empty; | |||
| tickLength = (highestIndex + 1 - tickLevelIndex) / (float) (totalLevels + 1 - tickLevelIndex); | |||
| pixelX = valueToPixels (tickPosition); | |||
| return tickPosition < endValue; | |||
| } | |||
| private: | |||
| double tickPosition; | |||
| int tickLevelIndex, labelLevelIndex; | |||
| const double startValue, endValue, valuePerPixel; | |||
| int findLevelIndexForValue (const double value) const | |||
| { | |||
| int i; | |||
| for (i = 0; i < sizeof (tickSizes) / sizeof (*tickSizes); ++i) | |||
| if (tickSizes [i] >= value) | |||
| break; | |||
| return i; | |||
| } | |||
| double pixelsToValue (int pixels) const | |||
| { | |||
| return startValue + pixels * valuePerPixel; | |||
| } | |||
| float valueToPixels (double value) const | |||
| { | |||
| return (float) ((value - startValue) / valuePerPixel); | |||
| } | |||
| static double snapValueToNearest (const double t, const int valueLevelIndex) | |||
| { | |||
| const double unitsPerInterval = tickSizes [valueLevelIndex]; | |||
| return unitsPerInterval * floor (t / unitsPerInterval + 0.5); | |||
| } | |||
| static double snapValueDown (const double t, const int valueLevelIndex) | |||
| { | |||
| const double unitsPerInterval = tickSizes [valueLevelIndex]; | |||
| return unitsPerInterval * floor (t / unitsPerInterval); | |||
| } | |||
| static inline int roundDoubleToInt (const double value) | |||
| { | |||
| union { int asInt[2]; double asDouble; } n; | |||
| n.asDouble = value + 6755399441055744.0; | |||
| #if TARGET_RT_BIG_ENDIAN | |||
| return n.asInt [1]; | |||
| #else | |||
| return n.asInt [0]; | |||
| #endif | |||
| } | |||
| static const String getDescriptionOfValue (const double value, const int valueLevelIndex) | |||
| { | |||
| return String (roundToInt (value)); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class ComponentEditor::Canvas : public Component, | |||
| public ValueTree::Listener, | |||
| @@ -287,6 +181,7 @@ public: | |||
| ~Canvas() | |||
| { | |||
| dragger = 0; | |||
| getDocument().getRoot().removeListener (this); | |||
| componentHolder->deleteAllChildren(); | |||
| deleteAllChildren(); | |||
| @@ -508,10 +403,260 @@ public: | |||
| void showSizeGuides() { overlay->showSizeGuides(); } | |||
| void hideSizeGuides() { overlay->hideSizeGuides(); } | |||
| //============================================================================== | |||
| class DragOperation | |||
| { | |||
| public: | |||
| DragOperation (Canvas& canvas_, | |||
| const Array<Component*>& items, | |||
| const MouseEvent& e, | |||
| const ResizableBorderComponent::Zone& zone_) | |||
| : canvas (canvas_), | |||
| zone (zone_) | |||
| { | |||
| for (int i = 0; i < items.size(); ++i) | |||
| { | |||
| Component* comp = items.getUnchecked(i); | |||
| jassert (comp != 0); | |||
| const ValueTree v (getDocument().getComponentState (comp)); | |||
| draggedComponents.add (v); | |||
| Rectangle<int> pos; | |||
| { | |||
| RectangleCoordinates relativePos (getDocument().getCoordsFor (v)); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (getDocument().createMarkerResolver (v)); | |||
| pos = relativePos.resolve (*markers); | |||
| originalPositions.add (pos); | |||
| } | |||
| const Rectangle<float> floatPos ((float) pos.getX(), (float) pos.getY(), | |||
| (float) pos.getWidth(), (float) pos.getHeight()); | |||
| 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())); | |||
| } | |||
| 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)); | |||
| 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)); | |||
| getDocument().beginNewTransaction(); | |||
| } | |||
| ~DragOperation() | |||
| { | |||
| getDocument().beginNewTransaction(); | |||
| } | |||
| struct SnapLine | |||
| { | |||
| SnapLine (float position_, float start_, float end_) | |||
| : position (position_), start (start_), end (end_) | |||
| {} | |||
| float position, start, end; | |||
| }; | |||
| class SnapGuideComponent : public Component | |||
| { | |||
| public: | |||
| SnapGuideComponent (const SnapLine& line_, bool isVertical_, Component* parent) | |||
| : line (line_), isVertical (isVertical_) | |||
| { | |||
| if (isVertical) | |||
| setBounds (roundToInt (line.position), roundToInt (line.start), 1, roundToInt (line.end - line.start)); | |||
| else | |||
| setBounds (roundToInt (line.start), roundToInt (line.position), roundToInt (line.end - line.start), 1); | |||
| parent->addAndMakeVisible (this); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.fillAll (Colours::blue.withAlpha (0.3f)); | |||
| } | |||
| private: | |||
| const SnapLine line; | |||
| const bool isVertical; | |||
| SnapGuideComponent (const SnapGuideComponent&); | |||
| SnapGuideComponent& operator= (const SnapGuideComponent&); | |||
| }; | |||
| void drag (const MouseEvent& e) | |||
| { | |||
| const float snapThreshold = 8.0f; | |||
| getDocument().getUndoManager()->undoCurrentTransactionOnly(); | |||
| Point<int> distance (e.getOffsetFromDragStart()); | |||
| snapGuides.clear(); | |||
| SnapLine snap (0, 0, 0); | |||
| const float snapX = findBestSnapDistance (verticalSnapTargets, getVerticalSnapPositions (distance), snap); | |||
| if (fabsf (snapX) < snapThreshold) | |||
| { | |||
| distance = Point<int> (distance.getX() + snapX, distance.getY()); | |||
| if (snap.position != 0) | |||
| snapGuides.add (new SnapGuideComponent (snap, true, canvas.overlay)); | |||
| } | |||
| const float snapY = findBestSnapDistance (horizontalSnapTargets, getHorizontalSnapPositions (distance), snap); | |||
| if (fabsf (snapY) < snapThreshold) | |||
| { | |||
| distance = Point<int> (distance.getX(), distance.getY() + snapY); | |||
| if (snap.position != 0) | |||
| snapGuides.add (new SnapGuideComponent (snap, false, canvas.overlay)); | |||
| } | |||
| 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<int>& distance, const Rectangle<int>& originalPos) | |||
| { | |||
| const Rectangle<int> newBounds (zone.resizeRectangleBy (originalPos, distance)); | |||
| RectangleCoordinates pr (getDocument().getCoordsFor (v)); | |||
| ScopedPointer<Coordinate::MarkerResolver> markers (getDocument().createMarkerResolver (v)); | |||
| pr.moveToAbsolute (newBounds, *markers); | |||
| return getDocument().setCoordsFor (v, pr); | |||
| } | |||
| const Array<SnapLine> getVerticalSnapPositions (const Point<int>& distance) const | |||
| { | |||
| Array<SnapLine> p (verticalSnapPositions); | |||
| for (int i = p.size(); --i >= 0;) | |||
| p.getReference(i).position += distance.getX(); | |||
| return p; | |||
| } | |||
| const Array<SnapLine> getHorizontalSnapPositions (const Point<int>& distance) const | |||
| { | |||
| Array<SnapLine> p (horizontalSnapPositions); | |||
| for (int i = p.size(); --i >= 0;) | |||
| p.getReference(i).position += distance.getY(); | |||
| return p; | |||
| } | |||
| static float findBestSnapDistance (const Array<SnapLine>& targets, const Array<SnapLine>& sources, SnapLine& line) | |||
| { | |||
| if (targets.size() == 0 || sources.size() == 0) | |||
| return 0.0f; | |||
| float best = 1.0e10f; | |||
| float absBest = fabsf (best); | |||
| line = SnapLine (1.0e10f, 0, 0); | |||
| for (int i = 0; i < targets.size(); ++i) | |||
| { | |||
| for (int j = 0; j < sources.size(); ++j) | |||
| { | |||
| SnapLine& target = targets.getReference(i); | |||
| SnapLine& source = sources.getReference(j); | |||
| const float diff = target.position - source.position; | |||
| const float absDiff = fabsf (diff); | |||
| if (absDiff < absBest) | |||
| { | |||
| line = SnapLine (target.position, jmin (target.start, source.start), jmax (target.end, source.end)); | |||
| best = diff; | |||
| absBest = absDiff; | |||
| } | |||
| } | |||
| } | |||
| jassert (absBest < 1.0e10f); | |||
| return best; | |||
| } | |||
| private: | |||
| Canvas& canvas; | |||
| Array <ValueTree> draggedComponents; | |||
| Array <Rectangle<int> > originalPositions; | |||
| Array <SnapLine> verticalSnapPositions, horizontalSnapPositions; | |||
| Array <SnapLine> verticalSnapTargets, horizontalSnapTargets; | |||
| const ResizableBorderComponent::Zone zone; | |||
| OwnedArray<Component> snapGuides; | |||
| ComponentDocument& getDocument() throw() { return canvas.getDocument(); } | |||
| }; | |||
| void beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone) | |||
| { | |||
| dragger = new DragOperation (*this, getSelectedComps(), 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 <DragOperation> dragger; | |||
| ResizableBorderComponent::Zone dragZone; | |||
| int dragStartWidth, dragStartHeight; | |||
| @@ -571,7 +716,7 @@ private: | |||
| if (component != 0) | |||
| { | |||
| updateDragZone (e.getPosition()); | |||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(), dragZone); | |||
| canvas.beginDrag (e, dragZone); | |||
| canvas.showSizeGuides(); | |||
| } | |||
| } | |||
| @@ -579,7 +724,7 @@ private: | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if (component != 0) | |||
| canvas.getDocument().continueDrag (e); | |||
| canvas.continueDrag (e); | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| @@ -587,7 +732,7 @@ private: | |||
| canvas.hideSizeGuides(); | |||
| if (component != 0) | |||
| canvas.getDocument().endDrag (e); | |||
| canvas.endDrag (e); | |||
| updateDragZone (e.getPosition()); | |||
| } | |||
| @@ -720,11 +865,10 @@ private: | |||
| { | |||
| isDraggingClickedComp = true; | |||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | |||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(), | |||
| ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | |||
| canvas.beginDrag (e, ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | |||
| } | |||
| canvas.getDocument().continueDrag (e); | |||
| canvas.continueDrag (e); | |||
| showSizeGuides(); | |||
| } | |||
| } | |||
| @@ -747,7 +891,7 @@ private: | |||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult); | |||
| } | |||
| canvas.getDocument().endDrag (e); | |||
| canvas.endDrag (e); | |||
| } | |||
| void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height) | |||
| @@ -503,363 +503,3 @@ int indexOfLineStartingWith (const StringArray& lines, const String& text, int s | |||
| return -1; | |||
| } | |||
| //============================================================================== | |||
| const char* Coordinate::parentLeftMarkerName = "parent.left"; | |||
| const char* Coordinate::parentRightMarkerName = "parent.right"; | |||
| const char* Coordinate::parentTopMarkerName = "parent.top"; | |||
| const char* Coordinate::parentBottomMarkerName = "parent.bottom"; | |||
| Coordinate::Coordinate (bool isHorizontal_) | |||
| : value (0), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal_) | |||
| : value (absoluteDistanceFromOrigin), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double absoluteDistance, const String& source, bool isHorizontal_) | |||
| : anchor1 (source), value (absoluteDistance), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::Coordinate (double relativeProportion, const String& pos1, const String& pos2, bool isHorizontal_) | |||
| : anchor1 (pos1), anchor2 (pos2), value (relativeProportion), isProportion (true), isHorizontal (isHorizontal_) | |||
| { | |||
| } | |||
| Coordinate::~Coordinate() | |||
| { | |||
| } | |||
| const Coordinate Coordinate::getAnchorPoint1() const | |||
| { | |||
| return Coordinate (0.0, anchor1, isHorizontal); | |||
| } | |||
| const Coordinate Coordinate::getAnchorPoint2() const | |||
| { | |||
| return Coordinate (0.0, anchor2, isHorizontal); | |||
| } | |||
| bool Coordinate::isOrigin (const String& name) | |||
| { | |||
| return name.isEmpty() || name == parentLeftMarkerName || name == parentTopMarkerName; | |||
| } | |||
| const String Coordinate::getOriginMarkerName() const | |||
| { | |||
| return isHorizontal ? parentLeftMarkerName : parentTopMarkerName; | |||
| } | |||
| const String Coordinate::getExtentMarkerName() const | |||
| { | |||
| return isHorizontal ? parentRightMarkerName : parentBottomMarkerName; | |||
| } | |||
| const String Coordinate::checkName (const String& name) const | |||
| { | |||
| return name.isEmpty() ? getOriginMarkerName() : name; | |||
| } | |||
| double Coordinate::getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const | |||
| { | |||
| if (isOrigin (name)) | |||
| return 0.0; | |||
| return markerResolver.findMarker (name, isHorizontal) | |||
| .resolve (markerResolver, recursionCounter + 1); | |||
| } | |||
| struct RecursivePositionException | |||
| { | |||
| }; | |||
| double Coordinate::resolve (MarkerResolver& markerResolver, int recursionCounter) const | |||
| { | |||
| if (recursionCounter > 100) | |||
| { | |||
| jassertfalse | |||
| throw RecursivePositionException(); | |||
| } | |||
| const double pos1 = getPosition (anchor1, markerResolver, recursionCounter); | |||
| return isProportion ? pos1 + (getPosition (anchor2, markerResolver, recursionCounter) - pos1) * value | |||
| : pos1 + value; | |||
| } | |||
| double Coordinate::resolve (MarkerResolver& markerResolver) const | |||
| { | |||
| try | |||
| { | |||
| return resolve (markerResolver, 0); | |||
| } | |||
| catch (RecursivePositionException&) | |||
| {} | |||
| return 0.0; | |||
| } | |||
| void Coordinate::moveToAbsolute (double newPos, MarkerResolver& markerResolver) | |||
| { | |||
| try | |||
| { | |||
| const double pos1 = getPosition (anchor1, markerResolver, 0); | |||
| if (isProportion) | |||
| { | |||
| const double size = getPosition (anchor2, markerResolver, 0) - pos1; | |||
| if (size != 0) | |||
| value = (newPos - pos1) / size; | |||
| } | |||
| else | |||
| { | |||
| value = newPos - pos1; | |||
| } | |||
| } | |||
| catch (RecursivePositionException&) | |||
| {} | |||
| } | |||
| bool Coordinate::isRecursive (MarkerResolver& markerResolver) const | |||
| { | |||
| try | |||
| { | |||
| resolve (markerResolver, 0); | |||
| } | |||
| catch (RecursivePositionException&) | |||
| { | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void Coordinate::skipWhitespace (const String& s, int& i) | |||
| { | |||
| while (CharacterFunctions::isWhitespace (s[i])) | |||
| ++i; | |||
| } | |||
| const String Coordinate::readMarkerName (const String& s, int& i) | |||
| { | |||
| skipWhitespace (s, i); | |||
| if (CharacterFunctions::isLetter (s[i]) || s[i] == '_') | |||
| { | |||
| int start = i; | |||
| while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.') | |||
| ++i; | |||
| return s.substring (start, i); | |||
| } | |||
| return String::empty; | |||
| } | |||
| double Coordinate::readNumber (const String& s, int& i) | |||
| { | |||
| skipWhitespace (s, i); | |||
| int start = i; | |||
| if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-') | |||
| ++i; | |||
| while (CharacterFunctions::isDigit (s[i]) || s[i] == '.') | |||
| ++i; | |||
| if ((s[i] == 'e' || s[i] == 'E') | |||
| && (CharacterFunctions::isDigit (s[i + 1]) | |||
| || s[i + 1] == '-' | |||
| || s[i + 1] == '+')) | |||
| { | |||
| i += 2; | |||
| while (CharacterFunctions::isDigit (s[i])) | |||
| ++i; | |||
| } | |||
| const double value = s.substring (start, i).getDoubleValue(); | |||
| while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',') | |||
| ++i; | |||
| return value; | |||
| } | |||
| Coordinate::Coordinate (const String& s, bool isHorizontal_) | |||
| : value (0), isProportion (false), isHorizontal (isHorizontal_) | |||
| { | |||
| int i = 0; | |||
| anchor1 = readMarkerName (s, i); | |||
| if (anchor1.isNotEmpty()) | |||
| { | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '+') | |||
| value = readNumber (s, ++i); | |||
| else if (s[i] == '-') | |||
| value = -readNumber (s, ++i); | |||
| } | |||
| else | |||
| { | |||
| value = readNumber (s, i); | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '%') | |||
| { | |||
| isProportion = true; | |||
| value /= 100.0; | |||
| skipWhitespace (s, ++i); | |||
| if (s[i] == '*') | |||
| { | |||
| anchor1 = readMarkerName (s, ++i); | |||
| skipWhitespace (s, i); | |||
| if (s[i] == '-' && s[i + 1] == '>') | |||
| { | |||
| i += 2; | |||
| anchor2 = readMarkerName (s, i); | |||
| } | |||
| else | |||
| { | |||
| anchor2 = anchor1; | |||
| anchor1 = getOriginMarkerName(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| anchor1 = getOriginMarkerName(); | |||
| anchor2 = getExtentMarkerName(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| const String Coordinate::toString() const | |||
| { | |||
| if (isProportion) | |||
| { | |||
| const String percent (value * 100.0); | |||
| if (isOrigin (anchor1)) | |||
| { | |||
| if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName) | |||
| return percent + "%"; | |||
| else | |||
| return percent + "% * " + checkName (anchor2); | |||
| } | |||
| else | |||
| return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2); | |||
| } | |||
| else | |||
| { | |||
| if (isOrigin (anchor1)) | |||
| return String (value); | |||
| else if (value > 0) | |||
| return checkName (anchor1) + " + " + String (value); | |||
| else if (value < 0) | |||
| return checkName (anchor1) + " - " + String (-value); | |||
| else | |||
| return checkName (anchor1); | |||
| } | |||
| } | |||
| const double Coordinate::getEditableValue() const | |||
| { | |||
| return isProportion ? value * 100.0 : value; | |||
| } | |||
| void Coordinate::setEditableValue (const double newValue) | |||
| { | |||
| value = isProportion ? newValue / 100.0 : newValue; | |||
| } | |||
| void Coordinate::toggleProportionality (MarkerResolver& markerResolver) | |||
| { | |||
| const double oldValue = resolve (markerResolver); | |||
| isProportion = ! isProportion; | |||
| anchor1 = getOriginMarkerName(); | |||
| anchor2 = getExtentMarkerName(); | |||
| moveToAbsolute (oldValue, markerResolver); | |||
| } | |||
| void Coordinate::changeAnchor1 (const String& newMarkerName, MarkerResolver& markerResolver) | |||
| { | |||
| const double oldValue = resolve (markerResolver); | |||
| anchor1 = newMarkerName; | |||
| moveToAbsolute (oldValue, markerResolver); | |||
| } | |||
| void Coordinate::changeAnchor2 (const String& newMarkerName, MarkerResolver& markerResolver) | |||
| { | |||
| const double oldValue = resolve (markerResolver); | |||
| anchor2 = newMarkerName; | |||
| moveToAbsolute (oldValue, markerResolver); | |||
| } | |||
| //============================================================================== | |||
| RectangleCoordinates::RectangleCoordinates() | |||
| : left (true), right (true), top (false), bottom (false) | |||
| { | |||
| } | |||
| RectangleCoordinates::RectangleCoordinates (const Rectangle<int>& rect) | |||
| : left (rect.getX(), true), | |||
| right (rect.getWidth(), "left", true), | |||
| top (rect.getY(), false), | |||
| bottom (rect.getHeight(), "top", false) | |||
| { | |||
| } | |||
| RectangleCoordinates::RectangleCoordinates (const String& stringVersion) | |||
| : left (true), right (true), top (false), bottom (false) | |||
| { | |||
| StringArray tokens; | |||
| tokens.addTokens (stringVersion, ",", String::empty); | |||
| left = Coordinate (tokens [0], true); | |||
| top = Coordinate (tokens [1], false); | |||
| right = Coordinate (tokens [2], true); | |||
| bottom = Coordinate (tokens [3], false); | |||
| } | |||
| bool RectangleCoordinates::isRecursive (Coordinate::MarkerResolver& markerResolver) const | |||
| { | |||
| return left.isRecursive (markerResolver) || right.isRecursive (markerResolver) | |||
| || top.isRecursive (markerResolver) || bottom.isRecursive (markerResolver); | |||
| } | |||
| const Rectangle<int> RectangleCoordinates::resolve (Coordinate::MarkerResolver& markerResolver) const | |||
| { | |||
| const int l = roundToInt (left.resolve (markerResolver)); | |||
| const int r = roundToInt (right.resolve (markerResolver)); | |||
| const int t = roundToInt (top.resolve (markerResolver)); | |||
| const int b = roundToInt (bottom.resolve (markerResolver)); | |||
| return Rectangle<int> (l, t, r - l, b - t); | |||
| } | |||
| void RectangleCoordinates::moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver) | |||
| { | |||
| left.moveToAbsolute (newPos.getX(), markerResolver); | |||
| right.moveToAbsolute (newPos.getRight(), markerResolver); | |||
| top.moveToAbsolute (newPos.getY(), markerResolver); | |||
| bottom.moveToAbsolute (newPos.getBottom(), markerResolver); | |||
| } | |||
| const String RectangleCoordinates::toString() const | |||
| { | |||
| return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString(); | |||
| } | |||
| @@ -109,127 +109,107 @@ private: | |||
| int64 fileHashCode, fileSize; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Holds a co-ordinate along the x or y axis, expressed either as an absolute | |||
| position, or relative to other named marker positions. | |||
| */ | |||
| class Coordinate | |||
| static const double tickSizes[] = { 1.0, 2.0, 5.0, | |||
| 10.0, 20.0, 50.0, | |||
| 100.0, 200.0, 500.0, 1000.0 }; | |||
| class TickIterator | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a zero coordinate. */ | |||
| explicit Coordinate (bool isHorizontal); | |||
| TickIterator (const double startValue_, const double endValue_, const double valuePerPixel_, | |||
| int minPixelsPerTick, int minWidthForLabels) | |||
| : startValue (startValue_), | |||
| endValue (endValue_), | |||
| valuePerPixel (valuePerPixel_) | |||
| { | |||
| tickLevelIndex = findLevelIndexForValue (valuePerPixel * minPixelsPerTick); | |||
| labelLevelIndex = findLevelIndexForValue (valuePerPixel * minWidthForLabels); | |||
| tickPosition = pixelsToValue (-minWidthForLabels); | |||
| tickPosition = snapValueDown (tickPosition, tickLevelIndex); | |||
| } | |||
| /** Recreates a coordinate from its stringified version. */ | |||
| Coordinate (const String& stringVersion, bool isHorizontal); | |||
| bool getNextTick (float& pixelX, float& tickLength, String& label) | |||
| { | |||
| const double tickUnits = tickSizes [tickLevelIndex]; | |||
| tickPosition += tickUnits; | |||
| /** Creates an absolute position from the parent origin. */ | |||
| Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal); | |||
| const int totalLevels = sizeof (tickSizes) / sizeof (*tickSizes); | |||
| int highestIndex = tickLevelIndex; | |||
| /** Creates an absolute position relative to a named marker. */ | |||
| Coordinate (double absolutePosition, const String& relativeToMarker, bool isHorizontal); | |||
| while (++highestIndex < totalLevels) | |||
| { | |||
| const double ticksAtThisLevel = tickPosition / tickSizes [highestIndex]; | |||
| /** Creates a relative position between two named markers. */ | |||
| Coordinate (double relativePosition, const String& marker1, const String& marker2, bool isHorizontal); | |||
| if (fabs (ticksAtThisLevel - floor (ticksAtThisLevel + 0.5)) > 0.000001) | |||
| break; | |||
| } | |||
| /** Destructor. */ | |||
| ~Coordinate(); | |||
| --highestIndex; | |||
| //============================================================================== | |||
| /** | |||
| Provides an interface for looking up the position of a named marker. | |||
| */ | |||
| class MarkerResolver | |||
| { | |||
| public: | |||
| virtual ~MarkerResolver() {} | |||
| virtual const Coordinate findMarker (const String& name, bool isHorizontal) = 0; | |||
| }; | |||
| /** Calculates the absolute position of this co-ordinate. */ | |||
| double resolve (MarkerResolver& markerResolver) const; | |||
| /** Returns true if this co-ordinate is expressed in terms of markers that form a recursive loop. */ | |||
| bool isRecursive (MarkerResolver& markerResolver) const; | |||
| /** Changes the value of this marker to make it resolve to the specified position. */ | |||
| void moveToAbsolute (double newPos, MarkerResolver& markerResolver); | |||
| const Coordinate getAnchorPoint1() const; | |||
| const Coordinate getAnchorPoint2() const; | |||
| const double getEditableValue() const; | |||
| void setEditableValue (const double newValue); | |||
| bool isProportional() const throw() { return isProportion; } | |||
| void toggleProportionality (MarkerResolver& markerResolver); | |||
| const String getAnchor1() const { return anchor1; } | |||
| void changeAnchor1 (const String& newMarkerName, MarkerResolver& markerResolver); | |||
| const String getAnchor2() const { return anchor2; } | |||
| void changeAnchor2 (const String& newMarkerName, MarkerResolver& markerResolver); | |||
| //============================================================================== | |||
| /* | |||
| Position string formats: | |||
| 123 = absolute pixels from parent origin | |||
| marker | |||
| marker + 123 | |||
| marker - 123 | |||
| 50% = percentage between parent origin and parent extent | |||
| 50% * marker = percentage between parent origin and marker | |||
| 50% * marker1 -> marker2 = percentage between two markers | |||
| standard marker names: | |||
| "origin" = parent origin | |||
| "size" = parent right or bottom | |||
| "top", "left", "bottom", "right" = refer to the component's own left, right, top and bottom. | |||
| */ | |||
| const String toString() const; | |||
| //============================================================================== | |||
| static const char* parentLeftMarkerName; | |||
| static const char* parentRightMarkerName; | |||
| static const char* parentTopMarkerName; | |||
| static const char* parentBottomMarkerName; | |||
| if (highestIndex >= labelLevelIndex) | |||
| label = getDescriptionOfValue (tickPosition, labelLevelIndex); | |||
| else | |||
| label = String::empty; | |||
| tickLength = (highestIndex + 1 - tickLevelIndex) / (float) (totalLevels + 1 - tickLevelIndex); | |||
| pixelX = valueToPixels (tickPosition); | |||
| return tickPosition < endValue; | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| String anchor1, anchor2; | |||
| double value; | |||
| bool isProportion, isHorizontal; | |||
| double resolve (MarkerResolver& markerResolver, int recursionCounter) const; | |||
| double getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const; | |||
| const String checkName (const String& name) const; | |||
| const String getOriginMarkerName() const; | |||
| const String getExtentMarkerName() const; | |||
| static bool isOrigin (const String& name); | |||
| static void skipWhitespace (const String& s, int& i); | |||
| static const String readMarkerName (const String& s, int& i); | |||
| static double readNumber (const String& s, int& i); | |||
| }; | |||
| double tickPosition; | |||
| int tickLevelIndex, labelLevelIndex; | |||
| const double startValue, endValue, valuePerPixel; | |||
| //============================================================================== | |||
| /** | |||
| Describes a rectangle as a set of Coordinate values. | |||
| */ | |||
| class RectangleCoordinates | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| RectangleCoordinates(); | |||
| explicit RectangleCoordinates (const Rectangle<int>& rect); | |||
| explicit RectangleCoordinates (const String& stringVersion); | |||
| //============================================================================== | |||
| const Rectangle<int> resolve (Coordinate::MarkerResolver& markerResolver) const; | |||
| bool isRecursive (Coordinate::MarkerResolver& markerResolver) const; | |||
| void moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver); | |||
| const String toString() const; | |||
| Coordinate left, right, top, bottom; | |||
| int findLevelIndexForValue (const double value) const | |||
| { | |||
| int i; | |||
| for (i = 0; i < sizeof (tickSizes) / sizeof (*tickSizes); ++i) | |||
| if (tickSizes [i] >= value) | |||
| break; | |||
| return i; | |||
| } | |||
| double pixelsToValue (int pixels) const | |||
| { | |||
| return startValue + pixels * valuePerPixel; | |||
| } | |||
| float valueToPixels (double value) const | |||
| { | |||
| return (float) ((value - startValue) / valuePerPixel); | |||
| } | |||
| static double snapValueToNearest (const double t, const int valueLevelIndex) | |||
| { | |||
| const double unitsPerInterval = tickSizes [valueLevelIndex]; | |||
| return unitsPerInterval * floor (t / unitsPerInterval + 0.5); | |||
| } | |||
| static double snapValueDown (const double t, const int valueLevelIndex) | |||
| { | |||
| const double unitsPerInterval = tickSizes [valueLevelIndex]; | |||
| return unitsPerInterval * floor (t / unitsPerInterval); | |||
| } | |||
| static inline int roundDoubleToInt (const double value) | |||
| { | |||
| union { int asInt[2]; double asDouble; } n; | |||
| n.asDouble = value + 6755399441055744.0; | |||
| #if TARGET_RT_BIG_ENDIAN | |||
| return n.asInt [1]; | |||
| #else | |||
| return n.asInt [0]; | |||
| #endif | |||
| } | |||
| static const String getDescriptionOfValue (const double value, const int valueLevelIndex) | |||
| { | |||
| return String (roundToInt (value)); | |||
| } | |||
| }; | |||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := amalgamator | |||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := amalgamator | |||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := Plugin\ Host | |||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := Plugin\ Host | |||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := BinaryBuilder | |||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := BinaryBuilder | |||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := HelloWorld | |||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := HelloWorld | |||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := JuceDemo | |||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||
| OUTDIR := build | |||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| CXXFLAGS += $(CFLAGS) | |||
| LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -L"/usr/X11R6/lib/" -L"../../../../../juce/bin" -lfreetype -lpthread -lrt -lX11 -lGL -lGLU -lXinerama -lasound | |||
| LDDEPS := | |||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | |||
| TARGET := JuceDemo | |||
| @@ -10189,7 +10189,8 @@ public: | |||
| static juce_wchar* createCopy (const char* const src, const size_t numChars) | |||
| { | |||
| juce_wchar* const dest = createUninitialised (numChars); | |||
| CharacterFunctions::copy (dest, src, numChars + 1); | |||
| CharacterFunctions::copy (dest, src, numChars); | |||
| dest [numChars] = 0; | |||
| return dest; | |||
| } | |||
| @@ -17924,7 +17925,11 @@ FileBasedDocument::~FileBasedDocument() | |||
| void FileBasedDocument::setChangedFlag (const bool hasChanged) | |||
| { | |||
| changedSinceSave = hasChanged; | |||
| if (changedSinceSave != hasChanged) | |||
| { | |||
| changedSinceSave = hasChanged; | |||
| sendChangeMessage (this); | |||
| } | |||
| } | |||
| void FileBasedDocument::changed() | |||
| @@ -17938,7 +17943,7 @@ void FileBasedDocument::setFile (const File& newFile) | |||
| if (documentFile != newFile) | |||
| { | |||
| documentFile = newFile; | |||
| changedSinceSave = true; | |||
| changed(); | |||
| } | |||
| } | |||
| @@ -48452,12 +48457,17 @@ void Slider::setTextValueSuffix (const String& suffix) | |||
| } | |||
| } | |||
| const String Slider::getTextValueSuffix() const | |||
| { | |||
| return textSuffix; | |||
| } | |||
| const String Slider::getTextFromValue (double v) | |||
| { | |||
| if (numDecimalPlaces > 0) | |||
| return String (v, numDecimalPlaces) + textSuffix; | |||
| if (getNumDecimalPlacesToDisplay() > 0) | |||
| return String (v, getNumDecimalPlacesToDisplay()) + getTextValueSuffix(); | |||
| else | |||
| return String (roundToInt (v)) + textSuffix; | |||
| return String (roundToInt (v)) + getTextValueSuffix(); | |||
| } | |||
| double Slider::getValueFromText (const String& text) | |||
| @@ -43,7 +43,7 @@ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 51 | |||
| #define JUCE_BUILDNUMBER 14 | |||
| #define JUCE_BUILDNUMBER 15 | |||
| #define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) | |||
| @@ -21367,6 +21367,8 @@ public: | |||
| void setTextValueSuffix (const String& suffix); | |||
| const String getTextValueSuffix() const; | |||
| virtual double proportionOfLengthToValue (double proportion); | |||
| virtual double valueToProportionOfLength (double value); | |||
| @@ -21415,6 +21417,8 @@ protected: | |||
| void colourChanged(); | |||
| void valueChanged (Value& value); | |||
| int getNumDecimalPlacesToDisplay() const throw() { return numDecimalPlaces; } | |||
| private: | |||
| ListenerList <SliderListener> listeners; | |||
| Value currentValue, valueMin, valueMax; | |||
| @@ -47,7 +47,7 @@ public: | |||
| /** If the current value of destination is equal to requiredCurrentValue, this | |||
| will set it to newValue; otherwise, it will leave it unchanged. | |||
| @returns the new value of destination | |||
| @returns the original value of destination | |||
| */ | |||
| static int32 compareAndExchange (int32& destination, int32 newValue, int32 requiredCurrentValue); | |||
| @@ -33,7 +33,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 51 | |||
| #define JUCE_BUILDNUMBER 14 | |||
| #define JUCE_BUILDNUMBER 15 | |||
| /** Current Juce version number. | |||
| @@ -632,12 +632,17 @@ void Slider::setTextValueSuffix (const String& suffix) | |||
| } | |||
| } | |||
| const String Slider::getTextValueSuffix() const | |||
| { | |||
| return textSuffix; | |||
| } | |||
| const String Slider::getTextFromValue (double v) | |||
| { | |||
| if (numDecimalPlaces > 0) | |||
| return String (v, numDecimalPlaces) + textSuffix; | |||
| if (getNumDecimalPlacesToDisplay() > 0) | |||
| return String (v, getNumDecimalPlacesToDisplay()) + getTextValueSuffix(); | |||
| else | |||
| return String (roundToInt (v)) + textSuffix; | |||
| return String (roundToInt (v)) + getTextValueSuffix(); | |||
| } | |||
| double Slider::getValueFromText (const String& text) | |||
| @@ -620,6 +620,9 @@ public: | |||
| */ | |||
| void setTextValueSuffix (const String& suffix); | |||
| /** Returns the suffix that was set by setTextValueSuffix(). */ | |||
| const String getTextValueSuffix() const; | |||
| //============================================================================== | |||
| /** Allows a user-defined mapping of distance along the slider to its value. | |||
| @@ -748,6 +751,11 @@ protected: | |||
| /** @internal */ | |||
| void valueChanged (Value& value); | |||
| /** Returns the best number of decimal places to use when displaying numbers. | |||
| This is calculated from the slider's interval setting. | |||
| */ | |||
| int getNumDecimalPlacesToDisplay() const throw() { return numDecimalPlaces; } | |||
| private: | |||
| ListenerList <SliderListener> listeners; | |||
| Value currentValue, valueMin, valueMax; | |||
| @@ -78,7 +78,8 @@ public: | |||
| static juce_wchar* createCopy (const char* const src, const size_t numChars) | |||
| { | |||
| juce_wchar* const dest = createUninitialised (numChars); | |||
| CharacterFunctions::copy (dest, src, numChars + 1); | |||
| CharacterFunctions::copy (dest, src, numChars); | |||
| dest [numChars] = 0; | |||
| return dest; | |||
| } | |||
| @@ -565,7 +565,7 @@ public: | |||
| /** Returns a copy of this string, having removed a specified set of characters from its start. | |||
| Characters are removed from the start of the string until it finds one that is not in the | |||
| specified set, and then it stops. | |||
| @param charactersToTrim the set of characters to remove. This must not be null. | |||
| @param charactersToTrim the set of characters to remove. | |||
| @see trim, trimStart, trimCharactersAtEnd | |||
| */ | |||
| const String trimCharactersAtStart (const String& charactersToTrim) const; | |||
| @@ -573,7 +573,7 @@ public: | |||
| /** Returns a copy of this string, having removed a specified set of characters from its end. | |||
| Characters are removed from the end of the string until it finds one that is not in the | |||
| specified set, and then it stops. | |||
| @param charactersToTrim the set of characters to remove. This must not be null. | |||
| @param charactersToTrim the set of characters to remove. | |||
| @see trim, trimEnd, trimCharactersAtStart | |||
| */ | |||
| const String trimCharactersAtEnd (const String& charactersToTrim) const; | |||
| @@ -54,7 +54,11 @@ FileBasedDocument::~FileBasedDocument() | |||
| //============================================================================== | |||
| void FileBasedDocument::setChangedFlag (const bool hasChanged) | |||
| { | |||
| changedSinceSave = hasChanged; | |||
| if (changedSinceSave != hasChanged) | |||
| { | |||
| changedSinceSave = hasChanged; | |||
| sendChangeMessage (this); | |||
| } | |||
| } | |||
| void FileBasedDocument::changed() | |||
| @@ -69,7 +73,7 @@ void FileBasedDocument::setFile (const File& newFile) | |||
| if (documentFile != newFile) | |||
| { | |||
| documentFile = newFile; | |||
| changedSinceSave = true; | |||
| changed(); | |||
| } | |||
| } | |||