| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | 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 | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := Jucer | TARGET := Jucer | ||||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -O3 | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := Jucer | TARGET := Jucer | ||||
| @@ -44,6 +44,7 @@ endif | |||||
| OBJECTS := \ | OBJECTS := \ | ||||
| $(OBJDIR)/jucer_ComponentDocument.o \ | $(OBJDIR)/jucer_ComponentDocument.o \ | ||||
| $(OBJDIR)/jucer_Coordinate.o \ | |||||
| $(OBJDIR)/jucer_DrawableDocument.o \ | $(OBJDIR)/jucer_DrawableDocument.o \ | ||||
| $(OBJDIR)/jucer_NewFileWizard.o \ | $(OBJDIR)/jucer_NewFileWizard.o \ | ||||
| $(OBJDIR)/jucer_Project.o \ | $(OBJDIR)/jucer_Project.o \ | ||||
| @@ -92,6 +93,11 @@ $(OBJDIR)/jucer_ComponentDocument.o: ../../Source/model/jucer_ComponentDocument. | |||||
| @echo $(notdir $<) | @echo $(notdir $<) | ||||
| @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | @$(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 | $(OBJDIR)/jucer_DrawableDocument.o: ../../Source/model/jucer_DrawableDocument.cpp | ||||
| -@mkdir -p $(OBJDIR) | -@mkdir -p $(OBJDIR) | ||||
| @echo $(notdir $<) | @echo $(notdir $<) | ||||
| @@ -18,6 +18,7 @@ | |||||
| 93C9F3F27602A33DDC9C2250 = { isa = PBXBuildFile; fileRef = 2767E1D082874D301D5D5F43; }; | 93C9F3F27602A33DDC9C2250 = { isa = PBXBuildFile; fileRef = 2767E1D082874D301D5D5F43; }; | ||||
| 2E6836738CE7EB452FDC7E9A = { isa = PBXBuildFile; fileRef = D9FB1A5365FEEB854A0FF7BF; }; | 2E6836738CE7EB452FDC7E9A = { isa = PBXBuildFile; fileRef = D9FB1A5365FEEB854A0FF7BF; }; | ||||
| CD4226951C3F7FE19CF8A7CE = { isa = PBXBuildFile; fileRef = 2D6D6985B452EA0B67A18914; }; | CD4226951C3F7FE19CF8A7CE = { isa = PBXBuildFile; fileRef = 2D6D6985B452EA0B67A18914; }; | ||||
| 1DF9688E29753A0459E6C32A = { isa = PBXBuildFile; fileRef = 45C80436FD5A8438D0E6BE17; }; | |||||
| 1174D3512AF8207950094C56 = { isa = PBXBuildFile; fileRef = FF625CB50FB5C3536BA40604; }; | 1174D3512AF8207950094C56 = { isa = PBXBuildFile; fileRef = FF625CB50FB5C3536BA40604; }; | ||||
| 087CCE9E7146F1EC4F241254 = { isa = PBXBuildFile; fileRef = DA142548FCADFAC50648ED3C; }; | 087CCE9E7146F1EC4F241254 = { isa = PBXBuildFile; fileRef = DA142548FCADFAC50648ED3C; }; | ||||
| E9935BFB0EFA8CCCD41DC08E = { isa = PBXBuildFile; fileRef = D47A40CB3CF6AAE14B3C7796; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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; }; | 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, | D3D6EC2C17524688F2E803EB, | ||||
| 2D6D6985B452EA0B67A18914, | 2D6D6985B452EA0B67A18914, | ||||
| E6CC3A04349F6B227FDAB26F, | E6CC3A04349F6B227FDAB26F, | ||||
| 45C80436FD5A8438D0E6BE17, | |||||
| 420E4189E7DE25E9D0D8E5B8, | |||||
| FF625CB50FB5C3536BA40604, | FF625CB50FB5C3536BA40604, | ||||
| A490098DA6400B3881F336D0, | A490098DA6400B3881F336D0, | ||||
| DA142548FCADFAC50648ED3C, | DA142548FCADFAC50648ED3C, | ||||
| @@ -319,6 +324,7 @@ | |||||
| 87CCE4CB1FAB40B6F21DEACE = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; | 87CCE4CB1FAB40B6F21DEACE = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; | ||||
| 5362E03ADF975A126C1F2F7B = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( | 5362E03ADF975A126C1F2F7B = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( | ||||
| CD4226951C3F7FE19CF8A7CE, | CD4226951C3F7FE19CF8A7CE, | ||||
| 1DF9688E29753A0459E6C32A, | |||||
| 1174D3512AF8207950094C56, | 1174D3512AF8207950094C56, | ||||
| 087CCE9E7146F1EC4F241254, | 087CCE9E7146F1EC4F241254, | ||||
| E9935BFB0EFA8CCCD41DC08E, | E9935BFB0EFA8CCCD41DC08E, | ||||
| @@ -139,6 +139,8 @@ | |||||
| </Filter> | </Filter> | ||||
| <File RelativePath="..\..\Source\model\jucer_ComponentDocument.cpp"/> | <File RelativePath="..\..\Source\model\jucer_ComponentDocument.cpp"/> | ||||
| <File RelativePath="..\..\Source\model\jucer_ComponentDocument.h"/> | <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.cpp"/> | ||||
| <File RelativePath="..\..\Source\model\jucer_DrawableDocument.h"/> | <File RelativePath="..\..\Source\model\jucer_DrawableDocument.h"/> | ||||
| <File RelativePath="..\..\Source\model\jucer_NewFileWizard.cpp"/> | <File RelativePath="..\..\Source\model\jucer_NewFileWizard.cpp"/> | ||||
| @@ -139,6 +139,8 @@ | |||||
| </Filter> | </Filter> | ||||
| <File RelativePath="..\..\Source\model\jucer_ComponentDocument.cpp"/> | <File RelativePath="..\..\Source\model\jucer_ComponentDocument.cpp"/> | ||||
| <File RelativePath="..\..\Source\model\jucer_ComponentDocument.h"/> | <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.cpp"/> | ||||
| <File RelativePath="..\..\Source\model\jucer_DrawableDocument.h"/> | <File RelativePath="..\..\Source\model\jucer_DrawableDocument.h"/> | ||||
| <File RelativePath="..\..\Source\model\jucer_NewFileWizard.cpp"/> | <File RelativePath="..\..\Source\model\jucer_NewFileWizard.cpp"/> | ||||
| @@ -37,6 +37,10 @@ | |||||
| resource="0" file="Source/model/jucer_ComponentDocument.cpp"/> | resource="0" file="Source/model/jucer_ComponentDocument.cpp"/> | ||||
| <FILE id="u5n6JTb4z" name="jucer_ComponentDocument.h" compile="0" resource="0" | <FILE id="u5n6JTb4z" name="jucer_ComponentDocument.h" compile="0" resource="0" | ||||
| file="Source/model/jucer_ComponentDocument.h"/> | 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" | <FILE id="XxIww5OtT" name="jucer_DrawableDocument.cpp" compile="1" | ||||
| resource="0" file="Source/model/jucer_DrawableDocument.cpp"/> | resource="0" file="Source/model/jucer_DrawableDocument.cpp"/> | ||||
| <FILE id="lbB698APl" name="jucer_DrawableDocument.h" compile="0" resource="0" | <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_TextButton.h" | ||||
| #include "Component Types/jucer_ToggleButton.h" | #include "Component Types/jucer_ToggleButton.h" | ||||
| //============================================================================== | //============================================================================== | ||||
| static const char* const componentDocumentTag = "COMPONENT"; | static const char* const componentDocumentTag = "COMPONENT"; | ||||
| static const char* const componentGroupTag = "COMPONENTS"; | static const char* const componentGroupTag = "COMPONENTS"; | ||||
| @@ -42,7 +43,8 @@ static const char* const metadataTagEnd = "JUCER_" "COMPONENT_METADATA_E | |||||
| //============================================================================== | //============================================================================== | ||||
| class ComponentBoundsEditor : public PropertyComponent, | class ComponentBoundsEditor : public PropertyComponent, | ||||
| public ButtonListener | |||||
| public ButtonListener, | |||||
| public Value::Listener | |||||
| { | { | ||||
| public: | public: | ||||
| enum Type | enum Type | ||||
| @@ -68,15 +70,21 @@ public: | |||||
| addAndMakeVisible (anchorButton1 = new TextButton (String::empty)); | addAndMakeVisible (anchorButton1 = new TextButton (String::empty)); | ||||
| anchorButton1->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); | anchorButton1->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); | ||||
| anchorButton1->setTriggeredOnMouseDown (true); | |||||
| anchorButton1->addButtonListener (this); | anchorButton1->addButtonListener (this); | ||||
| addAndMakeVisible (anchorButton2 = new TextButton (String::empty)); | addAndMakeVisible (anchorButton2 = new TextButton (String::empty)); | ||||
| anchorButton2->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); | anchorButton2->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); | ||||
| anchorButton2->setTriggeredOnMouseDown (true); | |||||
| anchorButton2->addButtonListener (this); | anchorButton2->addButtonListener (this); | ||||
| boundsValue.addListener (this); | |||||
| valueChanged (boundsValue); | |||||
| } | } | ||||
| ~ComponentBoundsEditor() | ~ComponentBoundsEditor() | ||||
| { | { | ||||
| boundsValue.removeListener (this); | |||||
| deleteAllChildren(); | deleteAllChildren(); | ||||
| } | } | ||||
| @@ -87,8 +95,16 @@ public: | |||||
| label->setBounds (r.getX(), r.getY(), r.getWidth() / 2, r.getHeight() / 2); | label->setBounds (r.getX(), r.getY(), r.getWidth() / 2, r.getHeight() / 2); | ||||
| proportionButton->setBounds (r.getX() + r.getWidth() / 2, r.getY(), | proportionButton->setBounds (r.getX() + r.getWidth() / 2, r.getY(), | ||||
| r.getWidth() / 2, r.getHeight() / 2); | 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() | void refresh() | ||||
| @@ -97,24 +113,49 @@ public: | |||||
| void buttonClicked (Button* button) | void buttonClicked (Button* button) | ||||
| { | { | ||||
| RectangleCoordinates r (boundsValue.toString()); | |||||
| Coordinate& coord = getCoord (r); | |||||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (compState)); | |||||
| if (button == proportionButton) | if (button == proportionButton) | ||||
| { | { | ||||
| RectangleCoordinates r (boundsValue.toString()); | |||||
| Coordinate& coord = getCoord (r); | |||||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (compState)); | |||||
| coord.toggleProportionality (*markers); | coord.toggleProportionality (*markers); | ||||
| boundsValue = r.toString(); | boundsValue = r.toString(); | ||||
| } | } | ||||
| else if (button == anchorButton1) | 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) | 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, | class BoundsCoordValueSource : public Value::ValueSource, | ||||
| public Value::Listener | public Value::Listener | ||||
| @@ -186,6 +227,22 @@ public: | |||||
| return r.left; | 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: | private: | ||||
| ComponentDocument& document; | ComponentDocument& document; | ||||
| Type type; | Type type; | ||||
| @@ -588,11 +645,44 @@ const RectangleCoordinates ComponentDocument::getCoordsFor (const ValueTree& sta | |||||
| return RectangleCoordinates (state [compBoundsProperty]); | 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) | Coordinate::MarkerResolver* ComponentDocument::createMarkerResolver (const ValueTree& state) | ||||
| { | { | ||||
| return new ComponentMarkerResolver (*this, state, getCanvasWidth().getValue(), getCanvasHeight().getValue()); | 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) | void ComponentDocument::updateComponent (Component* comp) | ||||
| { | { | ||||
| const ValueTree v (getComponentState (comp)); | const ValueTree v (getComponentState (comp)); | ||||
| @@ -675,151 +765,3 @@ UndoManager* ComponentDocument::getUndoManager() | |||||
| { | { | ||||
| return &undoManager; | 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_Headers.h" | ||||
| #include "jucer_Project.h" | #include "jucer_Project.h" | ||||
| #include "jucer_Coordinate.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -66,7 +67,9 @@ public: | |||||
| void getComponentProperties (Array <PropertyComponent*>& props, Component* comp); | void getComponentProperties (Array <PropertyComponent*>& props, Component* comp); | ||||
| bool isStateForComponent (const ValueTree& storedState, Component* comp) const; | bool isStateForComponent (const ValueTree& storedState, Component* comp) const; | ||||
| Coordinate::MarkerResolver* createMarkerResolver (const ValueTree& state); | 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 addNewComponentMenuItems (PopupMenu& menu) const; | ||||
| void performNewComponentMenuItem (int menuResultCode); | void performNewComponentMenuItem (int menuResultCode); | ||||
| @@ -93,9 +96,6 @@ private: | |||||
| UndoManager undoManager; | UndoManager undoManager; | ||||
| bool changedSinceSaved; | bool changedSinceSaved; | ||||
| class DragHandler; | |||||
| ScopedPointer <DragHandler> dragger; | |||||
| void checkRootObject(); | void checkRootObject(); | ||||
| ValueTree getComponentGroup() const; | ValueTree getComponentGroup() const; | ||||
| Value getRootValue (const var::identifier& name) { return root.getPropertyAsValue (name, getUndoManager()); } | 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) | void writeLinkerFlags (OutputStream& out, const Project::BuildConfiguration& config) | ||||
| { | { | ||||
| out << " LDFLAGS += -L$(BINDIR) -L$(LIBDIR) -mwindows"; | |||||
| out << " LDFLAGS += -L$(BINDIR) -L$(LIBDIR)"; | |||||
| { | { | ||||
| Array<RelativePath> libraryPaths; | Array<RelativePath> libraryPaths; | ||||
| @@ -159,112 +159,6 @@ private: | |||||
| Rectangle<int> textArea; | 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, | class ComponentEditor::Canvas : public Component, | ||||
| public ValueTree::Listener, | public ValueTree::Listener, | ||||
| @@ -287,6 +181,7 @@ public: | |||||
| ~Canvas() | ~Canvas() | ||||
| { | { | ||||
| dragger = 0; | |||||
| getDocument().getRoot().removeListener (this); | getDocument().getRoot().removeListener (this); | ||||
| componentHolder->deleteAllChildren(); | componentHolder->deleteAllChildren(); | ||||
| deleteAllChildren(); | deleteAllChildren(); | ||||
| @@ -508,10 +403,260 @@ public: | |||||
| void showSizeGuides() { overlay->showSizeGuides(); } | void showSizeGuides() { overlay->showSizeGuides(); } | ||||
| void hideSizeGuides() { overlay->hideSizeGuides(); } | 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: | private: | ||||
| ComponentEditor& editor; | ComponentEditor& editor; | ||||
| const BorderSize border; | const BorderSize border; | ||||
| const int resizerThickness; | const int resizerThickness; | ||||
| ScopedPointer <DragOperation> dragger; | |||||
| ResizableBorderComponent::Zone dragZone; | ResizableBorderComponent::Zone dragZone; | ||||
| int dragStartWidth, dragStartHeight; | int dragStartWidth, dragStartHeight; | ||||
| @@ -571,7 +716,7 @@ private: | |||||
| if (component != 0) | if (component != 0) | ||||
| { | { | ||||
| updateDragZone (e.getPosition()); | updateDragZone (e.getPosition()); | ||||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(), dragZone); | |||||
| canvas.beginDrag (e, dragZone); | |||||
| canvas.showSizeGuides(); | canvas.showSizeGuides(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -579,7 +724,7 @@ private: | |||||
| void mouseDrag (const MouseEvent& e) | void mouseDrag (const MouseEvent& e) | ||||
| { | { | ||||
| if (component != 0) | if (component != 0) | ||||
| canvas.getDocument().continueDrag (e); | |||||
| canvas.continueDrag (e); | |||||
| } | } | ||||
| void mouseUp (const MouseEvent& e) | void mouseUp (const MouseEvent& e) | ||||
| @@ -587,7 +732,7 @@ private: | |||||
| canvas.hideSizeGuides(); | canvas.hideSizeGuides(); | ||||
| if (component != 0) | if (component != 0) | ||||
| canvas.getDocument().endDrag (e); | |||||
| canvas.endDrag (e); | |||||
| updateDragZone (e.getPosition()); | updateDragZone (e.getPosition()); | ||||
| } | } | ||||
| @@ -720,11 +865,10 @@ private: | |||||
| { | { | ||||
| isDraggingClickedComp = true; | isDraggingClickedComp = true; | ||||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | 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(); | showSizeGuides(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -747,7 +891,7 @@ private: | |||||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult); | 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) | 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; | 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; | 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: | 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: | 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 | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | 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 | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := amalgamator | TARGET := amalgamator | ||||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := amalgamator | TARGET := amalgamator | ||||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | 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 | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := Plugin\ Host | TARGET := Plugin\ Host | ||||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := Plugin\ Host | TARGET := Plugin\ Host | ||||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | 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 | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := BinaryBuilder | TARGET := BinaryBuilder | ||||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := BinaryBuilder | TARGET := BinaryBuilder | ||||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | 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 | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := HelloWorld | TARGET := HelloWorld | ||||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := HelloWorld | TARGET := HelloWorld | ||||
| @@ -19,8 +19,8 @@ ifeq ($(CONFIG),Debug) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | 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 | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := JuceDemo | TARGET := JuceDemo | ||||
| @@ -34,8 +34,8 @@ ifeq ($(CONFIG),Release) | |||||
| OUTDIR := build | OUTDIR := build | ||||
| CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -Os | 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 := | LDDEPS := | ||||
| RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | RESFLAGS := -D "LINUX=1" -D "NDEBUG=1" -I "/usr/include" -I "/usr/include/freetype2" | ||||
| TARGET := JuceDemo | TARGET := JuceDemo | ||||
| @@ -10189,7 +10189,8 @@ public: | |||||
| static juce_wchar* createCopy (const char* const src, const size_t numChars) | static juce_wchar* createCopy (const char* const src, const size_t numChars) | ||||
| { | { | ||||
| juce_wchar* const dest = createUninitialised (numChars); | juce_wchar* const dest = createUninitialised (numChars); | ||||
| CharacterFunctions::copy (dest, src, numChars + 1); | |||||
| CharacterFunctions::copy (dest, src, numChars); | |||||
| dest [numChars] = 0; | |||||
| return dest; | return dest; | ||||
| } | } | ||||
| @@ -17924,7 +17925,11 @@ FileBasedDocument::~FileBasedDocument() | |||||
| void FileBasedDocument::setChangedFlag (const bool hasChanged) | void FileBasedDocument::setChangedFlag (const bool hasChanged) | ||||
| { | { | ||||
| changedSinceSave = hasChanged; | |||||
| if (changedSinceSave != hasChanged) | |||||
| { | |||||
| changedSinceSave = hasChanged; | |||||
| sendChangeMessage (this); | |||||
| } | |||||
| } | } | ||||
| void FileBasedDocument::changed() | void FileBasedDocument::changed() | ||||
| @@ -17938,7 +17943,7 @@ void FileBasedDocument::setFile (const File& newFile) | |||||
| if (documentFile != newFile) | if (documentFile != newFile) | ||||
| { | { | ||||
| 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) | const String Slider::getTextFromValue (double v) | ||||
| { | { | ||||
| if (numDecimalPlaces > 0) | |||||
| return String (v, numDecimalPlaces) + textSuffix; | |||||
| if (getNumDecimalPlacesToDisplay() > 0) | |||||
| return String (v, getNumDecimalPlacesToDisplay()) + getTextValueSuffix(); | |||||
| else | else | ||||
| return String (roundToInt (v)) + textSuffix; | |||||
| return String (roundToInt (v)) + getTextValueSuffix(); | |||||
| } | } | ||||
| double Slider::getValueFromText (const String& text) | double Slider::getValueFromText (const String& text) | ||||
| @@ -43,7 +43,7 @@ | |||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 51 | #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) | #define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) | ||||
| @@ -21367,6 +21367,8 @@ public: | |||||
| void setTextValueSuffix (const String& suffix); | void setTextValueSuffix (const String& suffix); | ||||
| const String getTextValueSuffix() const; | |||||
| virtual double proportionOfLengthToValue (double proportion); | virtual double proportionOfLengthToValue (double proportion); | ||||
| virtual double valueToProportionOfLength (double value); | virtual double valueToProportionOfLength (double value); | ||||
| @@ -21415,6 +21417,8 @@ protected: | |||||
| void colourChanged(); | void colourChanged(); | ||||
| void valueChanged (Value& value); | void valueChanged (Value& value); | ||||
| int getNumDecimalPlacesToDisplay() const throw() { return numDecimalPlaces; } | |||||
| private: | private: | ||||
| ListenerList <SliderListener> listeners; | ListenerList <SliderListener> listeners; | ||||
| Value currentValue, valueMin, valueMax; | Value currentValue, valueMin, valueMax; | ||||
| @@ -47,7 +47,7 @@ public: | |||||
| /** If the current value of destination is equal to requiredCurrentValue, this | /** If the current value of destination is equal to requiredCurrentValue, this | ||||
| will set it to newValue; otherwise, it will leave it unchanged. | 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); | static int32 compareAndExchange (int32& destination, int32 newValue, int32 requiredCurrentValue); | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 51 | #define JUCE_MINOR_VERSION 51 | ||||
| #define JUCE_BUILDNUMBER 14 | |||||
| #define JUCE_BUILDNUMBER 15 | |||||
| /** Current Juce version number. | /** 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) | const String Slider::getTextFromValue (double v) | ||||
| { | { | ||||
| if (numDecimalPlaces > 0) | |||||
| return String (v, numDecimalPlaces) + textSuffix; | |||||
| if (getNumDecimalPlacesToDisplay() > 0) | |||||
| return String (v, getNumDecimalPlacesToDisplay()) + getTextValueSuffix(); | |||||
| else | else | ||||
| return String (roundToInt (v)) + textSuffix; | |||||
| return String (roundToInt (v)) + getTextValueSuffix(); | |||||
| } | } | ||||
| double Slider::getValueFromText (const String& text) | double Slider::getValueFromText (const String& text) | ||||
| @@ -620,6 +620,9 @@ public: | |||||
| */ | */ | ||||
| void setTextValueSuffix (const String& suffix); | 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. | /** Allows a user-defined mapping of distance along the slider to its value. | ||||
| @@ -748,6 +751,11 @@ protected: | |||||
| /** @internal */ | /** @internal */ | ||||
| void valueChanged (Value& value); | 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: | private: | ||||
| ListenerList <SliderListener> listeners; | ListenerList <SliderListener> listeners; | ||||
| Value currentValue, valueMin, valueMax; | Value currentValue, valueMin, valueMax; | ||||
| @@ -78,7 +78,8 @@ public: | |||||
| static juce_wchar* createCopy (const char* const src, const size_t numChars) | static juce_wchar* createCopy (const char* const src, const size_t numChars) | ||||
| { | { | ||||
| juce_wchar* const dest = createUninitialised (numChars); | juce_wchar* const dest = createUninitialised (numChars); | ||||
| CharacterFunctions::copy (dest, src, numChars + 1); | |||||
| CharacterFunctions::copy (dest, src, numChars); | |||||
| dest [numChars] = 0; | |||||
| return dest; | return dest; | ||||
| } | } | ||||
| @@ -565,7 +565,7 @@ public: | |||||
| /** Returns a copy of this string, having removed a specified set of characters from its start. | /** 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 | Characters are removed from the start of the string until it finds one that is not in the | ||||
| specified set, and then it stops. | 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 | @see trim, trimStart, trimCharactersAtEnd | ||||
| */ | */ | ||||
| const String trimCharactersAtStart (const String& charactersToTrim) const; | 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. | /** 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 | Characters are removed from the end of the string until it finds one that is not in the | ||||
| specified set, and then it stops. | 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 | @see trim, trimEnd, trimCharactersAtStart | ||||
| */ | */ | ||||
| const String trimCharactersAtEnd (const String& charactersToTrim) const; | const String trimCharactersAtEnd (const String& charactersToTrim) const; | ||||
| @@ -54,7 +54,11 @@ FileBasedDocument::~FileBasedDocument() | |||||
| //============================================================================== | //============================================================================== | ||||
| void FileBasedDocument::setChangedFlag (const bool hasChanged) | void FileBasedDocument::setChangedFlag (const bool hasChanged) | ||||
| { | { | ||||
| changedSinceSave = hasChanged; | |||||
| if (changedSinceSave != hasChanged) | |||||
| { | |||||
| changedSinceSave = hasChanged; | |||||
| sendChangeMessage (this); | |||||
| } | |||||
| } | } | ||||
| void FileBasedDocument::changed() | void FileBasedDocument::changed() | ||||
| @@ -69,7 +73,7 @@ void FileBasedDocument::setFile (const File& newFile) | |||||
| if (documentFile != newFile) | if (documentFile != newFile) | ||||
| { | { | ||||
| documentFile = newFile; | documentFile = newFile; | ||||
| changedSinceSave = true; | |||||
| changed(); | |||||
| } | } | ||||
| } | } | ||||