mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-22 01:34:21 +00:00
887 lines
29 KiB
C++
887 lines
29 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
Copyright 2004-10 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_ComponentDocument.h"
|
|
#include "Types/jucer_ComponentTypeManager.h"
|
|
#include "../../utility/jucer_CoordinatePropertyComponent.h"
|
|
|
|
|
|
//==============================================================================
|
|
static const Identifier componentDocumentTag ("COMPONENT");
|
|
static const Identifier componentGroupTag ("COMPONENTS");
|
|
static const Identifier markersGroupXTag ("MARKERS_X");
|
|
static const Identifier markersGroupYTag ("MARKERS_Y");
|
|
|
|
static const char* const metadataTagStart = "JUCER_" "COMPONENT_METADATA_START"; // written like this to avoid thinking this file is a component!
|
|
static const char* const metadataTagEnd = "JUCER_" "COMPONENT_METADATA_END";
|
|
|
|
const Identifier ComponentDocument::idProperty ("id");
|
|
const Identifier ComponentDocument::compBoundsProperty ("position");
|
|
const Identifier ComponentDocument::memberNameProperty ("memberName");
|
|
const Identifier ComponentDocument::compNameProperty ("name");
|
|
const Identifier ComponentDocument::compTooltipProperty ("tooltip");
|
|
const Identifier ComponentDocument::compFocusOrderProperty ("focusOrder");
|
|
const Identifier ComponentDocument::jucerIDProperty ("jucerID");
|
|
|
|
|
|
//==============================================================================
|
|
ComponentDocument::ComponentDocument (Project* project_, const File& cppFile_)
|
|
: project (project_),
|
|
cppFile (cppFile_),
|
|
root (componentDocumentTag),
|
|
changedSinceSaved (false),
|
|
usingTemporaryCanvasSize (false)
|
|
{
|
|
checkRootObject();
|
|
|
|
root.addListener (this);
|
|
}
|
|
|
|
ComponentDocument::ComponentDocument (const ComponentDocument& other)
|
|
: project (other.project),
|
|
cppFile (other.cppFile),
|
|
root (other.root),
|
|
changedSinceSaved (false)
|
|
{
|
|
checkRootObject();
|
|
|
|
root.addListener (this);
|
|
}
|
|
|
|
ComponentDocument::~ComponentDocument()
|
|
{
|
|
root.removeListener (this);
|
|
}
|
|
|
|
void ComponentDocument::beginNewTransaction()
|
|
{
|
|
undoManager.beginNewTransaction();
|
|
}
|
|
|
|
void ComponentDocument::changed()
|
|
{
|
|
changedSinceSaved = true;
|
|
}
|
|
|
|
void ComponentDocument::valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property)
|
|
{
|
|
changed();
|
|
}
|
|
|
|
void ComponentDocument::valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged)
|
|
{
|
|
changed();
|
|
}
|
|
|
|
void ComponentDocument::valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged)
|
|
{
|
|
changed();
|
|
}
|
|
|
|
bool ComponentDocument::isComponentFile (const File& file)
|
|
{
|
|
if (! file.hasFileExtension (".cpp"))
|
|
return false;
|
|
|
|
InputStream* in = file.createInputStream();
|
|
|
|
if (in != 0)
|
|
{
|
|
BufferedInputStream buf (in, 8192, true);
|
|
|
|
while (! buf.isExhausted())
|
|
if (buf.readNextLine().contains (metadataTagStart))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const String ComponentDocument::getCppTemplate() const { return String (BinaryData::jucer_ComponentTemplate_cpp); }
|
|
const String ComponentDocument::getHeaderTemplate() const { return String (BinaryData::jucer_ComponentTemplate_h); }
|
|
|
|
const String ComponentDocument::getCppContent()
|
|
{
|
|
MemoryOutputStream cpp, header;
|
|
writeCode (cpp, header);
|
|
return cpp.toUTF8();
|
|
}
|
|
|
|
const String ComponentDocument::getHeaderContent()
|
|
{
|
|
MemoryOutputStream cpp, header;
|
|
writeCode (cpp, header);
|
|
return header.toUTF8();
|
|
}
|
|
|
|
void ComponentDocument::writeCode (OutputStream& cpp, OutputStream& header)
|
|
{
|
|
CodeGenerator codeGen;
|
|
|
|
codeGen.className = getClassName().toString();
|
|
codeGen.parentClasses = "public Component";
|
|
|
|
{
|
|
MemoryOutputStream stateStream (1024, 1024, &codeGen.componentStateData);
|
|
root.writeToStream (stateStream);
|
|
}
|
|
|
|
for (int i = 0; i < getNumComponents(); ++i)
|
|
{
|
|
ComponentTypeInstance item (*this, getComponent (i));
|
|
item.createCode (codeGen);
|
|
}
|
|
|
|
{
|
|
MemoryOutputStream metaData;
|
|
writeMetadata (metaData);
|
|
codeGen.jucerMetadata = metaData.toUTF8();
|
|
}
|
|
|
|
{
|
|
String code (getCppTemplate());
|
|
String oldContent;
|
|
|
|
codeGen.applyToCode (code, cppFile, false, project);
|
|
customCode.applyTo (code);
|
|
cpp << code;
|
|
}
|
|
|
|
{
|
|
String code (getHeaderTemplate());
|
|
String oldContent;
|
|
|
|
codeGen.applyToCode (code, cppFile.withFileExtension (".h"), false, project);
|
|
customCode.applyTo (code);
|
|
header << code;
|
|
}
|
|
}
|
|
|
|
void ComponentDocument::writeMetadata (OutputStream& out)
|
|
{
|
|
out << metadataTagStart << newLine << newLine;
|
|
|
|
ScopedPointer<XmlElement> xml (root.createXml());
|
|
jassert (xml != 0);
|
|
|
|
if (xml != 0)
|
|
xml->writeToStream (out, String::empty, false, false);
|
|
|
|
out << newLine << metadataTagEnd;
|
|
}
|
|
|
|
bool ComponentDocument::save()
|
|
{
|
|
MemoryOutputStream cpp, header;
|
|
writeCode (cpp, header);
|
|
|
|
bool savedOk = FileHelpers::overwriteFileWithNewDataIfDifferent (cppFile, cpp)
|
|
&& FileHelpers::overwriteFileWithNewDataIfDifferent (cppFile.withFileExtension (".h"), header);
|
|
|
|
if (savedOk)
|
|
changedSinceSaved = false;
|
|
|
|
return savedOk;
|
|
}
|
|
|
|
bool ComponentDocument::reload()
|
|
{
|
|
String xmlString;
|
|
bool hadMetaDataTags = false;
|
|
|
|
{
|
|
InputStream* in = cppFile.createInputStream();
|
|
|
|
if (in == 0)
|
|
return false;
|
|
|
|
BufferedInputStream buf (in, 8192, true);
|
|
String::Concatenator xml (xmlString);
|
|
|
|
while (! buf.isExhausted())
|
|
{
|
|
String line (buf.readNextLine());
|
|
|
|
if (line.contains (metadataTagStart))
|
|
{
|
|
while (! buf.isExhausted())
|
|
{
|
|
line = buf.readNextLine();
|
|
if (line.contains (metadataTagEnd))
|
|
{
|
|
hadMetaDataTags = true;
|
|
break;
|
|
}
|
|
|
|
xml.append (line);
|
|
xml.append (newLine);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
XmlDocument doc (xmlString);
|
|
ScopedPointer<XmlElement> xml (doc.getDocumentElement());
|
|
|
|
if (xml == 0 && hadMetaDataTags)
|
|
xml = new XmlElement (componentDocumentTag.toString());
|
|
|
|
if (xml != 0 && xml->hasTagName (componentDocumentTag.toString()))
|
|
{
|
|
ValueTree newTree (ValueTree::fromXml (*xml));
|
|
|
|
if (newTree.isValid())
|
|
{
|
|
root = newTree;
|
|
markersX = 0;
|
|
markersY = 0;
|
|
checkRootObject();
|
|
customCode.reloadFrom (cppFile.loadFileAsString());
|
|
|
|
root.addChild (ValueTree ("dummy"), 0, 0);
|
|
root.removeChild (root.getChildWithName("dummy"), 0);
|
|
|
|
undoManager.clearUndoHistory();
|
|
changedSinceSaved = false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ComponentDocument::hasChangedSinceLastSave()
|
|
{
|
|
return changedSinceSaved || customCode.needsSaving();
|
|
}
|
|
|
|
void ComponentDocument::createSubTreeIfNotThere (const Identifier& name)
|
|
{
|
|
if (! root.getChildWithName (name).isValid())
|
|
root.addChild (ValueTree (name), -1, 0);
|
|
}
|
|
|
|
void ComponentDocument::checkRootObject()
|
|
{
|
|
jassert (root.hasType (componentDocumentTag));
|
|
|
|
if (root [idProperty].toString().isEmpty())
|
|
root.setProperty (idProperty, createAlphaNumericUID(), 0);
|
|
|
|
createSubTreeIfNotThere (componentGroupTag);
|
|
createSubTreeIfNotThere (markersGroupXTag);
|
|
createSubTreeIfNotThere (markersGroupYTag);
|
|
|
|
if (markersX == 0)
|
|
markersX = new MarkerList (*this, true);
|
|
|
|
if (markersY == 0)
|
|
markersY = new MarkerList (*this, false);
|
|
|
|
if (getClassName().toString().isEmpty())
|
|
getClassName() = "NewComponent";
|
|
|
|
if ((int) getCanvasWidth().getValue() <= 0)
|
|
getCanvasWidth() = 640;
|
|
|
|
if ((int) getCanvasHeight().getValue() <= 0)
|
|
getCanvasHeight() = 480;
|
|
|
|
if (! root.hasProperty ("background"))
|
|
getBackgroundColour() = Colours::white.toString();
|
|
}
|
|
|
|
void ComponentDocument::setUsingTemporaryCanvasSize (bool b)
|
|
{
|
|
tempCanvasWidth = root.getProperty ("width");
|
|
tempCanvasHeight = root.getProperty ("height");
|
|
usingTemporaryCanvasSize = b;
|
|
}
|
|
|
|
Value ComponentDocument::getCanvasWidth() const
|
|
{
|
|
return usingTemporaryCanvasSize ? tempCanvasWidth : getRootValueNonUndoable ("width");
|
|
}
|
|
|
|
Value ComponentDocument::getCanvasHeight() const
|
|
{
|
|
return usingTemporaryCanvasSize ? tempCanvasHeight : getRootValueNonUndoable ("height");
|
|
}
|
|
|
|
Value ComponentDocument::getBackgroundColour() const
|
|
{
|
|
return getRootValueUndoable ("background");
|
|
}
|
|
|
|
//==============================================================================
|
|
const int menuItemOffset = 0x63451fa4;
|
|
|
|
void ComponentDocument::addNewComponentMenuItems (PopupMenu& menu) const
|
|
{
|
|
const StringArray displayNames (ComponentTypeManager::getInstance()->getDisplayNames());
|
|
|
|
for (int i = 0; i < displayNames.size(); ++i)
|
|
menu.addItem (i + menuItemOffset, "New " + displayNames[i]);
|
|
}
|
|
|
|
const ValueTree ComponentDocument::performNewComponentMenuItem (int menuResultCode)
|
|
{
|
|
const StringArray displayNames (ComponentTypeManager::getInstance()->getDisplayNames());
|
|
|
|
if (menuResultCode >= menuItemOffset && menuResultCode < menuItemOffset + displayNames.size())
|
|
{
|
|
ComponentTypeHandler* handler = ComponentTypeManager::getInstance()->getHandler (menuResultCode - menuItemOffset);
|
|
jassert (handler != 0);
|
|
|
|
if (handler != 0)
|
|
{
|
|
ValueTree state (handler->getXmlTag());
|
|
state.setProperty (idProperty, createAlphaNumericUID(), 0);
|
|
|
|
ComponentTypeInstance comp (*this, state);
|
|
handler->initialiseNewItem (comp);
|
|
|
|
getComponentGroup().addChild (state, -1, getUndoManager());
|
|
|
|
return state;
|
|
}
|
|
}
|
|
|
|
return ValueTree::invalid;
|
|
}
|
|
|
|
Component* ComponentDocument::findComponentForState (Component* compHolder, const ValueTree& state)
|
|
{
|
|
for (int i = compHolder->getNumChildComponents(); --i >= 0;)
|
|
{
|
|
Component* const c = compHolder->getChildComponent (i);
|
|
if (isStateForComponent (state, c))
|
|
return c;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ComponentDocument::updateComponentsIn (Component* compHolder)
|
|
{
|
|
int i;
|
|
for (i = compHolder->getNumChildComponents(); --i >= 0;)
|
|
{
|
|
Component* c = compHolder->getChildComponent (i);
|
|
|
|
if (! containsComponent (c))
|
|
delete c;
|
|
}
|
|
|
|
Array <Component*> componentsInOrder;
|
|
|
|
const int num = getNumComponents();
|
|
for (i = 0; i < num; ++i)
|
|
{
|
|
const ValueTree v (getComponent (i));
|
|
Component* c = findComponentForState (compHolder, v);
|
|
|
|
if (c == 0)
|
|
{
|
|
c = createComponent (i);
|
|
compHolder->addAndMakeVisible (c);
|
|
}
|
|
|
|
updateComponent (c);
|
|
componentsInOrder.add (c);
|
|
}
|
|
|
|
// Make sure the z-order is correct..
|
|
if (num > 0)
|
|
{
|
|
componentsInOrder.getLast()->toFront (false);
|
|
|
|
for (i = num - 1; --i >= 0;)
|
|
componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
ComponentDocument::TestComponent::TestComponent (ComponentDocument& document_)
|
|
: document (new ComponentDocument (document_))
|
|
{
|
|
setupDocument();
|
|
}
|
|
|
|
ComponentDocument::TestComponent::TestComponent (Project* project, const File& cppFile)
|
|
: document (new ComponentDocument (project, cppFile))
|
|
{
|
|
if (document->reload())
|
|
setupDocument();
|
|
else
|
|
document = 0;
|
|
}
|
|
|
|
ComponentDocument::TestComponent::~TestComponent()
|
|
{
|
|
deleteAllChildren();
|
|
}
|
|
|
|
void ComponentDocument::TestComponent::setupDocument()
|
|
{
|
|
document->setUsingTemporaryCanvasSize (true);
|
|
|
|
setSize (document->getCanvasWidth().getValue(),
|
|
document->getCanvasHeight().getValue());
|
|
}
|
|
|
|
void ComponentDocument::TestComponent::resized()
|
|
{
|
|
if (document != 0)
|
|
{
|
|
document->getCanvasWidth() = getWidth();
|
|
document->getCanvasHeight() = getHeight();
|
|
document->updateComponentsIn (this);
|
|
}
|
|
}
|
|
|
|
void ComponentDocument::TestComponent::paint (Graphics& g)
|
|
{
|
|
if (document == 0)
|
|
drawComponentPlaceholder (g, getWidth(), getHeight(), "(Not a valid Jucer component)");
|
|
}
|
|
|
|
//==============================================================================
|
|
ValueTree ComponentDocument::getComponentGroup() const
|
|
{
|
|
return root.getChildWithName (componentGroupTag);
|
|
}
|
|
|
|
int ComponentDocument::getNumComponents() const
|
|
{
|
|
return getComponentGroup().getNumChildren();
|
|
}
|
|
|
|
const ValueTree ComponentDocument::getComponent (int index) const
|
|
{
|
|
return getComponentGroup().getChild (index);
|
|
}
|
|
|
|
const ValueTree ComponentDocument::getComponentWithMemberName (const String& name) const
|
|
{
|
|
return getComponentGroup().getChildWithProperty (memberNameProperty, name);
|
|
}
|
|
|
|
const ValueTree ComponentDocument::getComponentWithID (const String& uid) const
|
|
{
|
|
return getComponentGroup().getChildWithProperty (idProperty, uid);
|
|
}
|
|
|
|
Component* ComponentDocument::createComponent (int index)
|
|
{
|
|
const ValueTree v (getComponentGroup().getChild (index));
|
|
|
|
if (v.isValid())
|
|
{
|
|
Component* c = ComponentTypeManager::getInstance()->createFromStoredType (*this, v);
|
|
c->getProperties().set (jucerIDProperty, v[idProperty]);
|
|
jassert (getJucerIDFor (c).isNotEmpty());
|
|
return c;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//==============================================================================
|
|
const Coordinate ComponentDocument::findMarker (const String& name, bool isHorizontal) const
|
|
{
|
|
if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) getCanvasWidth().getValue(), isHorizontal);
|
|
if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) getCanvasHeight().getValue(), isHorizontal);
|
|
|
|
if (name.containsChar ('.'))
|
|
{
|
|
const String compName (name.upToFirstOccurrenceOf (".", false, false).trim());
|
|
const String edge (name.fromFirstOccurrenceOf (".", false, false).trim());
|
|
|
|
if (compName.isNotEmpty() && edge.isNotEmpty())
|
|
{
|
|
const ValueTree comp (getComponentWithMemberName (compName));
|
|
|
|
if (comp.isValid())
|
|
{
|
|
const RectangleCoordinates coords (getCoordsFor (comp));
|
|
|
|
if (edge == "left") return coords.left;
|
|
if (edge == "right") return coords.right;
|
|
if (edge == "top") return coords.top;
|
|
if (edge == "bottom") return coords.bottom;
|
|
}
|
|
}
|
|
}
|
|
|
|
const ValueTree marker (getMarkerList (isHorizontal).getMarkerNamed (name));
|
|
if (marker.isValid())
|
|
return getMarkerList (isHorizontal).getCoordinate (marker);
|
|
|
|
return Coordinate (isHorizontal);
|
|
}
|
|
|
|
const RectangleCoordinates ComponentDocument::getCoordsFor (const ValueTree& state) const
|
|
{
|
|
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;
|
|
}
|
|
|
|
const String ComponentDocument::getNonexistentMemberName (String name)
|
|
{
|
|
String n (CodeHelpers::makeValidIdentifier (name, false, true, false));
|
|
int suffix = 2;
|
|
|
|
while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid()
|
|
|| getComponentWithMemberName (n).isValid())
|
|
n = n.trimCharactersAtEnd ("0123456789") + String (suffix++);
|
|
|
|
return n;
|
|
}
|
|
|
|
void ComponentDocument::renameAnchor (const String& oldName, const String& newName)
|
|
{
|
|
int i;
|
|
for (i = getNumComponents(); --i >= 0;)
|
|
{
|
|
ValueTree v (getComponent(i));
|
|
RectangleCoordinates coords (getCoordsFor (v));
|
|
coords.renameAnchorIfUsed (oldName, newName, *this);
|
|
setCoordsFor (v, coords);
|
|
}
|
|
|
|
markersX->renameAnchorInMarkers (oldName, newName);
|
|
markersY->renameAnchorInMarkers (oldName, newName);
|
|
}
|
|
|
|
void ComponentDocument::addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu,
|
|
bool isAnchor1, const String& fullCoordName)
|
|
{
|
|
Coordinate requestedCoord (findMarker (name, coord.isHorizontal()));
|
|
|
|
menu.addItem (i, name,
|
|
! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)),
|
|
name == (isAnchor1 ? coord.getAnchor1() : coord.getAnchor2()));
|
|
}
|
|
|
|
void ComponentDocument::addComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName,
|
|
Coordinate& coord, PopupMenu& menu, bool isAnchor1)
|
|
{
|
|
const String componentName (componentState [memberNameProperty].toString());
|
|
const String fullCoordName (componentName + "." + coordName);
|
|
|
|
if (coord.isHorizontal())
|
|
{
|
|
addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName);
|
|
addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName);
|
|
menu.addSeparator();
|
|
addMarkerMenuItem (3, coord, componentName + ".left", menu, isAnchor1, fullCoordName);
|
|
addMarkerMenuItem (4, coord, componentName + ".right", menu, isAnchor1, fullCoordName);
|
|
}
|
|
else
|
|
{
|
|
addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName);
|
|
addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName);
|
|
menu.addSeparator();
|
|
addMarkerMenuItem (3, coord, componentName + ".top", menu, isAnchor1, fullCoordName);
|
|
addMarkerMenuItem (4, coord, componentName + ".bottom", menu, isAnchor1, fullCoordName);
|
|
}
|
|
|
|
menu.addSeparator();
|
|
const MarkerList& markerList = getMarkerList (coord.isHorizontal());
|
|
|
|
int i;
|
|
for (i = 0; i < markerList.size(); ++i)
|
|
addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName);
|
|
|
|
menu.addSeparator();
|
|
for (i = 0; i < getNumComponents(); ++i)
|
|
{
|
|
const String compName (getComponent (i) [memberNameProperty].toString());
|
|
|
|
if (compName != componentName)
|
|
{
|
|
if (coord.isHorizontal())
|
|
{
|
|
addMarkerMenuItem (10000 + i * 4, coord, compName + ".left", menu, isAnchor1, fullCoordName);
|
|
addMarkerMenuItem (10001 + i * 4, coord, compName + ".right", menu, isAnchor1, fullCoordName);
|
|
}
|
|
else
|
|
{
|
|
addMarkerMenuItem (10002 + i * 4, coord, compName + ".top", menu, isAnchor1, fullCoordName);
|
|
addMarkerMenuItem (10003 + i * 4, coord, compName + ".bottom", menu, isAnchor1, fullCoordName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const String ComponentDocument::getChosenMarkerMenuItem (const ValueTree& componentState, Coordinate& coord, int i) const
|
|
{
|
|
const String componentName (componentState [memberNameProperty].toString());
|
|
|
|
if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName;
|
|
if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName;
|
|
if (i == 3) return componentName + (coord.isHorizontal() ? ".left" : ".top");
|
|
if (i == 4) return componentName + (coord.isHorizontal() ? ".right" : ".bottom");
|
|
|
|
const MarkerList& markerList = getMarkerList (coord.isHorizontal());
|
|
|
|
if (i >= 100 && i < 10000)
|
|
return markerList.getName (markerList.getMarker (i - 100));
|
|
|
|
if (i >= 10000)
|
|
{
|
|
const String compName (getComponent ((i - 10000) / 4) [memberNameProperty].toString());
|
|
switch (i & 3)
|
|
{
|
|
case 0: return compName + ".left";
|
|
case 1: return compName + ".right";
|
|
case 2: return compName + ".top";
|
|
case 3: return compName + ".bottom";
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
jassertfalse;
|
|
return String::empty;
|
|
}
|
|
|
|
void ComponentDocument::updateComponent (Component* comp)
|
|
{
|
|
const ValueTree v (getComponentState (comp));
|
|
|
|
if (v.isValid())
|
|
{
|
|
ComponentTypeInstance item (*this, v);
|
|
item.updateComponent (comp);
|
|
}
|
|
}
|
|
|
|
bool ComponentDocument::containsComponent (Component* comp) const
|
|
{
|
|
const ValueTree comps (getComponentGroup());
|
|
|
|
for (int i = 0; i < comps.getNumChildren(); ++i)
|
|
if (isStateForComponent (comps.getChild(i), comp))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
const ValueTree ComponentDocument::getComponentState (Component* comp) const
|
|
{
|
|
jassert (comp != 0);
|
|
const ValueTree comps (getComponentGroup());
|
|
|
|
for (int i = 0; i < comps.getNumChildren(); ++i)
|
|
if (isStateForComponent (comps.getChild(i), comp))
|
|
return comps.getChild(i);
|
|
|
|
jassertfalse;
|
|
return ValueTree::invalid;
|
|
}
|
|
|
|
bool ComponentDocument::isStateForComponent (const ValueTree& storedState, Component* comp) const
|
|
{
|
|
jassert (comp != 0);
|
|
jassert (! storedState [idProperty].isVoid());
|
|
return storedState [idProperty] == getJucerIDFor (comp);
|
|
}
|
|
|
|
void ComponentDocument::removeComponent (const ValueTree& state)
|
|
{
|
|
jassert (state.isAChildOf (getComponentGroup()));
|
|
renameAnchor (state [memberNameProperty], String::empty);
|
|
getComponentGroup().removeChild (state, getUndoManager());
|
|
}
|
|
|
|
//==============================================================================
|
|
ComponentDocument::MarkerList::MarkerList (ComponentDocument& document_, const bool isX_)
|
|
: MarkerListBase (document_.getRoot().getChildWithName (isX_ ? markersGroupXTag : markersGroupYTag), isX_),
|
|
document (document_)
|
|
{
|
|
jassert (group.isValid());
|
|
jassert (group.isAChildOf (document_.getRoot()));
|
|
}
|
|
|
|
UndoManager* ComponentDocument::MarkerList::getUndoManager() const
|
|
{
|
|
return document.getUndoManager();
|
|
}
|
|
|
|
const String ComponentDocument::MarkerList::getNonexistentMarkerName (const String& name)
|
|
{
|
|
return document.getNonexistentMemberName (name);
|
|
}
|
|
|
|
void ComponentDocument::MarkerList::renameAnchor (const String& oldName, const String& newName)
|
|
{
|
|
document.renameAnchor (oldName, newName);
|
|
}
|
|
|
|
const Coordinate ComponentDocument::MarkerList::findMarker (const String& name, bool isHorizontal_) const
|
|
{
|
|
if (isHorizontal_ == isX)
|
|
{
|
|
if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) document.getCanvasWidth().getValue(), isX);
|
|
if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) document.getCanvasHeight().getValue(), isX);
|
|
|
|
const ValueTree marker (document.getMarkerList (isX).getMarkerNamed (name));
|
|
if (marker.isValid())
|
|
return document.getMarkerList (isX).getCoordinate (marker);
|
|
}
|
|
|
|
return Coordinate (isX);
|
|
}
|
|
|
|
void ComponentDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1)
|
|
{
|
|
const String fullCoordName (getName (markerState));
|
|
|
|
if (coord.isHorizontal())
|
|
{
|
|
document.addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName);
|
|
document.addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName);
|
|
}
|
|
else
|
|
{
|
|
document.addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName);
|
|
document.addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName);
|
|
}
|
|
|
|
menu.addSeparator();
|
|
const MarkerList& markerList = document.getMarkerList (coord.isHorizontal());
|
|
|
|
for (int i = 0; i < markerList.size(); ++i)
|
|
document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName);
|
|
}
|
|
|
|
const String ComponentDocument::MarkerList::getChosenMarkerMenuItem (const Coordinate& coord, int i) const
|
|
{
|
|
if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName;
|
|
if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName;
|
|
|
|
const MarkerList& markerList = document.getMarkerList (coord.isHorizontal());
|
|
|
|
if (i >= 100 && i < 10000)
|
|
return markerList.getName (markerList.getMarker (i - 100));
|
|
|
|
jassertfalse;
|
|
return String::empty;
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
bool ComponentDocument::MarkerList::createProperties (Array <PropertyComponent*>& props, const String& itemId)
|
|
{
|
|
ValueTree marker (group.getChildWithProperty (idProperty, itemId));
|
|
|
|
if (marker.isValid())
|
|
{
|
|
props.add (new TextPropertyComponent (Value (new MarkerListBase::MarkerNameValueSource (this, getNameAsValue (marker))),
|
|
"Marker Name", 256, false));
|
|
|
|
props.add (new MarkerListBase::PositionPropertyComponent (document, *this, "Position", marker,
|
|
marker.getPropertyAsValue (getMarkerPosProperty(), document.getUndoManager())));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//==============================================================================
|
|
bool ComponentDocument::createItemProperties (Array <PropertyComponent*>& props, const String& itemId)
|
|
{
|
|
ValueTree comp (getComponentWithID (itemId));
|
|
|
|
if (comp.isValid())
|
|
{
|
|
ComponentTypeInstance item (*this, comp);
|
|
item.createProperties (props);
|
|
return true;
|
|
}
|
|
|
|
if (markersX->createProperties (props, itemId)
|
|
|| markersY->createProperties (props, itemId))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void ComponentDocument::createItemProperties (Array <PropertyComponent*>& props, const StringArray& selectedItemIds)
|
|
{
|
|
if (selectedItemIds.size() != 1)
|
|
return; //xxx
|
|
|
|
for (int i = 0; i < selectedItemIds.size(); ++i)
|
|
createItemProperties (props, selectedItemIds[i]);
|
|
}
|
|
|
|
//==============================================================================
|
|
UndoManager* ComponentDocument::getUndoManager() const
|
|
{
|
|
return &undoManager;
|
|
}
|
|
|
|
//==============================================================================
|
|
const String ComponentDocument::getJucerIDFor (Component* c)
|
|
{
|
|
if (c == 0)
|
|
{
|
|
jassertfalse;
|
|
return String::empty;
|
|
}
|
|
|
|
jassert (c->getProperties().contains (jucerIDProperty));
|
|
return c->getProperties() [jucerIDProperty];
|
|
}
|
|
|
|
//==============================================================================
|
|
void ComponentDocument::createClassProperties (Array <PropertyComponent*>& props)
|
|
{
|
|
props.add (new TextPropertyComponent (getClassName(), "Class Name", 256, false));
|
|
props.getLast()->setTooltip ("The C++ class name for the component class.");
|
|
|
|
props.add (new TextPropertyComponent (getClassDescription(), "Description", 512, false));
|
|
props.getLast()->setTooltip ("A freeform description of the component.");
|
|
|
|
props.add (new SliderPropertyComponent (getCanvasWidth(), "Initial Width", 1.0, 8192.0, 1.0));
|
|
props.getLast()->setTooltip ("The initial width of the component when it is created.");
|
|
|
|
props.add (new SliderPropertyComponent (getCanvasHeight(), "Initial Height", 1.0, 8192.0, 1.0));
|
|
props.getLast()->setTooltip ("The initial height of the component when it is created.");
|
|
}
|