@@ -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(); | |||||
} | } | ||||
} | } | ||||