| @@ -761,10 +761,9 @@ void ComponentDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerS | |||||
| } | } | ||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| const MarkerList& markerList = document.getMarkerList (isX); | |||||
| for (int i = 0; i < markerList.size(); ++i) | |||||
| document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), | |||||
| for (int i = 0; i < size(); ++i) | |||||
| document.addMarkerMenuItem (100 + i, coord, getName (getMarker (i)), | |||||
| String::empty, menu, isAnchor1, fullCoordName); | String::empty, menu, isAnchor1, fullCoordName); | ||||
| } | } | ||||
| @@ -773,10 +772,8 @@ const String ComponentDocument::MarkerList::getChosenMarkerMenuItem (const Relat | |||||
| if (i == 1) return isX ? "parent.left" : "parent.top"; | if (i == 1) return isX ? "parent.left" : "parent.top"; | ||||
| if (i == 2) return isX ? "parent.right" : "parent.bottom"; | if (i == 2) return isX ? "parent.right" : "parent.bottom"; | ||||
| const MarkerList& markerList = document.getMarkerList (isX); | |||||
| if (i >= 100 && i < 10000) | if (i >= 100 && i < 10000) | ||||
| return markerList.getName (markerList.getMarker (i - 100)); | |||||
| return getName (getMarker (i - 100)); | |||||
| jassertfalse; | jassertfalse; | ||||
| return String::empty; | return String::empty; | ||||
| @@ -56,10 +56,10 @@ DrawableDocument::~DrawableDocument() | |||||
| root.removeListener (this); | root.removeListener (this); | ||||
| } | } | ||||
| void DrawableDocument::recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d) | |||||
| void DrawableDocument::recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d, StringArray& recentlyUsedIdCache) | |||||
| { | { | ||||
| if (d.getID().isEmpty()) | if (d.getID().isEmpty()) | ||||
| d.setID (createUniqueID (d.getState().getType().toString().toLowerCase() + "1"), 0); | |||||
| d.setID (createUniqueID (d.getState().getType().toString().toLowerCase() + "1", recentlyUsedIdCache), 0); | |||||
| if (d.getState().getType() == DrawableComposite::valueTreeType) | if (d.getState().getType() == DrawableComposite::valueTreeType) | ||||
| { | { | ||||
| @@ -68,7 +68,7 @@ void DrawableDocument::recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d) | |||||
| for (int i = 0; i < composite.getNumDrawables(); ++i) | for (int i = 0; i < composite.getNumDrawables(); ++i) | ||||
| { | { | ||||
| Drawable::ValueTreeWrapperBase child (composite.getDrawableState (i)); | Drawable::ValueTreeWrapperBase child (composite.getDrawableState (i)); | ||||
| recursivelyUpdateIDs (child); | |||||
| recursivelyUpdateIDs (child, recentlyUsedIdCache); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -85,7 +85,13 @@ void DrawableDocument::checkRootObject() | |||||
| markersY = new MarkerList (*this, false); | markersY = new MarkerList (*this, false); | ||||
| DrawableComposite::ValueTreeWrapper rootObject (getRootDrawableNode()); | DrawableComposite::ValueTreeWrapper rootObject (getRootDrawableNode()); | ||||
| recursivelyUpdateIDs (rootObject); | |||||
| StringArray idCache; | |||||
| recursivelyUpdateIDs (rootObject, idCache); | |||||
| } | |||||
| const String DrawableDocument::getUniqueId() const | |||||
| { | |||||
| return root [Ids::id_]; | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -201,26 +207,59 @@ ValueTree DrawableDocument::findDrawableState (const String& objectId, bool recu | |||||
| return getRootDrawableNode().getDrawableWithId (objectId, recursive); | return getRootDrawableNode().getDrawableWithId (objectId, recursive); | ||||
| } | } | ||||
| const String DrawableDocument::createUniqueID (const String& name) const | |||||
| const String DrawableDocument::createUniqueID (const String& name, StringArray& recentlyUsedIdCache) const | |||||
| { | { | ||||
| String n (CodeHelpers::makeValidIdentifier (name, false, true, false)); | String n (CodeHelpers::makeValidIdentifier (name, false, true, false)); | ||||
| int suffix = 2; | int suffix = 2; | ||||
| int cacheIndex = -1; | |||||
| const String withoutNumbers (n.trimCharactersAtEnd ("0123456789")); | |||||
| for (int i = 0; i < recentlyUsedIdCache.size(); ++i) | |||||
| { | |||||
| if (recentlyUsedIdCache[i].startsWith (withoutNumbers)) | |||||
| { | |||||
| cacheIndex = i; | |||||
| suffix = jmax (suffix, recentlyUsedIdCache[i].substring (withoutNumbers.length()).getIntValue() + 1); | |||||
| n = withoutNumbers + String (suffix++); | |||||
| break; | |||||
| } | |||||
| } | |||||
| while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid() | while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid() | ||||
| || findDrawableState (n, true).isValid()) | || findDrawableState (n, true).isValid()) | ||||
| n = n.trimCharactersAtEnd ("0123456789") + String (suffix++); | |||||
| n = withoutNumbers + String (suffix++); | |||||
| if (cacheIndex >= 0) | |||||
| recentlyUsedIdCache.set (cacheIndex, n); | |||||
| else | |||||
| recentlyUsedIdCache.add (n); | |||||
| return n; | return n; | ||||
| } | } | ||||
| bool DrawableDocument::createItemProperties (Array <PropertyComponent*>& props, const String& itemId) | bool DrawableDocument::createItemProperties (Array <PropertyComponent*>& props, const String& itemId) | ||||
| { | { | ||||
| ValueTree drawable (findDrawableState (itemId, false)); | |||||
| ValueTree drawable (findDrawableState (itemId.upToFirstOccurrenceOf ("/", false, false), false)); | |||||
| if (drawable.isValid()) | if (drawable.isValid()) | ||||
| { | { | ||||
| DrawableTypeInstance item (*this, drawable); | DrawableTypeInstance item (*this, drawable); | ||||
| item.createProperties (props); | |||||
| if (itemId.containsChar ('/')) | |||||
| { | |||||
| OwnedArray <ControlPoint> points; | |||||
| item.getAllControlPoints (points); | |||||
| for (int i = 0; i < points.size(); ++i) | |||||
| if (points.getUnchecked(i)->getID() == itemId) | |||||
| points.getUnchecked(i)->createProperties (*this, props); | |||||
| } | |||||
| else | |||||
| { | |||||
| item.createProperties (props); | |||||
| } | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -264,8 +303,8 @@ const ValueTree DrawableDocument::performNewItemMenuItem (int menuResultCode) | |||||
| Random::getSystemRandom().nextFloat() * 100.0f + 100.0f))); | Random::getSystemRandom().nextFloat() * 100.0f + 100.0f))); | ||||
| Drawable::ValueTreeWrapperBase wrapper (state); | Drawable::ValueTreeWrapperBase wrapper (state); | ||||
| recursivelyUpdateIDs (wrapper); | |||||
| StringArray idCache; | |||||
| recursivelyUpdateIDs (wrapper, idCache); | |||||
| getRootDrawableNode().addDrawable (state, -1, getUndoManager()); | getRootDrawableNode().addDrawable (state, -1, getUndoManager()); | ||||
| return state; | return state; | ||||
| @@ -274,15 +313,80 @@ const ValueTree DrawableDocument::performNewItemMenuItem (int menuResultCode) | |||||
| return ValueTree::invalid; | return ValueTree::invalid; | ||||
| } | } | ||||
| const ValueTree DrawableDocument::insertSVG (const File& file, const Point<float>& position) | |||||
| { | |||||
| ScopedPointer<Drawable> d (Drawable::createFromImageFile (file)); | |||||
| DrawableComposite* dc = dynamic_cast <DrawableComposite*> (static_cast <Drawable*> (d)); | |||||
| if (dc != 0) | |||||
| { | |||||
| ValueTree state (dc->createValueTree (this)); | |||||
| if (state.isValid()) | |||||
| { | |||||
| Drawable::ValueTreeWrapperBase wrapper (state); | |||||
| getRootDrawableNode().addDrawable (state, -1, getUndoManager()); | |||||
| StringArray idCache; | |||||
| recursivelyUpdateIDs (wrapper, idCache); | |||||
| return state; | |||||
| } | |||||
| } | |||||
| return ValueTree::invalid; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| const Image DrawableDocument::getImageForIdentifier (const var& imageIdentifier) | const Image DrawableDocument::getImageForIdentifier (const var& imageIdentifier) | ||||
| { | { | ||||
| return ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize); | |||||
| const String s (imageIdentifier.toString()); | |||||
| if (s.startsWithIgnoreCase ("id:")) | |||||
| { | |||||
| jassert (project != 0); | |||||
| if (project != 0) | |||||
| { | |||||
| Project::Item item (project->getMainGroup().findItemWithID (s.substring (3).trim())); | |||||
| if (item.isValid()) | |||||
| { | |||||
| Image im (ImageCache::getFromFile (item.getFile())); | |||||
| if (im.isValid()) | |||||
| { | |||||
| im.setTag (imageIdentifier); | |||||
| return im; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| static Image dummy; | |||||
| if (dummy.isNull()) | |||||
| { | |||||
| dummy = Image (Image::ARGB, 128, 128, true); | |||||
| Graphics g (dummy); | |||||
| g.fillAll (Colours::khaki.withAlpha (0.51f)); | |||||
| g.setColour (Colours::grey); | |||||
| g.drawRect (0, 0, 128, 128); | |||||
| for (int i = -128; i < 128; i += 16) | |||||
| g.drawLine (i, 0, i + 128, 128); | |||||
| g.setColour (Colours::darkgrey); | |||||
| g.drawRect (0, 0, 128, 128); | |||||
| g.setFont (16.0f, Font::bold); | |||||
| g.drawText ("(Image Missing)", 0, 0, 128, 128, Justification::centred, false); | |||||
| } | |||||
| return dummy; | |||||
| } | } | ||||
| const var DrawableDocument::getIdentifierForImage (const Image& image) | const var DrawableDocument::getIdentifierForImage (const Image& image) | ||||
| { | { | ||||
| return var::null; //xxx todo | |||||
| return image.getTag(); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -403,50 +507,40 @@ bool DrawableDocument::MarkerList::createProperties (Array <PropertyComponent*>& | |||||
| return false; | return false; | ||||
| } | } | ||||
| void DrawableDocument::addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& objectName, const String& edge, PopupMenu& menu, | |||||
| bool isAnchor1, const String& fullCoordName) | |||||
| void DrawableDocument::MarkerList::addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& name, const String& edge, PopupMenu& menu, | |||||
| bool isAnchor1, const String& fullCoordName) | |||||
| { | { | ||||
| // RelativeCoordinate requestedCoord (findNamedCoordinate (objectName, edge, coord.isHorizontal())); | |||||
| RelativeCoordinate requestedCoord (findNamedCoordinate (name, edge)); | |||||
| // menu.addItem (i, name, | |||||
| // ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)), | |||||
| // name == (isAnchor1 ? coord.getAnchor1() : coord.getAnchor2())); | |||||
| menu.addItem (i, edge.isEmpty() ? name : (name + "." + edge), | |||||
| ! (name == fullCoordName || (fullCoordName.isNotEmpty() && requestedCoord.references (fullCoordName, this))), | |||||
| name == (isAnchor1 ? coord.getAnchorName1() : coord.getAnchorName2())); | |||||
| } | } | ||||
| void DrawableDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const RelativeCoordinate& coord, PopupMenu& menu, bool isAnchor1) | void DrawableDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const RelativeCoordinate& coord, PopupMenu& menu, bool isAnchor1) | ||||
| { | { | ||||
| /* const String fullCoordName (getName (markerState)); | |||||
| const String fullCoordName (getName (markerState)); | |||||
| if (coord.isHorizontal()) | |||||
| { | |||||
| document.addMarkerMenuItem (1, coord, "parent", "left", menu, isAnchor1, fullCoordName); | |||||
| document.addMarkerMenuItem (2, coord, "parent", "right", menu, isAnchor1, fullCoordName); | |||||
| } | |||||
| if (isHorizontal()) | |||||
| addMarkerMenuItem (1, coord, "parent", "left", menu, isAnchor1, fullCoordName); | |||||
| else | else | ||||
| { | |||||
| document.addMarkerMenuItem (1, coord, "parent", "top", menu, isAnchor1, fullCoordName); | |||||
| document.addMarkerMenuItem (2, coord, "parent", "bottom", menu, isAnchor1, fullCoordName); | |||||
| } | |||||
| addMarkerMenuItem (1, coord, "parent", "top", menu, isAnchor1, fullCoordName); | |||||
| menu.addSeparator(); | 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)), | |||||
| String::empty, menu, isAnchor1, fullCoordName);*/ | |||||
| for (int i = 0; i < size(); ++i) | |||||
| addMarkerMenuItem (100 + i, coord, getName (getMarker (i)), | |||||
| String::empty, menu, isAnchor1, fullCoordName); | |||||
| } | } | ||||
| const String DrawableDocument::MarkerList::getChosenMarkerMenuItem (const RelativeCoordinate& coord, int i) const | const String DrawableDocument::MarkerList::getChosenMarkerMenuItem (const RelativeCoordinate& coord, int i) const | ||||
| { | { | ||||
| /* if (i == 1) return coord.isHorizontal() ? "parent.left" : "parent.top"; | |||||
| if (i == 2) return coord.isHorizontal() ? "parent.right" : "parent.bottom"; | |||||
| const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); | |||||
| if (i == 1) return isHorizontal() ? "parent.left" : "parent.top"; | |||||
| if (i >= 100 && i < 10000) | if (i >= 100 && i < 10000) | ||||
| return markerList.getName (markerList.getMarker (i - 100)); | |||||
| return getName (getMarker (i - 100)); | |||||
| jassertfalse;*/ | |||||
| jassertfalse; | |||||
| return String::empty; | return String::empty; | ||||
| } | } | ||||
| @@ -49,16 +49,20 @@ public: | |||||
| bool hasChangedSinceLastSave() const; | bool hasChangedSinceLastSave() const; | ||||
| void changed(); | void changed(); | ||||
| Project* getProject() const throw() { return project; } | |||||
| const String getUniqueId() const; | |||||
| ValueTree& getRoot() { return root; } | ValueTree& getRoot() { return root; } | ||||
| DrawableComposite::ValueTreeWrapper getRootDrawableNode() const; | DrawableComposite::ValueTreeWrapper getRootDrawableNode() const; | ||||
| ValueTree findDrawableState (const String& objectId, bool recursive) const; | ValueTree findDrawableState (const String& objectId, bool recursive) const; | ||||
| const String createUniqueID (const String& suggestion) const; | |||||
| const String createUniqueID (const String& suggestion, StringArray& recentlyUsedIdCache) const; | |||||
| void createItemProperties (Array <PropertyComponent*>& props, const StringArray& selectedItemIds); | void createItemProperties (Array <PropertyComponent*>& props, const StringArray& selectedItemIds); | ||||
| void addNewItemMenuItems (PopupMenu& menu) const; | void addNewItemMenuItems (PopupMenu& menu) const; | ||||
| const ValueTree performNewItemMenuItem (int menuResultCode); | const ValueTree performNewItemMenuItem (int menuResultCode); | ||||
| const ValueTree insertSVG (const File& file, const Point<float>& position); | |||||
| //============================================================================== | //============================================================================== | ||||
| class MarkerList : public MarkerListBase | class MarkerList : public MarkerListBase | ||||
| @@ -86,6 +90,9 @@ public: | |||||
| DrawableDocument& document; | DrawableDocument& document; | ||||
| DrawableComposite::ValueTreeWrapper object; | DrawableComposite::ValueTreeWrapper object; | ||||
| void addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& objectName, const String& edge, | |||||
| PopupMenu& menu, bool isAnchor1, const String& fullCoordName); | |||||
| MarkerList (const MarkerList&); | MarkerList (const MarkerList&); | ||||
| MarkerList& operator= (const MarkerList&); | MarkerList& operator= (const MarkerList&); | ||||
| }; | }; | ||||
| @@ -116,7 +123,7 @@ private: | |||||
| bool saveAsXml, needsSaving; | bool saveAsXml, needsSaving; | ||||
| void checkRootObject(); | void checkRootObject(); | ||||
| void recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d); | |||||
| void recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d, StringArray& recentlyUsedIdCache); | |||||
| Value getRootValueUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, getUndoManager()); } | Value getRootValueUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, getUndoManager()); } | ||||
| Value getRootValueNonUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, 0); } | Value getRootValueNonUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, 0); } | ||||
| @@ -127,8 +134,6 @@ private: | |||||
| bool createItemProperties (Array <PropertyComponent*>& props, const String& itemId); | bool createItemProperties (Array <PropertyComponent*>& props, const String& itemId); | ||||
| const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const; | const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const; | ||||
| void addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& objectName, const String& edge, | |||||
| PopupMenu& menu, bool isAnchor1, const String& fullCoordName); | |||||
| }; | }; | ||||
| @@ -24,7 +24,92 @@ | |||||
| */ | */ | ||||
| #include "jucer_DrawableTypeHandler.h" | #include "jucer_DrawableTypeHandler.h" | ||||
| #include "../../utility/jucer_ColourPropertyComponent.h" | |||||
| //============================================================================== | |||||
| class ControlPointPropertyComp : public CoordinatePropertyComponent | |||||
| { | |||||
| public: | |||||
| ControlPointPropertyComp (DrawableTypeInstance& item_, ControlPoint* cp, const String& name, bool isHorizontal_, UndoManager* undoManager) | |||||
| : CoordinatePropertyComponent (0, name, Value (new CoordExtractor (cp->getPositionValue (undoManager), isHorizontal_)), isHorizontal_), | |||||
| item (item_) | |||||
| { | |||||
| nameSource = &item; | |||||
| } | |||||
| ~ControlPointPropertyComp() | |||||
| { | |||||
| } | |||||
| const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) | |||||
| { | |||||
| RelativeCoordinate coord (getCoordinate()); | |||||
| PopupMenu m; | |||||
| item.getDocument().getMarkerList (isHorizontal).addMarkerMenuItems (ValueTree::invalid, coord, m, isAnchor1); | |||||
| const int r = m.showAt (button); | |||||
| if (r > 0) | |||||
| return item.getDocument().getMarkerList (isHorizontal).getChosenMarkerMenuItem (coord, r); | |||||
| return String::empty; | |||||
| } | |||||
| DrawableTypeInstance item; | |||||
| //============================================================================== | |||||
| class CoordExtractor : public Value::ValueSource, | |||||
| public Value::Listener | |||||
| { | |||||
| public: | |||||
| CoordExtractor (const Value& sourceValue_, const bool isX_) | |||||
| : sourceValue (sourceValue_), isX (isX_) | |||||
| { | |||||
| sourceValue.addListener (this); | |||||
| } | |||||
| ~CoordExtractor() {} | |||||
| const var getValue() const | |||||
| { | |||||
| RelativePoint p (sourceValue.toString()); | |||||
| return getCoord (p).toString(); | |||||
| } | |||||
| void setValue (const var& newValue) | |||||
| { | |||||
| RelativePoint p (sourceValue.toString()); | |||||
| RelativeCoordinate& coord = getCoord (p); | |||||
| coord = RelativeCoordinate (newValue.toString(), isX); | |||||
| const String newVal (p.toString()); | |||||
| if (sourceValue != newVal) | |||||
| sourceValue = newVal; | |||||
| } | |||||
| void valueChanged (Value&) | |||||
| { | |||||
| sendChangeMessage (true); | |||||
| } | |||||
| //============================================================================== | |||||
| juce_UseDebuggingNewOperator | |||||
| protected: | |||||
| Value sourceValue; | |||||
| bool isX; | |||||
| RelativeCoordinate& getCoord (RelativePoint& p) const | |||||
| { | |||||
| return isX ? p.x : p.y; | |||||
| } | |||||
| CoordExtractor (const CoordExtractor&); | |||||
| const CoordExtractor& operator= (const CoordExtractor&); | |||||
| }; | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| class DrawablePathHandler : public DrawableTypeHandler | class DrawablePathHandler : public DrawableTypeHandler | ||||
| @@ -73,7 +158,8 @@ public: | |||||
| { | { | ||||
| public: | public: | ||||
| DrawablePathFillPropComp (DrawableTypeInstance& item_, const String& name, const ValueTree& fill) | DrawablePathFillPropComp (DrawableTypeInstance& item_, const String& name, const ValueTree& fill) | ||||
| : FillTypePropertyComponent (item_.getDocument().getUndoManager(), name, fill), | |||||
| : FillTypePropertyComponent (item_.getDocument().getUndoManager(), name, fill, | |||||
| &item_.getDocument(), item_.getProject()), | |||||
| item (item_) | item (item_) | ||||
| {} | {} | ||||
| @@ -110,9 +196,9 @@ public: | |||||
| class GradientControlPoint : public ControlPoint | class GradientControlPoint : public ControlPoint | ||||
| { | { | ||||
| public: | public: | ||||
| GradientControlPoint (const ValueTree& item_, | |||||
| GradientControlPoint (const String& id_, const ValueTree& item_, | |||||
| const bool isStart_, const bool isStroke_) | const bool isStart_, const bool isStroke_) | ||||
| : item (item_), isStart (isStart_), isStroke (isStroke_) | |||||
| : ControlPoint (id_), item (item_), isStart (isStart_), isStroke (isStroke_) | |||||
| {} | {} | ||||
| ~GradientControlPoint() {} | ~GradientControlPoint() {} | ||||
| @@ -124,7 +210,8 @@ public: | |||||
| RelativePoint p; | RelativePoint p; | ||||
| const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState(), | const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState(), | ||||
| isStart ? &p : 0, | isStart ? &p : 0, | ||||
| isStart ? 0 : &p, 0)); | |||||
| isStart ? 0 : &p, 0, | |||||
| 0)); | |||||
| jassert (fill.isGradient()); | jassert (fill.isGradient()); | ||||
| return p; | return p; | ||||
| } | } | ||||
| @@ -135,7 +222,7 @@ public: | |||||
| RelativePoint p1, p2; | RelativePoint p1, p2; | ||||
| ValueTree fillState (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState()); | ValueTree fillState (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState()); | ||||
| const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (fillState, &p1, &p2, 0)); | |||||
| const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (fillState, &p1, &p2, 0, 0)); | |||||
| jassert (fill.isGradient()); | jassert (fill.isGradient()); | ||||
| if (isStart) | if (isStart) | ||||
| @@ -143,11 +230,32 @@ public: | |||||
| else | else | ||||
| p2 = newPoint; | p2 = newPoint; | ||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, fill, &p1, &p2, undoManager); | |||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, fill, &p1, &p2, 0, undoManager); | |||||
| } | } | ||||
| bool hasLine() { return false; } | |||||
| RelativePoint getEndOfLine() { return RelativePoint(); } | |||||
| bool hasLine() { return isStart; } | |||||
| RelativePoint getEndOfLine() | |||||
| { | |||||
| RelativePoint p; | |||||
| DrawablePath::ValueTreeWrapper wrapper (item); | |||||
| ValueTree fillState (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState()); | |||||
| Drawable::ValueTreeWrapperBase::readFillType (fillState, 0, &p, 0, 0); | |||||
| return p; | |||||
| } | |||||
| const Value getPositionValue (UndoManager* undoManager) | |||||
| { | |||||
| DrawablePath::ValueTreeWrapper wrapper (item); | |||||
| ValueTree fillState (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState()); | |||||
| return fillState.getPropertyAsValue (isStart ? Drawable::ValueTreeWrapperBase::gradientPoint1 : Drawable::ValueTreeWrapperBase::gradientPoint2, undoManager); | |||||
| } | |||||
| void createProperties (DrawableDocument& document, Array <PropertyComponent*>& props) | |||||
| { | |||||
| DrawableTypeInstance instance (document, item); | |||||
| props.add (new ControlPointPropertyComp (instance, this, "X", true, document.getUndoManager())); | |||||
| props.add (new ControlPointPropertyComp (instance, this, "Y", false, document.getUndoManager())); | |||||
| } | |||||
| private: | private: | ||||
| ValueTree item; | ValueTree item; | ||||
| @@ -158,8 +266,10 @@ public: | |||||
| class PathControlPoint : public ControlPoint | class PathControlPoint : public ControlPoint | ||||
| { | { | ||||
| public: | public: | ||||
| PathControlPoint (const DrawablePath::ValueTreeWrapper::Element& element_, const int cpNum_) | |||||
| : element (element_), cpNum (cpNum_) | |||||
| PathControlPoint (const String& id_, | |||||
| const DrawablePath::ValueTreeWrapper::Element& element_, | |||||
| const DrawablePath::ValueTreeWrapper::Element& previousElement_, const int cpNum_, const int numCps_) | |||||
| : ControlPoint (id_), element (element_), previousElement (previousElement_), cpNum (cpNum_), numCps (numCps_) | |||||
| {} | {} | ||||
| ~PathControlPoint() {} | ~PathControlPoint() {} | ||||
| @@ -174,45 +284,133 @@ public: | |||||
| element.setControlPoint (cpNum, newPoint, undoManager); | element.setControlPoint (cpNum, newPoint, undoManager); | ||||
| } | } | ||||
| bool hasLine() { return false; } | |||||
| RelativePoint getEndOfLine() { return RelativePoint(); } | |||||
| const Value getPositionValue (UndoManager* undoManager) | |||||
| { | |||||
| return element.getControlPointValue (cpNum, undoManager); | |||||
| } | |||||
| bool hasLine() { return numCps > 1 && cpNum == 0 || cpNum == 1; } | |||||
| RelativePoint getEndOfLine() | |||||
| { | |||||
| if (cpNum == 0) | |||||
| return previousElement.getEndPoint(); | |||||
| else | |||||
| return element.getControlPoint (2); | |||||
| } | |||||
| void createProperties (DrawableDocument& document, Array <PropertyComponent*>& props) | |||||
| { | |||||
| DrawableTypeInstance instance (document, element.getParent().getState()); | |||||
| props.add (new ControlPointPropertyComp (instance, this, "X", true, document.getUndoManager())); | |||||
| props.add (new ControlPointPropertyComp (instance, this, "Y", false, document.getUndoManager())); | |||||
| } | |||||
| private: | private: | ||||
| DrawablePath::ValueTreeWrapper::Element element; | |||||
| int cpNum; | |||||
| DrawablePath::ValueTreeWrapper::Element element, previousElement; | |||||
| int cpNum, numCps; | |||||
| }; | }; | ||||
| void getGradientControlPoints (DrawablePath::ValueTreeWrapper& wrapper, DrawableTypeInstance& item, | |||||
| OwnedArray <ControlPoint>& points, const String& itemId) | |||||
| { | |||||
| const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getMainFillState(), 0, 0, 0, 0)); | |||||
| if (fill.isGradient()) | |||||
| { | |||||
| points.add (new GradientControlPoint (itemId + "/gf1", item.getState(), true, false)); | |||||
| points.add (new GradientControlPoint (itemId + "/gf2", item.getState(), false, false)); | |||||
| } | |||||
| const FillType stroke (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getStrokeFillState(), 0, 0, 0, 0)); | |||||
| if (stroke.isGradient()) | |||||
| { | |||||
| points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), true, true)); | |||||
| points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), false, true)); | |||||
| } | |||||
| } | |||||
| void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | ||||
| { | { | ||||
| DrawablePath::ValueTreeWrapper wrapper (item.getState()); | DrawablePath::ValueTreeWrapper wrapper (item.getState()); | ||||
| const ValueTree pathTree (wrapper.getPathState()); | const ValueTree pathTree (wrapper.getPathState()); | ||||
| const int numElements = pathTree.getNumChildren(); | const int numElements = pathTree.getNumChildren(); | ||||
| const String itemId (item.getID()); | |||||
| for (int i = 0; i < numElements; ++i) | |||||
| if (numElements > 0) | |||||
| { | { | ||||
| const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); | |||||
| const int numCps = e.getNumControlPoints(); | |||||
| DrawablePath::ValueTreeWrapper::Element last (pathTree.getChild(0)); | |||||
| for (int j = 0; j < numCps; ++j) | |||||
| points.add (new PathControlPoint (e, j)); | |||||
| } | |||||
| for (int i = 0; i < numElements; ++i) | |||||
| { | |||||
| const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); | |||||
| const int numCps = e.getNumControlPoints(); | |||||
| const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getMainFillState(), 0, 0, 0)); | |||||
| for (int j = 0; j < numCps; ++j) | |||||
| points.add (new PathControlPoint (itemId + "/" + String(i) + "/" + String(j), e, last, j, numCps)); | |||||
| if (fill.isGradient()) | |||||
| { | |||||
| points.add (new GradientControlPoint (item.getState(), true, false)); | |||||
| points.add (new GradientControlPoint (item.getState(), false, false)); | |||||
| last = e; | |||||
| } | |||||
| } | } | ||||
| const FillType stroke (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getStrokeFillState(), 0, 0, 0)); | |||||
| getGradientControlPoints (wrapper, item, points, itemId); | |||||
| } | |||||
| if (stroke.isGradient()) | |||||
| void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points, const EditorCanvasBase::SelectedItems& selection) | |||||
| { | |||||
| DrawablePath::ValueTreeWrapper wrapper (item.getState()); | |||||
| const ValueTree pathTree (wrapper.getPathState()); | |||||
| const int numElements = pathTree.getNumChildren(); | |||||
| const String itemId (item.getID()); | |||||
| if (numElements > 0) | |||||
| { | { | ||||
| points.add (new GradientControlPoint (item.getState(), true, true)); | |||||
| points.add (new GradientControlPoint (item.getState(), false, true)); | |||||
| DrawablePath::ValueTreeWrapper::Element last (pathTree.getChild(0)); | |||||
| bool lastWasSelected = false; | |||||
| for (int i = 0; i < numElements; ++i) | |||||
| { | |||||
| const String elementIdRoot (itemId + "/" + String(i) + "/"); | |||||
| const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); | |||||
| int numCps = e.getNumControlPoints(); | |||||
| bool pointIsSelected = false; | |||||
| for (int k = numCps; --k >= 0;) | |||||
| { | |||||
| if (selection.isSelected (elementIdRoot + String (k))) | |||||
| { | |||||
| pointIsSelected = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (numCps > 1) | |||||
| { | |||||
| if (pointIsSelected || lastWasSelected) | |||||
| { | |||||
| for (int j = 0; j < numCps; ++j) | |||||
| points.add (new PathControlPoint (elementIdRoot + String(j), e, last, j, numCps)); | |||||
| } | |||||
| else | |||||
| { | |||||
| points.add (new PathControlPoint (elementIdRoot + String (numCps - 1), e, last, numCps - 1, numCps)); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int j = 0; j < numCps; ++j) | |||||
| points.add (new PathControlPoint (elementIdRoot + String(j), e, last, j, numCps)); | |||||
| } | |||||
| last = e; | |||||
| lastWasSelected = pointIsSelected; | |||||
| } | |||||
| } | } | ||||
| getGradientControlPoints (wrapper, item, points, itemId); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -244,6 +442,34 @@ public: | |||||
| void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | ||||
| { | { | ||||
| DrawableImage::ValueTreeWrapper wrapper (item.getState()); | |||||
| if (item.getDocument().getProject() != 0) | |||||
| { | |||||
| OwnedArray<Project::Item> images; | |||||
| item.getDocument().getProject()->findAllImageItems (images); | |||||
| StringArray choices; | |||||
| Array<var> ids; | |||||
| for (int i = 0; i < images.size(); ++i) | |||||
| { | |||||
| choices.add (images.getUnchecked(i)->getName().toString()); | |||||
| ids.add (images.getUnchecked(i)->getImageFileID()); | |||||
| } | |||||
| props.add (new ChoicePropertyComponent (wrapper.getImageIdentifierValue (item.getDocument().getUndoManager()), | |||||
| "Image", choices, ids)); | |||||
| } | |||||
| props.add (new SliderPropertyComponent (wrapper.getOpacityValue (item.getDocument().getUndoManager()), | |||||
| "Opacity", 0, 1.0, 0.001)); | |||||
| props.add (new ColourPropertyComponent (item.getDocument().getUndoManager(), "Overlay Colour", | |||||
| wrapper.getOverlayColourValue (item.getDocument().getUndoManager()), | |||||
| Colours::transparentBlack, true)); | |||||
| props.add (new ResetButtonPropertyComponent (item, wrapper)); | |||||
| } | } | ||||
| void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | ||||
| @@ -254,8 +480,8 @@ public: | |||||
| class ImageControlPoint : public ControlPoint | class ImageControlPoint : public ControlPoint | ||||
| { | { | ||||
| public: | public: | ||||
| ImageControlPoint (const DrawableTypeInstance& item_, const int cpNum_) | |||||
| : item (item_), cpNum (cpNum_) | |||||
| ImageControlPoint (const String& id_, const DrawableTypeInstance& item_, const int cpNum_) | |||||
| : ControlPoint (id_), item (item_), cpNum (cpNum_) | |||||
| {} | {} | ||||
| ~ImageControlPoint() {} | ~ImageControlPoint() {} | ||||
| @@ -288,9 +514,29 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| const Value getPositionValue (UndoManager* undoManager) | |||||
| { | |||||
| DrawableImage::ValueTreeWrapper wrapper (item.getState()); | |||||
| switch (cpNum) | |||||
| { | |||||
| case 0: return item.getState().getPropertyAsValue (DrawableImage::ValueTreeWrapper::topLeft, undoManager); | |||||
| case 1: return item.getState().getPropertyAsValue (DrawableImage::ValueTreeWrapper::topRight, undoManager); | |||||
| case 2: return item.getState().getPropertyAsValue (DrawableImage::ValueTreeWrapper::bottomLeft, undoManager); | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| return Value(); | |||||
| } | |||||
| bool hasLine() { return false; } | bool hasLine() { return false; } | ||||
| RelativePoint getEndOfLine() { return RelativePoint(); } | RelativePoint getEndOfLine() { return RelativePoint(); } | ||||
| void createProperties (DrawableDocument& document, Array <PropertyComponent*>& props) | |||||
| { | |||||
| props.add (new ControlPointPropertyComp (item, this, "X", true, document.getUndoManager())); | |||||
| props.add (new ControlPointPropertyComp (item, this, "Y", false, document.getUndoManager())); | |||||
| } | |||||
| private: | private: | ||||
| DrawableTypeInstance item; | DrawableTypeInstance item; | ||||
| int cpNum; | int cpNum; | ||||
| @@ -298,9 +544,52 @@ public: | |||||
| void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | ||||
| { | { | ||||
| const String itemIDRoot (item.getID() + "/"); | |||||
| for (int i = 0; i < 3; ++i) | for (int i = 0; i < 3; ++i) | ||||
| points.add (new ImageControlPoint (item, i)); | |||||
| points.add (new ImageControlPoint (itemIDRoot + String (i), item, i)); | |||||
| } | |||||
| void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points, const EditorCanvasBase::SelectedItems&) | |||||
| { | |||||
| return getAllControlPoints (item, points); | |||||
| } | } | ||||
| //============================================================================== | |||||
| class ResetButtonPropertyComponent : public ButtonPropertyComponent | |||||
| { | |||||
| public: | |||||
| ResetButtonPropertyComponent (DrawableTypeInstance& item_, | |||||
| const DrawableImage::ValueTreeWrapper& wrapper_) | |||||
| : ButtonPropertyComponent ("Reset", false), | |||||
| item (item_), wrapper (wrapper_) | |||||
| { | |||||
| } | |||||
| const String getButtonText() const { return "Reset to Original Size"; } | |||||
| void buttonClicked() | |||||
| { | |||||
| Image im (item.getDocument().getImageForIdentifier (wrapper.getImageIdentifier())); | |||||
| if (im.isValid()) | |||||
| { | |||||
| RelativePoint topLeft (wrapper.getTargetPositionForTopLeft()); | |||||
| RelativePoint topRight (wrapper.getTargetPositionForTopRight()); | |||||
| RelativePoint bottomLeft (wrapper.getTargetPositionForBottomLeft()); | |||||
| topRight.moveToAbsolute (topLeft.resolve (&item) + Point<float> (im.getWidth(), 0.0f), &item); | |||||
| bottomLeft.moveToAbsolute (topLeft.resolve (&item) + Point<float> (0.0f, im.getHeight()), &item); | |||||
| wrapper.setTargetPositionForTopRight (topRight, item.getDocument().getUndoManager()); | |||||
| wrapper.setTargetPositionForBottomLeft (bottomLeft, item.getDocument().getUndoManager()); | |||||
| } | |||||
| } | |||||
| private: | |||||
| DrawableTypeInstance item; | |||||
| DrawableImage::ValueTreeWrapper wrapper; | |||||
| }; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -312,33 +601,20 @@ public: | |||||
| void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | ||||
| { | { | ||||
| DrawableComposite::ValueTreeWrapper wrapper (item.getState()); | |||||
| props.add (new ResetButtonPropertyComponent (item, wrapper)); | |||||
| } | } | ||||
| void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | ||||
| { | { | ||||
| } | } | ||||
| const RelativeCoordinate findNamedCoordinate (const DrawableTypeInstance& item, const String& objectName, const String& edge) const | |||||
| { | |||||
| DrawableComposite::ValueTreeWrapper wrapper (const_cast <DrawableTypeInstance&> (item).getState()); | |||||
| ValueTree markerState (wrapper.getMarkerState (true, objectName)); | |||||
| if (markerState.isValid()) | |||||
| return wrapper.getMarker (true, markerState).position; | |||||
| markerState = wrapper.getMarkerState (false, objectName); | |||||
| if (markerState.isValid()) | |||||
| return wrapper.getMarker (false, markerState).position; | |||||
| return RelativeCoordinate(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| class CompositeControlPoint : public ControlPoint | class CompositeControlPoint : public ControlPoint | ||||
| { | { | ||||
| public: | public: | ||||
| CompositeControlPoint (const ValueTree& item_, const int cpNum_) | |||||
| : item (item_), cpNum (cpNum_) | |||||
| CompositeControlPoint (const String& id_, const ValueTree& item_, const int cpNum_) | |||||
| : ControlPoint (id_), item (item_), cpNum (cpNum_) | |||||
| {} | {} | ||||
| ~CompositeControlPoint() {} | ~CompositeControlPoint() {} | ||||
| @@ -371,9 +647,22 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| const Value getPositionValue (UndoManager* undoManager) | |||||
| { | |||||
| jassertfalse | |||||
| return Value(); | |||||
| } | |||||
| bool hasLine() { return false; } | bool hasLine() { return false; } | ||||
| RelativePoint getEndOfLine() { return RelativePoint(); } | RelativePoint getEndOfLine() { return RelativePoint(); } | ||||
| void createProperties (DrawableDocument& document, Array <PropertyComponent*>& props) | |||||
| { | |||||
| DrawableTypeInstance instance (document, item); | |||||
| props.add (new ControlPointPropertyComp (instance, this, "X", true, document.getUndoManager())); | |||||
| props.add (new ControlPointPropertyComp (instance, this, "Y", false, document.getUndoManager())); | |||||
| } | |||||
| private: | private: | ||||
| ValueTree item; | ValueTree item; | ||||
| int cpNum; | int cpNum; | ||||
| @@ -381,9 +670,47 @@ public: | |||||
| void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | ||||
| { | { | ||||
| const String itemIDRoot (item.getID() + "/"); | |||||
| for (int i = 0; i < 3; ++i) | for (int i = 0; i < 3; ++i) | ||||
| points.add (new CompositeControlPoint (item.getState(), i)); | |||||
| points.add (new CompositeControlPoint (itemIDRoot + String(i), item.getState(), i)); | |||||
| } | |||||
| void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points, const EditorCanvasBase::SelectedItems&) | |||||
| { | |||||
| return getAllControlPoints (item, points); | |||||
| } | } | ||||
| //============================================================================== | |||||
| class ResetButtonPropertyComponent : public ButtonPropertyComponent | |||||
| { | |||||
| public: | |||||
| ResetButtonPropertyComponent (DrawableTypeInstance& item_, | |||||
| const DrawableComposite::ValueTreeWrapper& wrapper_) | |||||
| : ButtonPropertyComponent ("Reset", false), | |||||
| item (item_), wrapper (wrapper_) | |||||
| { | |||||
| } | |||||
| const String getButtonText() const { return "Reset to Original Size"; } | |||||
| void buttonClicked() | |||||
| { | |||||
| RelativePoint topLeft (wrapper.getTargetPositionForOrigin()); | |||||
| RelativePoint topRight (wrapper.getTargetPositionForX1Y0()); | |||||
| RelativePoint bottomLeft (wrapper.getTargetPositionForX0Y1()); | |||||
| topRight.moveToAbsolute (topLeft.resolve (&item) + Point<float> (1.0f, 0.0f), &item); | |||||
| bottomLeft.moveToAbsolute (topLeft.resolve (&item) + Point<float> (0.0f, 1.0f), &item); | |||||
| wrapper.setTargetPositionForX1Y0 (topRight, item.getDocument().getUndoManager()); | |||||
| wrapper.setTargetPositionForX0Y1 (bottomLeft, item.getDocument().getUndoManager()); | |||||
| } | |||||
| private: | |||||
| DrawableTypeInstance item; | |||||
| DrawableComposite::ValueTreeWrapper wrapper; | |||||
| }; | |||||
| }; | }; | ||||
| @@ -459,7 +786,21 @@ DrawableTypeHandler* DrawableTypeInstance::getHandler() const | |||||
| const RelativeCoordinate DrawableTypeInstance::findNamedCoordinate (const String& objectName, const String& edge) const | const RelativeCoordinate DrawableTypeInstance::findNamedCoordinate (const String& objectName, const String& edge) const | ||||
| { | { | ||||
| return getHandler()->findNamedCoordinate (*this, objectName, edge); | |||||
| ValueTree v (state); | |||||
| while (v.getParent().isValid() && ! v.hasType (DrawableComposite::valueTreeType)) | |||||
| v = v.getParent(); | |||||
| DrawableComposite::ValueTreeWrapper wrapper (v); | |||||
| ValueTree markerState (wrapper.getMarkerState (true, objectName)); | |||||
| if (markerState.isValid()) | |||||
| return wrapper.getMarker (true, markerState).position; | |||||
| markerState = wrapper.getMarkerState (false, objectName); | |||||
| if (markerState.isValid()) | |||||
| return wrapper.getMarker (false, markerState).position; | |||||
| return RelativeCoordinate(); | |||||
| } | } | ||||
| const Rectangle<float> DrawableTypeInstance::getBounds() | const Rectangle<float> DrawableTypeInstance::getBounds() | ||||
| @@ -485,7 +826,28 @@ void DrawableTypeInstance::setBounds (Drawable* drawable, const Rectangle<float> | |||||
| return getHandler()->setBounds (*this, drawable, newBounds); | return getHandler()->setBounds (*this, drawable, newBounds); | ||||
| } | } | ||||
| void DrawableTypeInstance::applyTransform (Drawable* drawable, const AffineTransform& transform) | |||||
| { | |||||
| OwnedArray <ControlPoint> points; | |||||
| getAllControlPoints (points); | |||||
| for (int i = points.size(); --i >= 0;) | |||||
| { | |||||
| RelativePoint rp (points.getUnchecked(i)->getPosition()); | |||||
| Point<float> p (rp.resolve (drawable->getParent())); | |||||
| p.applyTransform (transform); | |||||
| rp.moveToAbsolute (p, drawable->getParent()); | |||||
| points.getUnchecked(i)->setPosition (rp, document.getUndoManager()); | |||||
| } | |||||
| } | |||||
| void DrawableTypeInstance::getAllControlPoints (OwnedArray <ControlPoint>& points) | void DrawableTypeInstance::getAllControlPoints (OwnedArray <ControlPoint>& points) | ||||
| { | { | ||||
| return getHandler()->getAllControlPoints (*this, points); | return getHandler()->getAllControlPoints (*this, points); | ||||
| } | } | ||||
| void DrawableTypeInstance::getVisibleControlPoints (OwnedArray <ControlPoint>& points, const EditorCanvasBase::SelectedItems& selection) | |||||
| { | |||||
| return getHandler()->getVisibleControlPoints (*this, points, selection); | |||||
| } | |||||
| @@ -28,22 +28,36 @@ | |||||
| #include "jucer_DrawableDocument.h" | #include "jucer_DrawableDocument.h" | ||||
| #include "../../utility/jucer_FillTypePropertyComponent.h" | #include "../../utility/jucer_FillTypePropertyComponent.h" | ||||
| #include "../../ui/Editor Base/jucer_EditorCanvas.h" | |||||
| class DrawableTypeHandler; | class DrawableTypeHandler; | ||||
| //============================================================================== | //============================================================================== | ||||
| class ControlPoint | class ControlPoint | ||||
| { | { | ||||
| public: | public: | ||||
| ControlPoint() {} | |||||
| ControlPoint (const String& pointID_) : pointID (pointID_) {} | |||||
| virtual ~ControlPoint() {} | virtual ~ControlPoint() {} | ||||
| const String& getID() const throw() { return pointID; } | |||||
| virtual const RelativePoint getPosition() = 0; | virtual const RelativePoint getPosition() = 0; | ||||
| virtual void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) = 0; | virtual void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) = 0; | ||||
| virtual bool hasLine() = 0; | virtual bool hasLine() = 0; | ||||
| virtual RelativePoint getEndOfLine() = 0; | virtual RelativePoint getEndOfLine() = 0; | ||||
| virtual const Value getPositionValue (UndoManager* undoManager) = 0; | |||||
| virtual void createProperties (DrawableDocument& document, Array <PropertyComponent*>& props) = 0; | |||||
| private: | |||||
| const String pointID; | |||||
| ControlPoint (const ControlPoint&); | |||||
| ControlPoint& operator= (const ControlPoint&); | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| class DrawableTypeInstance : public RelativeCoordinate::NamedCoordinateFinder | class DrawableTypeInstance : public RelativeCoordinate::NamedCoordinateFinder | ||||
| { | { | ||||
| @@ -52,13 +66,17 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| DrawableDocument& getDocument() throw() { return document; } | DrawableDocument& getDocument() throw() { return document; } | ||||
| Project* getProject() { return document.getProject(); } | |||||
| ValueTree& getState() throw() { return state; } | ValueTree& getState() throw() { return state; } | ||||
| const String getID() const { return Drawable::ValueTreeWrapperBase (state).getID(); } | |||||
| Value getValue (const Identifier& name) const; | Value getValue (const Identifier& name) const; | ||||
| void createProperties (Array <PropertyComponent*>& props); | void createProperties (Array <PropertyComponent*>& props); | ||||
| const Rectangle<float> getBounds(); | const Rectangle<float> getBounds(); | ||||
| void setBounds (Drawable* drawable, const Rectangle<float>& newBounds); | void setBounds (Drawable* drawable, const Rectangle<float>& newBounds); | ||||
| void applyTransform (Drawable* drawable, const AffineTransform& transform); | |||||
| void getAllControlPoints (OwnedArray <ControlPoint>& points); | void getAllControlPoints (OwnedArray <ControlPoint>& points); | ||||
| void getVisibleControlPoints (OwnedArray <ControlPoint>& points, const EditorCanvasBase::SelectedItems& selection); | |||||
| const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const; | const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const; | ||||
| @@ -87,7 +105,7 @@ public: | |||||
| virtual void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) = 0; | virtual void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) = 0; | ||||
| virtual void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) = 0; | virtual void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) = 0; | ||||
| virtual void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) = 0; | virtual void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) = 0; | ||||
| virtual const RelativeCoordinate findNamedCoordinate (const DrawableTypeInstance& item, const String& objectName, const String& edge) const { return RelativeCoordinate(); } | |||||
| virtual void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points, const EditorCanvasBase::SelectedItems& selection) = 0; | |||||
| const String& getDisplayName() const { return displayName; } | const String& getDisplayName() const { return displayName; } | ||||
| const Identifier& getValueTreeType() const { return valueTreeType; } | const Identifier& getValueTreeType() const { return valueTreeType; } | ||||
| @@ -411,6 +411,25 @@ Project::Item Project::createNewItem (const File& file) | |||||
| return item; | return item; | ||||
| } | } | ||||
| static void findImages (const Project::Item& item, OwnedArray<Project::Item>& found) | |||||
| { | |||||
| if (item.isFile()) | |||||
| { | |||||
| if (item.getFile().hasFileExtension ("png;jpg;jpeg;gif")) | |||||
| found.add (new Project::Item (item)); | |||||
| } | |||||
| else if (item.isGroup()) | |||||
| { | |||||
| for (int i = 0; i < item.getNumChildren(); ++i) | |||||
| findImages (item.getChild (i), found); | |||||
| } | |||||
| } | |||||
| void Project::findAllImageItems (OwnedArray<Project::Item>& items) | |||||
| { | |||||
| findImages (getMainGroup(), items); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| Project::Item::Item (Project& project_, const ValueTree& node_) | Project::Item::Item (Project& project_, const ValueTree& node_) | ||||
| : project (project_), node (node_) | : project (project_), node (node_) | ||||
| @@ -426,7 +445,8 @@ Project::Item::~Item() | |||||
| { | { | ||||
| } | } | ||||
| const String Project::Item::getID() const { return node [Ids::id_]; } | |||||
| const String Project::Item::getID() const { return node [Ids::id_]; } | |||||
| const String Project::Item::getImageFileID() const { return "id:" + getID(); } | |||||
| bool Project::Item::isFile() const { return node.hasType (Tags::file); } | bool Project::Item::isFile() const { return node.hasType (Tags::file); } | ||||
| bool Project::Item::isGroup() const { return node.hasType (Tags::group) || isMainGroup(); } | bool Project::Item::isGroup() const { return node.hasType (Tags::group) || isMainGroup(); } | ||||
| @@ -166,6 +166,7 @@ public: | |||||
| const String getID() const; | const String getID() const; | ||||
| Item findItemWithID (const String& targetId) const; // (recursive search) | Item findItemWithID (const String& targetId) const; // (recursive search) | ||||
| const String getImageFileID() const; | |||||
| //============================================================================== | //============================================================================== | ||||
| Value getName() const; | Value getName() const; | ||||
| @@ -207,6 +208,8 @@ public: | |||||
| Item createNewGroup(); | Item createNewGroup(); | ||||
| Item createNewItem (const File& file); | Item createNewItem (const File& file); | ||||
| void findAllImageItems (OwnedArray<Item>& items); | |||||
| //============================================================================== | //============================================================================== | ||||
| class BuildConfiguration | class BuildConfiguration | ||||
| { | { | ||||
| @@ -175,10 +175,10 @@ public: | |||||
| { | { | ||||
| public: | public: | ||||
| DragOperation (ComponentEditorCanvas* canvas_, | DragOperation (ComponentEditorCanvas* canvas_, | ||||
| const MouseEvent& e, const Point<int>& mousePos, | |||||
| const Point<int>& mousePos, | |||||
| Component* snapGuideParentComp_, | Component* snapGuideParentComp_, | ||||
| const ResizableBorderComponent::Zone& zone_) | const ResizableBorderComponent::Zone& zone_) | ||||
| : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_) | |||||
| : EditorDragOperation (canvas_, mousePos, snapGuideParentComp_, zone_, false) | |||||
| { | { | ||||
| } | } | ||||
| @@ -262,12 +262,18 @@ public: | |||||
| ComponentDocument& doc = getDocument(); | ComponentDocument& doc = getDocument(); | ||||
| return (float) doc.getMarkerList (isX).getCoordinate (marker).resolve (&doc); | return (float) doc.getMarkerList (isX).getCoordinate (marker).resolve (&doc); | ||||
| } | } | ||||
| void transformObject (ValueTree& state, const AffineTransform& transform) | |||||
| { | |||||
| } | |||||
| }; | }; | ||||
| DragOperation* createDragOperation (const MouseEvent& e, Component* snapGuideParentComponent, | |||||
| const ResizableBorderComponent::Zone& zone) | |||||
| bool canRotate() const { return false; } | |||||
| DragOperation* createDragOperation (const Point<int>& mouseDownPos, Component* snapGuideParentComponent, | |||||
| const ResizableBorderComponent::Zone& zone, bool isRotating) | |||||
| { | { | ||||
| DragOperation* d = new DragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone); | |||||
| DragOperation* d = new DragOperation (this, mouseDownPos, snapGuideParentComponent, zone); | |||||
| Array<ValueTree> selected, unselected; | Array<ValueTree> selected, unselected; | ||||
| @@ -115,14 +115,57 @@ const StringArray DrawableEditor::getSelectedIds() const | |||||
| void DrawableEditor::deleteSelection() | void DrawableEditor::deleteSelection() | ||||
| { | { | ||||
| getUndoManager()->beginNewTransaction(); | |||||
| DrawableComposite::ValueTreeWrapper root (getDocument().getRootDrawableNode()); | |||||
| const StringArray ids (getSelectedIds()); | |||||
| for (int i = ids.size(); --i >= 0;) | |||||
| { | |||||
| const ValueTree v (root.getDrawableWithId (ids[i], false)); | |||||
| root.removeDrawable (v, getUndoManager()); | |||||
| } | |||||
| getUndoManager()->beginNewTransaction(); | |||||
| } | } | ||||
| void DrawableEditor::selectionToFront() | void DrawableEditor::selectionToFront() | ||||
| { | { | ||||
| getUndoManager()->beginNewTransaction(); | |||||
| DrawableComposite::ValueTreeWrapper root (getDocument().getRootDrawableNode()); | |||||
| int index = 0; | |||||
| for (int i = root.getNumDrawables(); --i >= 0;) | |||||
| { | |||||
| const Drawable::ValueTreeWrapperBase d (root.getDrawableState (index)); | |||||
| if (getSelection().isSelected (d.getID())) | |||||
| root.moveDrawableOrder (index, -1, getUndoManager()); | |||||
| else | |||||
| ++index; | |||||
| } | |||||
| getUndoManager()->beginNewTransaction(); | |||||
| } | } | ||||
| void DrawableEditor::selectionToBack() | void DrawableEditor::selectionToBack() | ||||
| { | { | ||||
| getUndoManager()->beginNewTransaction(); | |||||
| DrawableComposite::ValueTreeWrapper root (getDocument().getRootDrawableNode()); | |||||
| int index = root.getNumDrawables() - 1; | |||||
| for (int i = root.getNumDrawables(); --i >= 0;) | |||||
| { | |||||
| const Drawable::ValueTreeWrapperBase d (root.getDrawableState (index)); | |||||
| if (getSelection().isSelected (d.getID())) | |||||
| root.moveDrawableOrder (index, 0, getUndoManager()); | |||||
| else | |||||
| --index; | |||||
| } | |||||
| getUndoManager()->beginNewTransaction(); | |||||
| } | } | ||||
| void DrawableEditor::showNewShapeMenu (Component* componentToAttachTo) | void DrawableEditor::showNewShapeMenu (Component* componentToAttachTo) | ||||
| @@ -130,7 +173,11 @@ void DrawableEditor::showNewShapeMenu (Component* componentToAttachTo) | |||||
| PopupMenu m; | PopupMenu m; | ||||
| getDocument().addNewItemMenuItems (m); | getDocument().addNewItemMenuItems (m); | ||||
| const int r = m.showAt (componentToAttachTo); | const int r = m.showAt (componentToAttachTo); | ||||
| getDocument().performNewItemMenuItem (r); | |||||
| ValueTree newItem (getDocument().performNewItemMenuItem (r)); | |||||
| if (newItem.isValid()) | |||||
| getSelection().selectOnly (Drawable::ValueTreeWrapperBase (newItem).getID()); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -213,13 +260,13 @@ bool DrawableEditor::perform (const InvocationInfo& info) | |||||
| switch (info.commandID) | switch (info.commandID) | ||||
| { | { | ||||
| case CommandIDs::undo: | case CommandIDs::undo: | ||||
| getDocument().getUndoManager()->beginNewTransaction(); | |||||
| getDocument().getUndoManager()->undo(); | |||||
| getUndoManager()->beginNewTransaction(); | |||||
| getUndoManager()->undo(); | |||||
| return true; | return true; | ||||
| case CommandIDs::redo: | case CommandIDs::redo: | ||||
| getDocument().getUndoManager()->beginNewTransaction(); | |||||
| getDocument().getUndoManager()->redo(); | |||||
| getUndoManager()->beginNewTransaction(); | |||||
| getUndoManager()->redo(); | |||||
| return true; | return true; | ||||
| case CommandIDs::toFront: | case CommandIDs::toFront: | ||||
| @@ -59,6 +59,7 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| DrawableDocument& getDocument() const { return *drawableDocument; } | DrawableDocument& getDocument() const { return *drawableDocument; } | ||||
| UndoManager* getUndoManager() const { return getDocument().getUndoManager(); } | |||||
| EditorCanvasBase::SelectedItems& getSelection() { return selection; } | EditorCanvasBase::SelectedItems& getSelection() { return selection; } | ||||
| @@ -32,6 +32,7 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| class DrawableEditorCanvas : public EditorCanvasBase, | class DrawableEditorCanvas : public EditorCanvasBase, | ||||
| public FileDragAndDropTarget, | |||||
| public Timer | public Timer | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -124,9 +125,7 @@ public: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| getDocument().addNewItemMenuItems (m); | |||||
| const int r = m.show(); | |||||
| getDocument().performNewItemMenuItem (r); | |||||
| editor.showNewShapeMenu (0); | |||||
| } | } | ||||
| } | } | ||||
| @@ -210,6 +209,21 @@ public: | |||||
| return getObjectPositionFloat (state).getSmallestIntegerContainer(); | return getObjectPositionFloat (state).getSmallestIntegerContainer(); | ||||
| } | } | ||||
| void transformObject (ValueTree& state, const AffineTransform& transform) | |||||
| { | |||||
| if (drawable != 0) | |||||
| { | |||||
| Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (state).getID()); | |||||
| if (d != 0) | |||||
| { | |||||
| d->refreshFromValueTree (state, &getDocument()); | |||||
| DrawableTypeInstance di (getDocument(), state); | |||||
| di.applyTransform (d, transform); | |||||
| } | |||||
| } | |||||
| } | |||||
| RelativeRectangle getObjectCoords (const ValueTree& state) | RelativeRectangle getObjectCoords (const ValueTree& state) | ||||
| { | { | ||||
| return RelativeRectangle(); | return RelativeRectangle(); | ||||
| @@ -221,9 +235,10 @@ public: | |||||
| public: | public: | ||||
| ControlPointComponent (DrawableEditorCanvas* canvas, const ValueTree& drawableState_, int controlPointNum_) | ControlPointComponent (DrawableEditorCanvas* canvas, const ValueTree& drawableState_, int controlPointNum_) | ||||
| : OverlayItemComponent (canvas), drawableState (drawableState_), | : OverlayItemComponent (canvas), drawableState (drawableState_), | ||||
| controlPointNum (controlPointNum_), isDragging (false), mouseDownResult (false), selected (false) | |||||
| controlPointNum (controlPointNum_), isDragging (false), mouseDownResult (false), selected (false), | |||||
| sizeNormal (7), sizeOver (11) | |||||
| { | { | ||||
| selectionId = getControlPointId (drawableState, controlPointNum); | |||||
| setRepaintsOnMouseActivity (true); | |||||
| } | } | ||||
| ~ControlPointComponent() | ~ControlPointComponent() | ||||
| @@ -232,11 +247,24 @@ public: | |||||
| void paint (Graphics& g) | void paint (Graphics& g) | ||||
| { | { | ||||
| Rectangle<int> r (getLocalBounds()); | |||||
| if (! isMouseOverOrDragging()) | |||||
| r = r.reduced ((sizeOver - sizeNormal) / 2, (sizeOver - sizeNormal) / 2); | |||||
| g.setColour (Colour (selected ? 0xaaaaaaaa : 0xaa333333)); | g.setColour (Colour (selected ? 0xaaaaaaaa : 0xaa333333)); | ||||
| g.drawRect (0, 0, getWidth(), getHeight()); | |||||
| g.drawRect (r); | |||||
| g.setColour (Colour (selected ? 0xaa000000 : 0x99ffffff)); | g.setColour (Colour (selected ? 0xaa000000 : 0x99ffffff)); | ||||
| g.fillRect (1, 1, getWidth() - 2, getHeight() - 2); | |||||
| g.fillRect (r.reduced (1, 1)); | |||||
| } | |||||
| bool hitTest (int x, int y) | |||||
| { | |||||
| if (isMouseOverOrDragging()) | |||||
| return true; | |||||
| return getLocalBounds().reduced ((sizeOver - sizeNormal) / 2, (sizeOver - sizeNormal) / 2).contains (x, y); | |||||
| } | } | ||||
| void mouseDown (const MouseEvent& e) | void mouseDown (const MouseEvent& e) | ||||
| @@ -261,7 +289,7 @@ public: | |||||
| isDragging = true; | isDragging = true; | ||||
| canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), | canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), | ||||
| ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | |||||
| ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre), false, Point<float>()); | |||||
| } | } | ||||
| if (isDragging) | if (isDragging) | ||||
| @@ -273,9 +301,12 @@ public: | |||||
| void mouseUp (const MouseEvent& e) | void mouseUp (const MouseEvent& e) | ||||
| { | { | ||||
| if (isDragging) | |||||
| if (! e.mods.isPopupMenu()) | |||||
| { | { | ||||
| canvas->endDrag (e.getEventRelativeTo (getParentComponent())); | |||||
| if (isDragging) | |||||
| canvas->endDrag (e.getEventRelativeTo (getParentComponent())); | |||||
| else | |||||
| canvas->getSelection().addToSelectionOnMouseUp (selectionId, e.mods, false, mouseDownResult); | |||||
| } | } | ||||
| } | } | ||||
| @@ -283,10 +314,50 @@ public: | |||||
| { | { | ||||
| } | } | ||||
| class LineComponent : public OverlayItemComponent | |||||
| { | |||||
| public: | |||||
| LineComponent (EditorCanvasBase* canvas) | |||||
| : OverlayItemComponent (canvas) | |||||
| {} | |||||
| ~LineComponent() {} | |||||
| void setLine (const Line<float>& newLine) | |||||
| { | |||||
| if (line != newLine) | |||||
| { | |||||
| line = newLine; | |||||
| setBoundsInTargetSpace (Rectangle<float> (line.getStart(), line.getEnd()) | |||||
| .getSmallestIntegerContainer().expanded (2, 2)); | |||||
| repaint(); | |||||
| } | |||||
| } | |||||
| void paint (Graphics& g) | |||||
| { | |||||
| g.setColour (Colours::black.withAlpha (0.6f)); | |||||
| g.drawLine (Line<float> (pointToLocalSpace (line.getStart()), | |||||
| pointToLocalSpace (line.getEnd())), 1.0f); | |||||
| } | |||||
| bool hitTest (int, int) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| private: | |||||
| Line<float> line; | |||||
| }; | |||||
| void updatePosition (ControlPoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder) | void updatePosition (ControlPoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder) | ||||
| { | { | ||||
| selectionId = point.getID(); | |||||
| const Point<float> p (point.getPosition().resolve (nameFinder)); | const Point<float> p (point.getPosition().resolve (nameFinder)); | ||||
| setBoundsInTargetSpace (Rectangle<int> (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 7, 7)); | |||||
| setBoundsInTargetSpace (Rectangle<int> (roundToInt (p.getX()) - sizeOver / 2, | |||||
| roundToInt (p.getY()) - sizeOver / 2, | |||||
| sizeOver, sizeOver)); | |||||
| const bool nowSelected = canvas->getSelection().isSelected (selectionId); | const bool nowSelected = canvas->getSelection().isSelected (selectionId); | ||||
| @@ -295,6 +366,21 @@ public: | |||||
| selected = nowSelected; | selected = nowSelected; | ||||
| repaint(); | repaint(); | ||||
| } | } | ||||
| if (point.hasLine()) | |||||
| { | |||||
| if (line == 0) | |||||
| { | |||||
| line = new LineComponent (canvas); | |||||
| getParentComponent()->addAndMakeVisible (line, 0); | |||||
| } | |||||
| line->setLine (Line<float> (p, point.getEndOfLine().resolve (nameFinder))); | |||||
| } | |||||
| else | |||||
| { | |||||
| line = 0; | |||||
| } | |||||
| } | } | ||||
| private: | private: | ||||
| @@ -302,6 +388,8 @@ public: | |||||
| int controlPointNum; | int controlPointNum; | ||||
| bool isDragging, mouseDownResult, selected; | bool isDragging, mouseDownResult, selected; | ||||
| String selectionId; | String selectionId; | ||||
| ScopedPointer <LineComponent> line; | |||||
| const int sizeNormal, sizeOver; | |||||
| }; | }; | ||||
| void updateControlPointComponents (Component* parent, OwnedArray<OverlayItemComponent>& comps) | void updateControlPointComponents (Component* parent, OwnedArray<OverlayItemComponent>& comps) | ||||
| @@ -314,7 +402,7 @@ public: | |||||
| DrawableTypeInstance item (getDocument(), controlPointEditingTarget); | DrawableTypeInstance item (getDocument(), controlPointEditingTarget); | ||||
| OwnedArray <ControlPoint> points; | OwnedArray <ControlPoint> points; | ||||
| item.getAllControlPoints (points); | |||||
| item.getVisibleControlPoints (points, getSelection()); | |||||
| Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); | Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); | ||||
| DrawableComposite* parentDrawable = d->getParent(); | DrawableComposite* parentDrawable = d->getParent(); | ||||
| @@ -366,12 +454,31 @@ public: | |||||
| if (drawable != 0) | if (drawable != 0) | ||||
| { | { | ||||
| for (int i = drawable->getNumDrawables(); --i >= 0;) | |||||
| if (isControlPointMode()) | |||||
| { | { | ||||
| Drawable* d = drawable->getDrawable (i); | |||||
| DrawableTypeInstance item (getDocument(), controlPointEditingTarget); | |||||
| OwnedArray <ControlPoint> points; | |||||
| item.getVisibleControlPoints (points, getSelection()); | |||||
| const Rectangle<float> floatArea (area.toFloat()); | |||||
| for (int i = 0; i < points.size(); ++i) | |||||
| { | |||||
| const Point<float> p (points.getUnchecked(i)->getPosition().resolve (drawable)); | |||||
| if (floatArea.contains (p)) | |||||
| itemsFound.add (points.getUnchecked(i)->getID()); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = drawable->getNumDrawables(); --i >= 0;) | |||||
| { | |||||
| Drawable* d = drawable->getDrawable (i); | |||||
| if (d->getBounds().intersects (floatArea)) | |||||
| itemsFound.add (d->getName()); | |||||
| if (d->getBounds().intersects (floatArea)) | |||||
| itemsFound.add (d->getName()); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -381,20 +488,14 @@ public: | |||||
| return itemId.containsChar ('/'); | return itemId.containsChar ('/'); | ||||
| } | } | ||||
| static const String getControlPointId (const ValueTree& drawableState, int index) | |||||
| { | |||||
| return Drawable::ValueTreeWrapperBase (drawableState).getID() + "/" + String (index); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| class ObjectDragOperation : public EditorDragOperation | class ObjectDragOperation : public EditorDragOperation | ||||
| { | { | ||||
| public: | public: | ||||
| ObjectDragOperation (DrawableEditorCanvas* canvas_, | |||||
| const MouseEvent& e, const Point<int>& mousePos, | |||||
| Component* snapGuideParentComp_, | |||||
| const ResizableBorderComponent::Zone& zone_) | |||||
| : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), drawableCanvas (canvas_) | |||||
| ObjectDragOperation (DrawableEditorCanvas* canvas_, const Point<int>& mousePos, | |||||
| Component* snapGuideParentComp_, const ResizableBorderComponent::Zone& zone_, bool isRotating) | |||||
| : EditorDragOperation (canvas_, mousePos, snapGuideParentComp_, zone_, isRotating), | |||||
| drawableCanvas (canvas_) | |||||
| { | { | ||||
| } | } | ||||
| @@ -423,6 +524,11 @@ public: | |||||
| drawableCanvas->setObjectPositionFloat (state, newBounds); | drawableCanvas->setObjectPositionFloat (state, newBounds); | ||||
| } | } | ||||
| void transformObject (ValueTree& state, const AffineTransform& transform) | |||||
| { | |||||
| drawableCanvas->transformObject (state, transform); | |||||
| } | |||||
| float getMarkerPosition (const ValueTree& marker, bool isX) | float getMarkerPosition (const ValueTree& marker, bool isX) | ||||
| { | { | ||||
| return 0; | return 0; | ||||
| @@ -438,14 +544,14 @@ public: | |||||
| public: | public: | ||||
| ControlPointDragOperation (DrawableEditorCanvas* canvas_, | ControlPointDragOperation (DrawableEditorCanvas* canvas_, | ||||
| const DrawableTypeInstance& drawableItem_, | const DrawableTypeInstance& drawableItem_, | ||||
| Drawable* drawable_, | |||||
| const MouseEvent& e, const Point<int>& mousePos, | |||||
| DrawableComposite* drawable_, | |||||
| const Point<int>& mousePos, | |||||
| Component* snapGuideParentComp_, | Component* snapGuideParentComp_, | ||||
| const ResizableBorderComponent::Zone& zone_) | const ResizableBorderComponent::Zone& zone_) | ||||
| : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), | |||||
| : EditorDragOperation (canvas_, mousePos, snapGuideParentComp_, zone_, false), | |||||
| drawableCanvas (canvas_), drawableItem (drawableItem_), drawable (drawable_) | drawableCanvas (canvas_), drawableItem (drawableItem_), drawable (drawable_) | ||||
| { | { | ||||
| drawableItem.getAllControlPoints (points); | |||||
| drawableItem.getVisibleControlPoints (points, canvas_->getSelection()); | |||||
| } | } | ||||
| ~ControlPointDragOperation() {} | ~ControlPointDragOperation() {} | ||||
| @@ -467,27 +573,31 @@ public: | |||||
| const Rectangle<float> getObjectPosition (const ValueTree& state) | const Rectangle<float> getObjectPosition (const ValueTree& state) | ||||
| { | { | ||||
| int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue(); | |||||
| int index = state [Ids::id_]; | |||||
| ControlPoint* cp = points[index]; | ControlPoint* cp = points[index]; | ||||
| if (cp == 0) | if (cp == 0) | ||||
| return Rectangle<float>(); | return Rectangle<float>(); | ||||
| Point<float> p (cp->getPosition().resolve (drawable->getParent())); | |||||
| Point<float> p (cp->getPosition().resolve (drawable)); | |||||
| return Rectangle<float> (p, p); | return Rectangle<float> (p, p); | ||||
| } | } | ||||
| void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds) | void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds) | ||||
| { | { | ||||
| int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue(); | |||||
| int index = state [Ids::id_]; | |||||
| ControlPoint* cp = points[index]; | ControlPoint* cp = points[index]; | ||||
| if (cp != 0) | if (cp != 0) | ||||
| { | { | ||||
| RelativePoint p (cp->getPosition()); | RelativePoint p (cp->getPosition()); | ||||
| p.moveToAbsolute (newBounds.getPosition(), drawable->getParent()); | |||||
| p.moveToAbsolute (newBounds.getPosition(), drawable); | |||||
| cp->setPosition (p, getDocument().getUndoManager()); | cp->setPosition (p, getDocument().getUndoManager()); | ||||
| } | } | ||||
| } | } | ||||
| void transformObject (ValueTree& state, const AffineTransform& transform) | |||||
| { | |||||
| } | |||||
| float getMarkerPosition (const ValueTree& marker, bool isX) | float getMarkerPosition (const ValueTree& marker, bool isX) | ||||
| { | { | ||||
| return 0; | return 0; | ||||
| @@ -496,30 +606,30 @@ public: | |||||
| private: | private: | ||||
| DrawableEditorCanvas* drawableCanvas; | DrawableEditorCanvas* drawableCanvas; | ||||
| DrawableTypeInstance drawableItem; | DrawableTypeInstance drawableItem; | ||||
| Drawable* drawable; | |||||
| DrawableComposite* drawable; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| DragOperation* createDragOperation (const MouseEvent& e, Component* snapGuideParentComponent, | |||||
| const ResizableBorderComponent::Zone& zone) | |||||
| bool canRotate() const { return true; } | |||||
| DragOperation* createDragOperation (const Point<int>& mouseDownPos, Component* snapGuideParentComponent, | |||||
| const ResizableBorderComponent::Zone& zone, bool isRotating) | |||||
| { | { | ||||
| Array<ValueTree> selected, unselected; | Array<ValueTree> selected, unselected; | ||||
| EditorDragOperation* drag = 0; | EditorDragOperation* drag = 0; | ||||
| if (isControlPointMode()) | if (isControlPointMode()) | ||||
| { | { | ||||
| Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); | |||||
| DrawableTypeInstance item (getDocument(), controlPointEditingTarget); | DrawableTypeInstance item (getDocument(), controlPointEditingTarget); | ||||
| ControlPointDragOperation* cpd = new ControlPointDragOperation (this, item, d, e, e.getPosition() - origin, snapGuideParentComponent, zone); | |||||
| ControlPointDragOperation* cpd = new ControlPointDragOperation (this, item, drawable, mouseDownPos, snapGuideParentComponent, zone); | |||||
| drag = cpd; | drag = cpd; | ||||
| for (int i = 0; i < cpd->points.size(); ++i) | for (int i = 0; i < cpd->points.size(); ++i) | ||||
| { | { | ||||
| const String pointId (getControlPointId (item.getState(), i)); | |||||
| const String pointId (cpd->points.getUnchecked(i)->getID()); | |||||
| ValueTree v (Ids::controlPoint); | ValueTree v (Ids::controlPoint); | ||||
| v.setProperty (Ids::id_, pointId, 0); | |||||
| v.setProperty (Ids::id_, i, 0); | |||||
| if (editor.getSelection().isSelected (pointId)) | if (editor.getSelection().isSelected (pointId)) | ||||
| selected.add (v); | selected.add (v); | ||||
| @@ -529,9 +639,8 @@ public: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| drag = new ObjectDragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone); | |||||
| DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode()); | DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode()); | ||||
| drag = new ObjectDragOperation (this, mouseDownPos, snapGuideParentComponent, zone, isRotating); | |||||
| for (int i = mainGroup.getNumDrawables(); --i >= 0;) | for (int i = mainGroup.getNumDrawables(); --i >= 0;) | ||||
| { | { | ||||
| @@ -556,6 +665,35 @@ public: | |||||
| getUndoManager().beginNewTransaction(); | getUndoManager().beginNewTransaction(); | ||||
| } | } | ||||
| //============================================================================== | |||||
| bool isInterestedInFileDrag (const StringArray& files) | |||||
| { | |||||
| for (int i = files.size(); --i >= 0;) | |||||
| if (File (files[i]).hasFileExtension ("svg;jpg;jpeg;gif;png")) | |||||
| return true; | |||||
| return false; | |||||
| } | |||||
| void filesDropped (const StringArray& files, int x, int y) | |||||
| { | |||||
| for (int i = files.size(); --i >= 0;) | |||||
| { | |||||
| const File f (files[i]); | |||||
| if (f.hasFileExtension ("svg")) | |||||
| { | |||||
| ValueTree newItem (getDocument().insertSVG (f, screenSpaceToObjectSpace (Point<int> (x, y).toFloat()))); | |||||
| if (newItem.isValid()) | |||||
| getSelection().selectOnly (Drawable::ValueTreeWrapperBase (newItem).getID()); | |||||
| } | |||||
| else if (f.hasFileExtension ("jpg;jpeg;gif;png")) | |||||
| { | |||||
| } | |||||
| } | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| class DrawableComponent : public Component | class DrawableComponent : public Component | ||||
| { | { | ||||
| @@ -32,7 +32,8 @@ | |||||
| */ | */ | ||||
| class DrawableTreeViewItem : public JucerTreeViewBase, | class DrawableTreeViewItem : public JucerTreeViewBase, | ||||
| public ValueTree::Listener, | public ValueTree::Listener, | ||||
| public ChangeListener | |||||
| public ChangeListener, | |||||
| public AsyncUpdater | |||||
| { | { | ||||
| public: | public: | ||||
| DrawableTreeViewItem (DrawableEditor& editor_, const ValueTree& drawableRoot) | DrawableTreeViewItem (DrawableEditor& editor_, const ValueTree& drawableRoot) | ||||
| @@ -51,18 +52,25 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| void valueTreePropertyChanged (ValueTree& tree, const Identifier& property) | void valueTreePropertyChanged (ValueTree& tree, const Identifier& property) | ||||
| { | { | ||||
| if (property == Drawable::ValueTreeWrapperBase::idProperty) | |||||
| repaintItem(); | |||||
| } | } | ||||
| void valueTreeChildrenChanged (ValueTree& tree) | void valueTreeChildrenChanged (ValueTree& tree) | ||||
| { | { | ||||
| if (tree == node.getState()) | |||||
| refreshSubItems(); | |||||
| if (tree == node.getState() || tree.isAChildOf (node.getState())) | |||||
| triggerAsyncUpdate(); | |||||
| } | } | ||||
| void valueTreeParentChanged (ValueTree& tree) | void valueTreeParentChanged (ValueTree& tree) | ||||
| { | { | ||||
| } | } | ||||
| void handleAsyncUpdate() | |||||
| { | |||||
| refreshSubItems(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| // TreeViewItem stuff.. | // TreeViewItem stuff.. | ||||
| bool mightContainSubItems() | bool mightContainSubItems() | ||||
| @@ -157,9 +165,14 @@ public: | |||||
| return String::empty; | return String::empty; | ||||
| } | } | ||||
| static const String getDragIdFor (DrawableEditor& editor) | |||||
| { | |||||
| return drawableItemDragType + editor.getDocument().getUniqueId(); | |||||
| } | |||||
| const String getDragSourceDescription() | const String getDragSourceDescription() | ||||
| { | { | ||||
| return drawableItemDragType; | |||||
| return getDragIdFor (editor); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -175,11 +188,94 @@ public: | |||||
| bool isInterestedInDragSource (const String& sourceDescription, Component* sourceComponent) | bool isInterestedInDragSource (const String& sourceDescription, Component* sourceComponent) | ||||
| { | { | ||||
| return false; | |||||
| return node.getState().getType() == DrawableComposite::valueTreeType | |||||
| && sourceDescription == getDragIdFor (editor) | |||||
| && editor.getSelection().getNumSelected() > 0; | |||||
| } | } | ||||
| void itemDropped (const String& sourceDescription, Component* sourceComponent, int insertIndex) | void itemDropped (const String& sourceDescription, Component* sourceComponent, int insertIndex) | ||||
| { | { | ||||
| if (editor.getSelection().getNumSelected() > 0) | |||||
| { | |||||
| TreeView* tree = getOwnerView(); | |||||
| const ScopedPointer <XmlElement> oldOpenness (tree->getOpennessState (false)); | |||||
| Array <ValueTree> selectedComps; | |||||
| // scan the source tree rather than look at the selection manager, because it might | |||||
| // be from a different editor, and the order needs to be correct. | |||||
| getAllSelectedNodesInTree (sourceComponent, selectedComps); | |||||
| insertItems (selectedComps, insertIndex); | |||||
| if (oldOpenness != 0) | |||||
| tree->restoreOpennessState (*oldOpenness); | |||||
| } | |||||
| } | |||||
| static void getAllSelectedNodesInTree (Component* componentInTree, Array<ValueTree>& selectedItems) | |||||
| { | |||||
| TreeView* tree = dynamic_cast <TreeView*> (componentInTree); | |||||
| if (tree == 0) | |||||
| tree = componentInTree->findParentComponentOfClass ((TreeView*) 0); | |||||
| if (tree != 0) | |||||
| { | |||||
| const int numSelected = tree->getNumSelectedItems(); | |||||
| for (int i = 0; i < numSelected; ++i) | |||||
| { | |||||
| DrawableTreeViewItem* const item = dynamic_cast <DrawableTreeViewItem*> (tree->getSelectedItem (i)); | |||||
| if (item != 0) | |||||
| selectedItems.add (item->node.getState()); | |||||
| } | |||||
| } | |||||
| } | |||||
| void insertItems (Array <ValueTree>& items, int insertIndex) | |||||
| { | |||||
| DrawableComposite::ValueTreeWrapper composite (node.getState()); | |||||
| int i; | |||||
| for (i = items.size(); --i >= 0;) | |||||
| if (node.getState() == items.getReference(i) || composite.getState().isAChildOf (items.getReference(i))) // Check for recursion. | |||||
| return; | |||||
| // Don't include any nodes that are children of other selected nodes.. | |||||
| for (i = items.size(); --i >= 0;) | |||||
| { | |||||
| const ValueTree& n = items.getReference(i); | |||||
| for (int j = items.size(); --j >= 0;) | |||||
| { | |||||
| if (j != i && n.isAChildOf (items.getReference(j))) | |||||
| { | |||||
| items.remove (i); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| // Remove and re-insert them one at a time.. | |||||
| for (i = 0; i < items.size(); ++i) | |||||
| { | |||||
| ValueTree& n = items.getReference(i); | |||||
| int index = composite.indexOfDrawable (n); | |||||
| if (index >= 0 && index < insertIndex) | |||||
| --insertIndex; | |||||
| if (index >= 0) | |||||
| { | |||||
| composite.moveDrawableOrder (index, insertIndex++, editor.getDocument().getUndoManager()); | |||||
| } | |||||
| else | |||||
| { | |||||
| n.getParent().removeChild (n, editor.getDocument().getUndoManager()); | |||||
| composite.addDrawable (n, insertIndex++, editor.getDocument().getUndoManager()); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -38,7 +38,9 @@ public: | |||||
| objectState (objectState_), | objectState (objectState_), | ||||
| objectId (objectId_), | objectId (objectId_), | ||||
| borderThickness (4), | borderThickness (4), | ||||
| isDragging (false) | |||||
| isDragging (false), | |||||
| isRotating (false), | |||||
| canRotate (canvas_->canRotate()) | |||||
| { | { | ||||
| jassert (objectState.isValid()); | jassert (objectState.isValid()); | ||||
| } | } | ||||
| @@ -49,8 +51,13 @@ public: | |||||
| void paint (Graphics& g) | void paint (Graphics& g) | ||||
| { | { | ||||
| g.setColour (resizableBorderColour); | |||||
| g.drawRect (0, 0, getWidth(), getHeight(), borderThickness); | |||||
| if (! canvas->isRotating()) | |||||
| { | |||||
| g.setColour (resizableBorderColour); | |||||
| g.drawRect (0, 0, getWidth(), getHeight(), borderThickness); | |||||
| g.fillRect (rotateArea); | |||||
| } | |||||
| } | } | ||||
| void mouseEnter (const MouseEvent& e) { updateDragZone (e.getPosition()); } | void mouseEnter (const MouseEvent& e) { updateDragZone (e.getPosition()); } | ||||
| @@ -60,36 +67,47 @@ public: | |||||
| void mouseDown (const MouseEvent& e) | void mouseDown (const MouseEvent& e) | ||||
| { | { | ||||
| updateDragZone (e.getPosition()); | updateDragZone (e.getPosition()); | ||||
| isDragging = false; | |||||
| if (e.mods.isPopupMenu()) | if (e.mods.isPopupMenu()) | ||||
| { | { | ||||
| isDragging = false; | |||||
| canvas->showPopupMenu (true); | canvas->showPopupMenu (true); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| isDragging = true; | |||||
| canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), dragZone); | |||||
| canvas->showSizeGuides(); | |||||
| } | |||||
| } | } | ||||
| void mouseDrag (const MouseEvent& e) | void mouseDrag (const MouseEvent& e) | ||||
| { | { | ||||
| if (! (isDragging || e.mods.isPopupMenu() || e.mouseWasClicked())) | |||||
| { | |||||
| isDragging = true; | |||||
| bool isRotating = rotateArea.contains (e.getMouseDownPosition()); | |||||
| canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()), | |||||
| dragZone, isRotating, canvas->getObjectPosition (objectState).getCentre().toFloat()); | |||||
| if (! isRotating) | |||||
| canvas->showSizeGuides(); | |||||
| repaint(); | |||||
| } | |||||
| if (isDragging) | if (isDragging) | ||||
| { | { | ||||
| canvas->continueDrag (e.getEventRelativeTo (getParentComponent())); | |||||
| canvas->continueDrag (e); | |||||
| autoScrollForMouseEvent (e); | autoScrollForMouseEvent (e); | ||||
| } | } | ||||
| } | } | ||||
| void mouseUp (const MouseEvent& e) | void mouseUp (const MouseEvent& e) | ||||
| { | { | ||||
| if (isDragging) | |||||
| if (isDragging || isRotating) | |||||
| { | { | ||||
| isRotating = false; | |||||
| canvas->hideSizeGuides(); | canvas->hideSizeGuides(); | ||||
| canvas->endDrag (e.getEventRelativeTo (getParentComponent())); | |||||
| canvas->endDrag (e); | |||||
| updateDragZone (e.getPosition()); | updateDragZone (e.getPosition()); | ||||
| repaint(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -101,7 +119,7 @@ public: | |||||
| bool hitTest (int x, int y) | bool hitTest (int x, int y) | ||||
| { | { | ||||
| if (ModifierKeys::getCurrentModifiers().isAnyModifierKeyDown()) | if (ModifierKeys::getCurrentModifiers().isAnyModifierKeyDown()) | ||||
| return ! getCentreArea().contains (x, y); | |||||
| return rotateArea.contains (x, y) || ! getCentreArea().contains (x, y); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -114,6 +132,9 @@ public: | |||||
| const Rectangle<int> bounds (canvas->getObjectPosition (objectState)); | const Rectangle<int> bounds (canvas->getObjectPosition (objectState)); | ||||
| setBoundsInTargetSpace (bounds.expanded (borderThickness, borderThickness)); | setBoundsInTargetSpace (bounds.expanded (borderThickness, borderThickness)); | ||||
| if (canRotate) | |||||
| rotateArea = Rectangle<int> (2, 2, 10, 10); | |||||
| int i; | int i; | ||||
| for (i = sizeGuides.size(); --i >= 0;) | for (i = sizeGuides.size(); --i >= 0;) | ||||
| { | { | ||||
| @@ -126,7 +147,6 @@ public: | |||||
| const String& getTargetObjectID() const { return objectId; } | const String& getTargetObjectID() const { return objectId; } | ||||
| //============================================================================== | //============================================================================== | ||||
| class SizeGuideComponent : public OverlayItemComponent, | class SizeGuideComponent : public OverlayItemComponent, | ||||
| public ComponentListener | public ComponentListener | ||||
| @@ -200,7 +220,8 @@ private: | |||||
| ResizableBorderComponent::Zone dragZone; | ResizableBorderComponent::Zone dragZone; | ||||
| const int borderThickness; | const int borderThickness; | ||||
| OwnedArray <SizeGuideComponent> sizeGuides; | OwnedArray <SizeGuideComponent> sizeGuides; | ||||
| bool isDragging; | |||||
| Rectangle<int> rotateArea; | |||||
| bool isDragging, canRotate, isRotating; | |||||
| const Rectangle<int> getCentreArea() const | const Rectangle<int> getCentreArea() const | ||||
| { | { | ||||
| @@ -467,7 +488,9 @@ public: | |||||
| isDraggingClickedComp = true; | isDraggingClickedComp = true; | ||||
| canvas->enableResizingMode(); | canvas->enableResizingMode(); | ||||
| getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | ||||
| canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()), ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | |||||
| canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()), | |||||
| ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre), | |||||
| false, Point<float>()); | |||||
| } | } | ||||
| if (isDraggingClickedComp) | if (isDraggingClickedComp) | ||||
| @@ -843,6 +866,16 @@ const Point<int> EditorCanvasBase::objectSpaceToScreenSpace (const Point<int>& p | |||||
| return p + origin; | return p + origin; | ||||
| } | } | ||||
| const Point<float> EditorCanvasBase::screenSpaceToObjectSpace (const Point<float>& p) const | |||||
| { | |||||
| return p - origin.toFloat(); | |||||
| } | |||||
| const Point<float> EditorCanvasBase::objectSpaceToScreenSpace (const Point<float>& p) const | |||||
| { | |||||
| return p + origin.toFloat(); | |||||
| } | |||||
| const Rectangle<int> EditorCanvasBase::screenSpaceToObjectSpace (const Rectangle<int>& r) const | const Rectangle<int> EditorCanvasBase::screenSpaceToObjectSpace (const Rectangle<int>& r) const | ||||
| { | { | ||||
| return r - origin; | return r - origin; | ||||
| @@ -868,6 +901,11 @@ void EditorCanvasBase::enableControlPointMode (const ValueTree& objectToEdit) | |||||
| } | } | ||||
| } | } | ||||
| bool EditorCanvasBase::isRotating() const | |||||
| { | |||||
| return dragger != 0 && dragger->isRotating(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void EditorCanvasBase::paint (Graphics& g) | void EditorCanvasBase::paint (Graphics& g) | ||||
| { | { | ||||
| @@ -927,6 +965,9 @@ void EditorCanvasBase::handleAsyncUpdate() | |||||
| const Rectangle<int> canvasBounds (getCanvasBounds()); | const Rectangle<int> canvasBounds (getCanvasBounds()); | ||||
| const Point<int> newOrigin (jmax (0, -canvasBounds.getX()), jmax (0, -canvasBounds.getY())); | const Point<int> newOrigin (jmax (0, -canvasBounds.getX()), jmax (0, -canvasBounds.getY())); | ||||
| const int newWidth = jmax (canvasBounds.getWidth(), canvasBounds.getRight()) + border.getLeftAndRight(); | |||||
| const int newHeight = jmax (canvasBounds.getHeight(), canvasBounds.getBottom()) + border.getTopAndBottom(); | |||||
| if (origin != newOrigin) | if (origin != newOrigin) | ||||
| { | { | ||||
| repaint(); | repaint(); | ||||
| @@ -936,16 +977,16 @@ void EditorCanvasBase::handleAsyncUpdate() | |||||
| setBounds (jmin (0, getX() + oldOrigin.getX() - origin.getX()), | setBounds (jmin (0, getX() + oldOrigin.getX() - origin.getX()), | ||||
| jmin (0, getY() + oldOrigin.getY() - origin.getY()), | jmin (0, getY() + oldOrigin.getY() - origin.getY()), | ||||
| jmax (canvasBounds.getWidth(), canvasBounds.getRight()) + border.getLeftAndRight(), | |||||
| jmax (canvasBounds.getHeight(), canvasBounds.getBottom()) + border.getTopAndBottom()); | |||||
| newWidth, newHeight); | |||||
| } | |||||
| else if (getWidth() != newWidth || getHeight() != newHeight) | |||||
| { | |||||
| setSize (newWidth, newHeight); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| setSize (jmax (canvasBounds.getWidth(), canvasBounds.getRight()) + border.getLeftAndRight(), | |||||
| jmax (canvasBounds.getHeight(), canvasBounds.getBottom()) + border.getTopAndBottom()); | |||||
| overlay->update(); | |||||
| } | } | ||||
| overlay->update(); | |||||
| } | } | ||||
| void EditorCanvasBase::resized() | void EditorCanvasBase::resized() | ||||
| @@ -954,6 +995,7 @@ void EditorCanvasBase::resized() | |||||
| overlay->setBounds (getLocalBounds()); | overlay->setBounds (getLocalBounds()); | ||||
| resizeFrame->setBounds (getLocalBounds()); | resizeFrame->setBounds (getLocalBounds()); | ||||
| overlay->update(); | overlay->update(); | ||||
| handleUpdateNowIfNeeded(); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -962,25 +1004,33 @@ void EditorCanvasBase::hideSizeGuides() { overlay->hideSizeGuides(); } | |||||
| //============================================================================== | //============================================================================== | ||||
| void EditorCanvasBase::beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone) | |||||
| void EditorCanvasBase::beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone, | |||||
| bool isRotating, const Point<float>& rotationCentre) | |||||
| { | { | ||||
| dragger = createDragOperation (e, overlay, zone); | |||||
| dragger = createDragOperation (e.getEventRelativeTo (overlay).getPosition() - origin, overlay, zone, isRotating); | |||||
| dragger->setRotationCentre (rotationCentre); | |||||
| repaint(); | |||||
| } | } | ||||
| void EditorCanvasBase::continueDrag (const MouseEvent& e) | void EditorCanvasBase::continueDrag (const MouseEvent& e) | ||||
| { | { | ||||
| MouseEvent e2 (e.getEventRelativeTo (overlay)); | |||||
| if (dragger != 0) | if (dragger != 0) | ||||
| dragger->drag (e, e.getPosition() - origin); | |||||
| dragger->drag (e2, e2.getPosition() - origin); | |||||
| } | } | ||||
| void EditorCanvasBase::endDrag (const MouseEvent& e) | void EditorCanvasBase::endDrag (const MouseEvent& e) | ||||
| { | { | ||||
| if (dragger != 0) | if (dragger != 0) | ||||
| { | { | ||||
| dragger->drag (e, e.getPosition() - origin); | |||||
| MouseEvent e2 (e.getEventRelativeTo (overlay)); | |||||
| dragger->drag (e2, e2.getPosition() - origin); | |||||
| dragger = 0; | dragger = 0; | ||||
| getUndoManager().beginNewTransaction(); | getUndoManager().beginNewTransaction(); | ||||
| repaint(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -999,3 +1049,10 @@ void EditorCanvasBase::OverlayItemComponent::setBoundsInTargetSpace (const Recta | |||||
| setBounds (canvas->objectSpaceToScreenSpace (r) | setBounds (canvas->objectSpaceToScreenSpace (r) | ||||
| + canvas->getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point<int>())); | + canvas->getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point<int>())); | ||||
| } | } | ||||
| const Point<float> EditorCanvasBase::OverlayItemComponent::pointToLocalSpace (const Point<float>& p) const | |||||
| { | |||||
| return canvas->objectSpaceToScreenSpace (p) | |||||
| + (canvas->getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point<int>()) | |||||
| - getPosition()).toFloat(); | |||||
| } | |||||
| @@ -96,13 +96,19 @@ public: | |||||
| virtual ~DragOperation() {} | virtual ~DragOperation() {} | ||||
| virtual void drag (const MouseEvent& e, const Point<int>& newPos) = 0; | virtual void drag (const MouseEvent& e, const Point<int>& newPos) = 0; | ||||
| virtual void setRotationCentre (const Point<float>& rotationCentre) = 0; | |||||
| virtual bool isRotating() const = 0; | |||||
| }; | }; | ||||
| virtual DragOperation* createDragOperation (const MouseEvent& e, | |||||
| virtual DragOperation* createDragOperation (const Point<int>& mouseDownPos, | |||||
| Component* snapGuideParentComponent, | Component* snapGuideParentComponent, | ||||
| const ResizableBorderComponent::Zone& zone) = 0; | |||||
| const ResizableBorderComponent::Zone& zone, | |||||
| bool isRotating) = 0; | |||||
| void beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone); | |||||
| virtual bool canRotate() const = 0; | |||||
| void beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone, | |||||
| bool isRotating, const Point<float>& rotationCentre); | |||||
| void continueDrag (const MouseEvent& e); | void continueDrag (const MouseEvent& e); | ||||
| void endDrag (const MouseEvent& e); | void endDrag (const MouseEvent& e); | ||||
| @@ -111,6 +117,7 @@ public: | |||||
| bool isResizingMode() const { return ! isControlPointMode(); } | bool isResizingMode() const { return ! isControlPointMode(); } | ||||
| bool isControlPointMode() const { return controlPointEditingTarget.isValid(); } | bool isControlPointMode() const { return controlPointEditingTarget.isValid(); } | ||||
| bool isRotating() const; | |||||
| //============================================================================== | //============================================================================== | ||||
| Component* getComponentHolder() const { return componentHolder; } | Component* getComponentHolder() const { return componentHolder; } | ||||
| @@ -118,7 +125,9 @@ public: | |||||
| const Point<int>& getOrigin() const throw() { return origin; } | const Point<int>& getOrigin() const throw() { return origin; } | ||||
| const Point<int> screenSpaceToObjectSpace (const Point<int>& p) const; | const Point<int> screenSpaceToObjectSpace (const Point<int>& p) const; | ||||
| const Point<float> screenSpaceToObjectSpace (const Point<float>& p) const; | |||||
| const Point<int> objectSpaceToScreenSpace (const Point<int>& p) const; | const Point<int> objectSpaceToScreenSpace (const Point<int>& p) const; | ||||
| const Point<float> objectSpaceToScreenSpace (const Point<float>& p) const; | |||||
| const Rectangle<int> screenSpaceToObjectSpace (const Rectangle<int>& r) const; | const Rectangle<int> screenSpaceToObjectSpace (const Rectangle<int>& r) const; | ||||
| const Rectangle<int> objectSpaceToScreenSpace (const Rectangle<int>& r) const; | const Rectangle<int> objectSpaceToScreenSpace (const Rectangle<int>& r) const; | ||||
| @@ -130,6 +139,7 @@ public: | |||||
| ~OverlayItemComponent(); | ~OverlayItemComponent(); | ||||
| void setBoundsInTargetSpace (const Rectangle<int>& r); | void setBoundsInTargetSpace (const Rectangle<int>& r); | ||||
| const Point<float> pointToLocalSpace (const Point<float>& p) const; | |||||
| protected: | protected: | ||||
| EditorCanvasBase* canvas; | EditorCanvasBase* canvas; | ||||
| @@ -33,13 +33,14 @@ | |||||
| class EditorDragOperation : public EditorCanvasBase::DragOperation | class EditorDragOperation : public EditorCanvasBase::DragOperation | ||||
| { | { | ||||
| public: | public: | ||||
| EditorDragOperation (EditorCanvasBase* canvas_, const MouseEvent& e, const Point<int>& mousePos, | |||||
| Component* snapGuideParentComp_, | |||||
| const ResizableBorderComponent::Zone& zone_) | |||||
| EditorDragOperation (EditorCanvasBase* canvas_, const Point<int>& mousePos, | |||||
| Component* snapGuideParentComp_, const ResizableBorderComponent::Zone& zone_, | |||||
| bool rotating_) | |||||
| : canvas (canvas_), | : canvas (canvas_), | ||||
| snapGuideParentComp (snapGuideParentComp_), | snapGuideParentComp (snapGuideParentComp_), | ||||
| zone (zone_), | zone (zone_), | ||||
| mouseDownPos (mousePos) | |||||
| mouseDownPos (mousePos), | |||||
| rotating (rotating_) | |||||
| { | { | ||||
| } | } | ||||
| @@ -141,6 +142,16 @@ public: | |||||
| getUndoManager().beginNewTransaction(); | getUndoManager().beginNewTransaction(); | ||||
| } | } | ||||
| void setRotationCentre (const Point<float>& rotationCentre) | |||||
| { | |||||
| centre = rotationCentre; | |||||
| } | |||||
| bool isRotating() const | |||||
| { | |||||
| return rotating; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| struct SnapLine | struct SnapLine | ||||
| { | { | ||||
| @@ -195,31 +206,35 @@ public: | |||||
| { | { | ||||
| getUndoManager().undoCurrentTransactionOnly(); | getUndoManager().undoCurrentTransactionOnly(); | ||||
| // (can't use getOffsetFromDragStart() because of auto-scrolling) | |||||
| Point<int> distance (newPos - mouseDownPos); | |||||
| if (! isDraggingLeftRight()) | |||||
| distance = distance.withX (0); | |||||
| if (rotating) | |||||
| { | |||||
| const float angle = centre.getAngleToPoint (mouseDownPos.toFloat()) - centre.getAngleToPoint (newPos.toFloat()); | |||||
| const AffineTransform transform (AffineTransform::rotation (angle, centre.getX(), centre.getY())); | |||||
| if (! isDraggingUpDown()) | |||||
| distance = distance.withY (0); | |||||
| for (int i = 0; i < updateList.size(); ++i) | |||||
| transformObject (updateList.getReference(i), transform); | |||||
| } | |||||
| else | |||||
| { | |||||
| // (can't use getOffsetFromDragStart() because of auto-scrolling) | |||||
| Point<int> distance (newPos - mouseDownPos); | |||||
| if (! isDraggingLeftRight()) | |||||
| distance = distance.withX (0); | |||||
| snapGuides.clear(); | |||||
| if (! isDraggingUpDown()) | |||||
| distance = distance.withY (0); | |||||
| if (canvas->getPanel()->isSnappingEnabled() != (e.mods.isCommandDown() || e.mods.isCtrlDown())) | |||||
| { | |||||
| performSnap (verticalSnapTargets, getVerticalSnapPositions (distance), true, distance); | |||||
| performSnap (horizontalSnapTargets, getHorizontalSnapPositions (distance), false, distance); | |||||
| } | |||||
| snapGuides.clear(); | |||||
| for (int i = 0; i < updateList.size(); ++i) | |||||
| dragItem (updateList.getReference(i), distance, originalPositions.getReference(i)); | |||||
| } | |||||
| if (canvas->getPanel()->isSnappingEnabled() != (e.mods.isCommandDown() || e.mods.isCtrlDown())) | |||||
| { | |||||
| performSnap (verticalSnapTargets, getVerticalSnapPositions (distance), true, distance); | |||||
| performSnap (horizontalSnapTargets, getHorizontalSnapPositions (distance), false, distance); | |||||
| } | |||||
| void dragItem (ValueTree& v, const Point<int>& distance, const Rectangle<float>& originalPos) | |||||
| { | |||||
| const Rectangle<float> newBounds (zone.resizeRectangleBy (originalPos, Point<float> ((float) distance.getX(), | |||||
| (float) distance.getY()))); | |||||
| setObjectPosition (v, newBounds); | |||||
| for (int i = 0; i < updateList.size(); ++i) | |||||
| dragItem (updateList.getReference(i), distance, originalPositions.getReference(i)); | |||||
| } | |||||
| } | } | ||||
| protected: | protected: | ||||
| @@ -231,6 +246,7 @@ protected: | |||||
| virtual void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps) = 0; | virtual void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps) = 0; | ||||
| virtual const Rectangle<float> getObjectPosition (const ValueTree& state) = 0; | virtual const Rectangle<float> getObjectPosition (const ValueTree& state) = 0; | ||||
| virtual void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds) = 0; | virtual void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds) = 0; | ||||
| virtual void transformObject (ValueTree& state, const AffineTransform& transform) = 0; | |||||
| virtual UndoManager& getUndoManager() = 0; | virtual UndoManager& getUndoManager() = 0; | ||||
| @@ -246,6 +262,15 @@ private: | |||||
| OwnedArray<Component> snapGuides; | OwnedArray<Component> snapGuides; | ||||
| Component* snapGuideParentComp; | Component* snapGuideParentComp; | ||||
| Point<int> mouseDownPos; | Point<int> mouseDownPos; | ||||
| Point<float> centre; | |||||
| bool rotating; | |||||
| void dragItem (ValueTree& v, const Point<int>& distance, const Rectangle<float>& originalPos) | |||||
| { | |||||
| const Rectangle<float> newBounds (zone.resizeRectangleBy (originalPos, Point<float> ((float) distance.getX(), | |||||
| (float) distance.getY()))); | |||||
| setObjectPosition (v, newBounds); | |||||
| } | |||||
| void getCompleteDependencyList (const ValueTree& object, Array <ValueTree>& deps, const Array<ValueTree>& activeObjects) | void getCompleteDependencyList (const ValueTree& object, Array <ValueTree>& deps, const Array<ValueTree>& activeObjects) | ||||
| { | { | ||||
| @@ -31,44 +31,41 @@ | |||||
| ItemPreviewComponent::ItemPreviewComponent (const File& file_) | ItemPreviewComponent::ItemPreviewComponent (const File& file_) | ||||
| : file (file_) | : file (file_) | ||||
| { | { | ||||
| facts.add (file.getFullPathName()); | |||||
| tryToLoadImage (file.createInputStream()); | |||||
| facts.removeEmptyStrings (true); | |||||
| } | |||||
| ItemPreviewComponent::ItemPreviewComponent (InputStream* input, const String& name) | |||||
| { | |||||
| facts.add (name); | |||||
| tryToLoadImage (input); | |||||
| facts.removeEmptyStrings (true); | |||||
| tryToLoadImage(); | |||||
| } | } | ||||
| ItemPreviewComponent::~ItemPreviewComponent() | ItemPreviewComponent::~ItemPreviewComponent() | ||||
| { | { | ||||
| } | } | ||||
| void ItemPreviewComponent::tryToLoadImage (InputStream* in) | |||||
| void ItemPreviewComponent::tryToLoadImage() | |||||
| { | { | ||||
| if (in != 0) | |||||
| { | |||||
| ScopedPointer <InputStream> input (in); | |||||
| facts.clear(); | |||||
| facts.add (file.getFullPathName()); | |||||
| image = Image(); | |||||
| ScopedPointer <InputStream> input (file.createInputStream()); | |||||
| if (input != 0) | |||||
| { | |||||
| const int64 totalSize = input->getTotalLength(); | |||||
| ImageFileFormat* format = ImageFileFormat::findImageFormatForStream (*input); | ImageFileFormat* format = ImageFileFormat::findImageFormatForStream (*input); | ||||
| input = 0; | |||||
| String formatName; | String formatName; | ||||
| if (format != 0) | if (format != 0) | ||||
| formatName = " " + format->getFormatName(); | formatName = " " + format->getFormatName(); | ||||
| image = ImageFileFormat::loadFrom (*input); | |||||
| image = ImageCache::getFromFile (file); | |||||
| if (image.isValid()) | if (image.isValid()) | ||||
| facts.add (String (image.getWidth()) + " x " + String (image.getHeight()) + formatName); | facts.add (String (image.getWidth()) + " x " + String (image.getHeight()) + formatName); | ||||
| const int64 totalSize = input->getTotalLength(); | |||||
| if (totalSize > 0) | if (totalSize > 0) | ||||
| facts.add (File::descriptionOfSizeInBytes (totalSize)); | facts.add (File::descriptionOfSizeInBytes (totalSize)); | ||||
| } | } | ||||
| facts.removeEmptyStrings (true); | |||||
| } | } | ||||
| void ItemPreviewComponent::paint (Graphics& g) | void ItemPreviewComponent::paint (Graphics& g) | ||||
| @@ -35,7 +35,6 @@ class ItemPreviewComponent : public Component | |||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| // This will delete the stream | // This will delete the stream | ||||
| ItemPreviewComponent (InputStream* input, const String& name); | |||||
| ItemPreviewComponent (const File& file); | ItemPreviewComponent (const File& file); | ||||
| ~ItemPreviewComponent(); | ~ItemPreviewComponent(); | ||||
| @@ -50,7 +49,7 @@ private: | |||||
| File file; | File file; | ||||
| Image image; | Image image; | ||||
| void tryToLoadImage (InputStream* input); | |||||
| void tryToLoadImage(); | |||||
| }; | }; | ||||
| @@ -26,19 +26,25 @@ | |||||
| #ifndef __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ | #ifndef __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ | ||||
| #define __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ | #define __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ | ||||
| #include "../model/Project/jucer_Project.h" | |||||
| class FillTypeEditorComponent; | class FillTypeEditorComponent; | ||||
| //============================================================================== | //============================================================================== | ||||
| class PopupFillSelector : public Component, | class PopupFillSelector : public Component, | ||||
| public ChangeListener, | public ChangeListener, | ||||
| public ValueTree::Listener, | public ValueTree::Listener, | ||||
| public ButtonListener | |||||
| public ButtonListener, | |||||
| public AsyncUpdater | |||||
| { | { | ||||
| public: | public: | ||||
| PopupFillSelector (const ValueTree& fillState_, const ColourGradient& defaultGradient_, UndoManager* undoManager_) | |||||
| PopupFillSelector (const ValueTree& fillState_, const ColourGradient& defaultGradient_, | |||||
| Drawable::ImageProvider* imageProvider_, Project* project, UndoManager* undoManager_) | |||||
| : gradientPicker (defaultGradient_), | : gradientPicker (defaultGradient_), | ||||
| defaultGradient (defaultGradient_), | defaultGradient (defaultGradient_), | ||||
| tilePicker (imageProvider_, project), | |||||
| fillState (fillState_), | fillState (fillState_), | ||||
| imageProvider (imageProvider_), | |||||
| undoManager (undoManager_) | undoManager (undoManager_) | ||||
| { | { | ||||
| colourButton.setButtonText ("Colour"); | colourButton.setButtonText ("Colour"); | ||||
| @@ -60,6 +66,9 @@ public: | |||||
| addChildComponent (&gradientPicker); | addChildComponent (&gradientPicker); | ||||
| gradientPicker.addChangeListener (this); | gradientPicker.addChangeListener (this); | ||||
| addChildComponent (&tilePicker); | |||||
| tilePicker.addChangeListener (this); | |||||
| fillState.addListener (this); | fillState.addListener (this); | ||||
| colourButton.setRadioGroupId (123); | colourButton.setRadioGroupId (123); | ||||
| @@ -90,6 +99,7 @@ public: | |||||
| const Rectangle<int> content (2, y + h + 4, getWidth() - 4, getHeight() - (y + h + 6)); | const Rectangle<int> content (2, y + h + 4, getWidth() - 4, getHeight() - (y + h + 6)); | ||||
| colourPicker.setBounds (content); | colourPicker.setBounds (content); | ||||
| gradientPicker.setBounds (content); | gradientPicker.setBounds (content); | ||||
| tilePicker.setBounds (content); | |||||
| } | } | ||||
| void buttonClicked (Button* b) | void buttonClicked (Button* b) | ||||
| @@ -109,7 +119,7 @@ public: | |||||
| // Use a cunning trick to make the wrapper dig out the earlier gradient settings, if there are any.. | // Use a cunning trick to make the wrapper dig out the earlier gradient settings, if there are any.. | ||||
| FillType newFill (defaultGradient); | FillType newFill (defaultGradient); | ||||
| ValueTree temp ("dummy"); | ValueTree temp ("dummy"); | ||||
| Drawable::ValueTreeWrapperBase::writeFillType (temp, newFill, 0, 0, 0); | |||||
| Drawable::ValueTreeWrapperBase::writeFillType (temp, newFill, 0, 0, 0, 0); | |||||
| fillState.setProperty (Drawable::ValueTreeWrapperBase::type, temp [Drawable::ValueTreeWrapperBase::type], undoManager); | fillState.setProperty (Drawable::ValueTreeWrapperBase::type, temp [Drawable::ValueTreeWrapperBase::type], undoManager); | ||||
| newFill = readFillType (&gp1, &gp2); | newFill = readFillType (&gp1, &gp2); | ||||
| @@ -117,11 +127,11 @@ public: | |||||
| if (newFill.gradient->getNumColours() <= 1) | if (newFill.gradient->getNumColours() <= 1) | ||||
| { | { | ||||
| newFill = FillType (defaultGradient); | newFill = FillType (defaultGradient); | ||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, undoManager); | |||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, imageProvider, undoManager); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, undoManager); | |||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, imageProvider, undoManager); | |||||
| } | } | ||||
| refresh(); | refresh(); | ||||
| @@ -137,7 +147,7 @@ public: | |||||
| const FillType readFillType (RelativePoint* gp1, RelativePoint* gp2) const | const FillType readFillType (RelativePoint* gp1, RelativePoint* gp2) const | ||||
| { | { | ||||
| return Drawable::ValueTreeWrapperBase::readFillType (fillState, gp1, gp2, 0); | |||||
| return Drawable::ValueTreeWrapperBase::readFillType (fillState, gp1, gp2, 0, imageProvider); | |||||
| } | } | ||||
| void setFillType (const FillType& newFill) | void setFillType (const FillType& newFill) | ||||
| @@ -150,7 +160,7 @@ public: | |||||
| if (undoManager != 0) | if (undoManager != 0) | ||||
| undoManager->undoCurrentTransactionOnly(); | undoManager->undoCurrentTransactionOnly(); | ||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, undoManager); | |||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, imageProvider, undoManager); | |||||
| refresh(); | refresh(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -163,6 +173,8 @@ public: | |||||
| setFillType (colourPicker.getCurrentColour()); | setFillType (colourPicker.getCurrentColour()); | ||||
| else if (currentFill.isGradient()) | else if (currentFill.isGradient()) | ||||
| setFillType (gradientPicker.getGradient()); | setFillType (gradientPicker.getGradient()); | ||||
| else if (currentFill.isTiledImage()) | |||||
| setFillType (tilePicker.getFill()); | |||||
| } | } | ||||
| void refresh() | void refresh() | ||||
| @@ -171,6 +183,7 @@ public: | |||||
| colourPicker.setVisible (newFill.isColour()); | colourPicker.setVisible (newFill.isColour()); | ||||
| gradientPicker.setVisible (newFill.isGradient()); | gradientPicker.setVisible (newFill.isGradient()); | ||||
| tilePicker.setVisible (newFill.isTiledImage()); | |||||
| if (newFill.isColour()) | if (newFill.isColour()) | ||||
| { | { | ||||
| @@ -182,7 +195,7 @@ public: | |||||
| if (newFill.gradient->getNumColours() <= 1) | if (newFill.gradient->getNumColours() <= 1) | ||||
| { | { | ||||
| newFill = FillType (defaultGradient); | newFill = FillType (defaultGradient); | ||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, undoManager); | |||||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, imageProvider, undoManager); | |||||
| } | } | ||||
| gradientButton.setToggleState (true, false); | gradientButton.setToggleState (true, false); | ||||
| @@ -190,12 +203,14 @@ public: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| tilePicker.setFill (newFill); | |||||
| imageButton.setToggleState (true, false); | imageButton.setToggleState (true, false); | ||||
| } | } | ||||
| } | } | ||||
| void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { refresh(); } | |||||
| void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { refresh(); } | |||||
| void handleAsyncUpdate() { refresh(); } | |||||
| void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { triggerAsyncUpdate(); } | |||||
| void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { triggerAsyncUpdate(); } | |||||
| void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {} | void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {} | ||||
| private: | private: | ||||
| @@ -433,12 +448,124 @@ private: | |||||
| } | } | ||||
| }; | }; | ||||
| //============================================================================== | |||||
| class TiledFillDesigner : public Component, | |||||
| public ChangeBroadcaster, | |||||
| public ComboBoxListener, | |||||
| public SliderListener | |||||
| { | |||||
| public: | |||||
| TiledFillDesigner (Drawable::ImageProvider* imageProvider_, Project* project_) | |||||
| : imageProvider (imageProvider_), project (project_) | |||||
| { | |||||
| addAndMakeVisible (&imageBox); | |||||
| addAndMakeVisible (&opacitySlider); | |||||
| opacitySlider.setRange (0.0, 1.0, 0.001); | |||||
| sliderLabel.setText ("Opacity:", false); | |||||
| sliderLabel.attachToComponent (&opacitySlider, false); | |||||
| OwnedArray<Project::Item> images; | |||||
| project->findAllImageItems (images); | |||||
| for (int i = 0; i < images.size(); ++i) | |||||
| imageBox.addItem (images.getUnchecked(i)->getName().toString(), i + 1); | |||||
| imageBox.setTextWhenNothingSelected ("Select an image..."); | |||||
| opacitySlider.addListener (this); | |||||
| imageBox.addListener (this); | |||||
| } | |||||
| ~TiledFillDesigner() | |||||
| { | |||||
| } | |||||
| const FillType getFill() const | |||||
| { | |||||
| return fill; | |||||
| } | |||||
| void setFill (const FillType& newFill) | |||||
| { | |||||
| if (fill != newFill) | |||||
| { | |||||
| fill = newFill; | |||||
| OwnedArray<Project::Item> images; | |||||
| project->findAllImageItems (images); | |||||
| const String currentID (imageProvider->getIdentifierForImage (fill.image).toString()); | |||||
| int idToSelect = -1; | |||||
| for (int i = 0; i < images.size(); ++i) | |||||
| { | |||||
| if (images.getUnchecked(i)->getImageFileID() == currentID) | |||||
| { | |||||
| idToSelect = i + 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| imageBox.setSelectedId (idToSelect, true); | |||||
| opacitySlider.setValue (fill.getOpacity(), false, false); | |||||
| } | |||||
| } | |||||
| void resized() | |||||
| { | |||||
| imageBox.setBounds (20, 10, getWidth() - 40, 22); | |||||
| opacitySlider.setBounds (20, 60, getWidth() - 40, 22); | |||||
| } | |||||
| void sliderValueChanged (Slider* slider) | |||||
| { | |||||
| if (opacitySlider.getValue() != fill.getOpacity()) | |||||
| { | |||||
| FillType f (fill); | |||||
| f.setOpacity ((float) opacitySlider.getValue()); | |||||
| setFill (f); | |||||
| sendChangeMessage (this); | |||||
| } | |||||
| } | |||||
| void comboBoxChanged (ComboBox* comboBoxThatHasChanged) | |||||
| { | |||||
| OwnedArray<Project::Item> images; | |||||
| project->findAllImageItems (images); | |||||
| Project::Item* item = images [imageBox.getSelectedId() - 1]; | |||||
| if (item != 0) | |||||
| { | |||||
| Image im (imageProvider->getImageForIdentifier (item->getImageFileID())); | |||||
| if (im.isValid() && im != fill.image) | |||||
| { | |||||
| FillType f (fill); | |||||
| f.image = im; | |||||
| setFill (f); | |||||
| sendChangeMessage (this); | |||||
| } | |||||
| } | |||||
| } | |||||
| private: | |||||
| FillType fill; | |||||
| Drawable::ImageProvider* imageProvider; | |||||
| Project* project; | |||||
| ComboBox imageBox; | |||||
| Slider opacitySlider; | |||||
| Label sliderLabel; | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| FillTypeEditorComponent* owner; | FillTypeEditorComponent* owner; | ||||
| StoredSettings::ColourSelectorWithSwatches colourPicker; | StoredSettings::ColourSelectorWithSwatches colourPicker; | ||||
| GradientDesigner gradientPicker; | GradientDesigner gradientPicker; | ||||
| TiledFillDesigner tilePicker; | |||||
| ColourGradient defaultGradient; | ColourGradient defaultGradient; | ||||
| ValueTree fillState; | ValueTree fillState; | ||||
| Drawable::ImageProvider* imageProvider; | |||||
| UndoManager* undoManager; | UndoManager* undoManager; | ||||
| TextButton colourButton, gradientButton, imageButton; | TextButton colourButton, gradientButton, imageButton; | ||||
| @@ -454,8 +581,10 @@ class FillTypeEditorComponent : public Component, | |||||
| public ValueTree::Listener | public ValueTree::Listener | ||||
| { | { | ||||
| public: | public: | ||||
| FillTypeEditorComponent (const ValueTree& fillState_, UndoManager* undoManager_) | |||||
| : fillState (fillState_), undoManager (undoManager_) | |||||
| FillTypeEditorComponent (const ValueTree& fillState_, Drawable::ImageProvider* imageProvider_, | |||||
| Project* project_, UndoManager* undoManager_) | |||||
| : fillState (fillState_), undoManager (undoManager_), | |||||
| imageProvider (imageProvider_), project (project_) | |||||
| { | { | ||||
| fillState.addListener (this); | fillState.addListener (this); | ||||
| refresh(); | refresh(); | ||||
| @@ -498,7 +627,7 @@ public: | |||||
| void refresh() | void refresh() | ||||
| { | { | ||||
| const FillType newFill (Drawable::ValueTreeWrapperBase::readFillType (fillState, 0, 0, 0)); | |||||
| const FillType newFill (Drawable::ValueTreeWrapperBase::readFillType (fillState, 0, 0, 0, imageProvider)); | |||||
| if (newFill != fillType) | if (newFill != fillType) | ||||
| { | { | ||||
| @@ -511,7 +640,7 @@ public: | |||||
| { | { | ||||
| undoManager->beginNewTransaction(); | undoManager->beginNewTransaction(); | ||||
| PopupFillSelector popup (fillState, getDefaultGradient(), undoManager); | |||||
| PopupFillSelector popup (fillState, getDefaultGradient(), imageProvider, project, undoManager); | |||||
| PopupMenu m; | PopupMenu m; | ||||
| m.addCustomItem (1234, &popup, 300, 450, false); | m.addCustomItem (1234, &popup, 300, 450, false); | ||||
| @@ -526,7 +655,9 @@ public: | |||||
| private: | private: | ||||
| ValueTree fillState; | ValueTree fillState; | ||||
| Drawable::ImageProvider* imageProvider; | |||||
| UndoManager* undoManager; | UndoManager* undoManager; | ||||
| Project* project; | |||||
| FillType fillType; | FillType fillType; | ||||
| }; | }; | ||||
| @@ -536,9 +667,10 @@ class FillTypePropertyComponent : public PropertyComponent | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| FillTypePropertyComponent (UndoManager* undoManager, const String& name, const ValueTree& fill) | |||||
| FillTypePropertyComponent (UndoManager* undoManager, const String& name, const ValueTree& fill, | |||||
| Drawable::ImageProvider* imageProvider, Project* project) | |||||
| : PropertyComponent (name), | : PropertyComponent (name), | ||||
| editor (fill, undoManager) | |||||
| editor (fill, imageProvider, project, undoManager) | |||||
| { | { | ||||
| jassert (fill.isValid()); | jassert (fill.isValid()); | ||||
| addAndMakeVisible (&editor); | addAndMakeVisible (&editor); | ||||