/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-9 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online at www.gnu.org/licenses. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #ifndef __JUCER_DRAWABLEOBJECTCOMPONENT_JUCEHEADER__ #define __JUCER_DRAWABLEOBJECTCOMPONENT_JUCEHEADER__ #include "jucer_DrawableEditor.h" //============================================================================== class DrawableObjectComponent : public Component, public ValueTree::Listener, public ChangeListener { public: DrawableObjectComponent (const ValueTree& drawableNode_, DrawableEditor& editor_, Drawable* drawable_) : drawable (drawable_), drawableNode (drawableNode_), editor (editor_) { setVisible (true); nodeHashCode = DrawableEditor::getHashForNode (drawableNode); drawableNode.addListener (this); editor.selectedItems.addChangeListener (this); } ~DrawableObjectComponent() { deleteAllChildren(); editor.selectedItems.removeChangeListener (this); drawableNode.removeListener (this); drawable = 0; } static DrawableObjectComponent* create (const ValueTree& node, DrawableEditor& editor, Drawable* drawable_); //============================================================================== void paint (Graphics& g) { //g.setColour (Colours::pink.withAlpha (0.2f)); //g.fillRect (0, 0, getWidth(), getHeight()); if (isSelected()) { g.setColour (Colours::red); g.drawRect (0, 0, getWidth(), getHeight(), 2); } const Point offset (getDrawableOriginRelativeToTopLeft()); g.setOrigin (offset.getX(), offset.getY()); drawable->draw (g, 1.0f, transform); } const Point getDrawableOriginRelativeToTopLeft() const { return drawableOriginRelativeToParentTopLeft - getPosition(); } void findLassoItemsInArea (Array & itemsFound, Rectangle r) const { if (getBounds().intersects (r)) itemsFound.add (nodeHashCode); r.translate (-getX(), -getY()); for (int i = getNumChildComponents(); --i >= 0;) { DrawableObjectComponent* d = dynamic_cast (getChildComponent(i)); if (d != 0) d->findLassoItemsInArea (itemsFound, r); } } bool isSelected() const { return editor.selectedItems.isSelected (nodeHashCode); } //============================================================================== void valueTreePropertyChanged (ValueTree& tree, const var::identifier& property) { if (tree == drawableNode) { editor.getDocument().changed(); reloadFromValueTree(); repaint(); } } void valueTreeChildrenChanged (ValueTree& tree) { if (tree == drawableNode) { editor.getDocument().changed(); reloadFromValueTree(); repaint(); } } void valueTreeParentChanged (ValueTree& tree) { editor.getDocument().changed(); reloadFromValueTree(); repaint(); } void changeListenerCallback (void*) { repaint(); } //============================================================================== virtual void reloadFromValueTree() = 0; void commitModifiedPath() { drawableNode = drawable->createValueTree(); } //============================================================================== AffineTransform transform; ScopedPointer drawable; ValueTree drawableNode; int64 nodeHashCode; DrawableEditor& editor; Point drawableOriginRelativeToParentTopLeft; }; //============================================================================== class PathDrawableComponent : public DrawableObjectComponent { public: PathDrawableComponent (const ValueTree& drawableNode_, DrawableEditor& editor_, DrawablePath* drawable_) : DrawableObjectComponent (drawableNode_, editor_, drawable_) { } ~PathDrawableComponent() { } // Relative to the drawable's origin, not the parent component or any other comp. const Rectangle getBoundsRectangle() { if (drawable == 0) reloadFromValueTree(); return drawable->getBounds().getSmallestIntegerContainer().expanded (2, 2); } bool hitTest (int x, int y) { const Point offset (getDrawableOriginRelativeToTopLeft()); return drawable->hitTest ((float) x - offset.getX(), (float) y - offset.getY()); } void mouseDown (const MouseEvent& e) { mouseDownSelectResult = editor.selectedItems.addToSelectionOnMouseDown (nodeHashCode, e.mods); pathBoundsOnMouseDown = getPath()->getPath().getBounds(); } void mouseDrag (const MouseEvent& e) { } void mouseUp (const MouseEvent& e) { editor.selectedItems.addToSelectionOnMouseUp (nodeHashCode, e.mods, ! e.mouseWasClicked(), mouseDownSelectResult); } void reloadFromValueTree() { drawable = Drawable::createFromValueTree (drawableNode); jassert (dynamic_cast ((Drawable*) drawable) != 0); setBounds (getBoundsRectangle().translated (drawableOriginRelativeToParentTopLeft.getX(), drawableOriginRelativeToParentTopLeft.getY())); } private: bool mouseDownSelectResult; Rectangle pathBoundsOnMouseDown; DrawablePath* getPath() const { return dynamic_cast ((Drawable*) drawable); } }; //============================================================================== class CompositeDrawableComponent : public DrawableObjectComponent { public: CompositeDrawableComponent (const ValueTree& drawableNode_, DrawableEditor& editor_, DrawableComposite* drawable_) : DrawableObjectComponent (drawableNode_, editor_, drawable_) { } ~CompositeDrawableComponent() { } void reloadFromValueTree() { drawable = Drawable::createFromValueTree (drawableNode); DrawableComposite* dc = dynamic_cast ((Drawable*) drawable); jassert (dc != 0); deleteAllChildren(); Rectangle childBounds; int i; for (i = 0; i < dc->getNumDrawables(); ++i) { Drawable* d = dc->getDrawable (i); jassert (d != 0); DrawableObjectComponent* c = DrawableObjectComponent::create (drawableNode.getChild(i), editor, d); if (c != 0) { addChildComponent (c); const AffineTransform* t = dc->getDrawableTransform (i); if (t != 0) c->transform = *t; c->drawableOriginRelativeToParentTopLeft = drawableOriginRelativeToParentTopLeft; c->reloadFromValueTree(); if (childBounds.isEmpty()) childBounds = c->getBounds(); else childBounds = childBounds.getUnion (c->getBounds()); } } for (i = dc->getNumDrawables(); --i >= 0;) dc->removeDrawable (i, false); setBounds (childBounds); for (i = getNumChildComponents(); --i >= 0;) { DrawableObjectComponent* dc = dynamic_cast (getChildComponent (i)); if (dc != 0) { dc->setTopLeftPosition (dc->getX() - getX(), dc->getY() - getY()); dc->drawableOriginRelativeToParentTopLeft = drawableOriginRelativeToParentTopLeft - getPosition(); } } } }; //============================================================================== DrawableObjectComponent* DrawableObjectComponent::create (const ValueTree& node, DrawableEditor& editor, Drawable* drawable_) { ScopedPointer drawable (drawable_); if (drawable == 0) drawable = Drawable::createFromValueTree (node); DrawablePath* p = dynamic_cast ((Drawable*) drawable); if (p != 0) { drawable.release(); return new PathDrawableComponent (node, editor, p); } DrawableComposite* dc = dynamic_cast ((Drawable*) drawable); if (dc != 0) { drawable.release(); return new CompositeDrawableComponent (node, editor, dc); } jassertfalse return 0; } #endif // __JUCER_DRAWABLEOBJECTCOMPONENT_JUCEHEADER__