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