| @@ -27,13 +27,146 @@ | |||
| //============================================================================== | |||
| static const char* const componentDocumentTag = "COMPONENT"; | |||
| static const char* const componentDocumentTag = "COMPONENT"; | |||
| static const char* const componentGroupTag = "COMPONENTS"; | |||
| static const char* const idProperty = "id"; | |||
| static const char* const compBoundsProperty = "position"; | |||
| //============================================================================== | |||
| static const String componentBoundsToString (const Rectangle<int>& bounds) | |||
| { | |||
| return bounds.toString(); | |||
| } | |||
| static const Rectangle<int> stringToComponentBounds (const String& s) | |||
| { | |||
| return Rectangle<int>::fromString (s); | |||
| } | |||
| //============================================================================== | |||
| class ComponentTypeHandler | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| ComponentTypeHandler (const String& name_, const String& tag_) | |||
| : name (name_), tag (tag_) | |||
| { | |||
| } | |||
| virtual ~ComponentTypeHandler() | |||
| { | |||
| } | |||
| const String& getName() const { return name; } | |||
| const String& getTag() const { return tag; } | |||
| virtual Component* createComponent() = 0; | |||
| virtual const Rectangle<int> getDefaultSize() = 0; | |||
| virtual void updateComponent (Component* comp, const ValueTree& state) | |||
| { | |||
| comp->setBounds (stringToComponentBounds (state [compBoundsProperty])); | |||
| } | |||
| virtual const ValueTree createNewItem() | |||
| { | |||
| ValueTree v (tag); | |||
| v.setProperty (idProperty, createAlphaNumericUID(), 0); | |||
| v.setProperty (compBoundsProperty, | |||
| componentBoundsToString (getDefaultSize().withPosition (Point<int> (Random::getSystemRandom().nextInt (100) + 100, | |||
| Random::getSystemRandom().nextInt (100) + 100))), 0); | |||
| return v; | |||
| } | |||
| //============================================================================== | |||
| protected: | |||
| const String name, tag; | |||
| }; | |||
| //============================================================================== | |||
| class TextButtonHandler : public ComponentTypeHandler | |||
| { | |||
| public: | |||
| TextButtonHandler() : ComponentTypeHandler ("TextButton", "TEXTBUTTON") {} | |||
| ~TextButtonHandler() {} | |||
| Component* createComponent() { return new TextButton (String::empty); } | |||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 150, 24); } | |||
| void updateComponent (Component* comp, const ValueTree& state) | |||
| { | |||
| ComponentTypeHandler::updateComponent (comp, state); | |||
| } | |||
| const ValueTree createNewItem() | |||
| { | |||
| ValueTree v (ComponentTypeHandler::createNewItem()); | |||
| return v; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class ComponentTypeManager : public DeletedAtShutdown | |||
| { | |||
| public: | |||
| ComponentTypeManager() | |||
| { | |||
| handlers.add (new TextButtonHandler()); | |||
| } | |||
| ~ComponentTypeManager() | |||
| { | |||
| } | |||
| juce_DeclareSingleton_SingleThreaded_Minimal (ComponentTypeManager); | |||
| Component* createFromStoredType (const ValueTree& value) | |||
| { | |||
| ComponentTypeHandler* handler = getHandlerFor (value.getType()); | |||
| if (handler == 0) | |||
| return 0; | |||
| Component* c = handler->createComponent(); | |||
| if (c != 0) | |||
| handler->updateComponent (c, value); | |||
| return c; | |||
| } | |||
| ComponentTypeHandler* getHandlerFor (const String& type) | |||
| { | |||
| for (int i = handlers.size(); --i >= 0;) | |||
| if (handlers.getUnchecked(i)->getTag() == type) | |||
| return handlers.getUnchecked(i); | |||
| return 0; | |||
| } | |||
| const StringArray getTypeNames() const | |||
| { | |||
| StringArray s; | |||
| for (int i = 0; i < handlers.size(); ++i) | |||
| s.add (handlers.getUnchecked(i)->getName()); | |||
| return s; | |||
| } | |||
| int getNumHandlers() const { return handlers.size(); } | |||
| ComponentTypeHandler* getHandler (const int index) const { return handlers[index]; } | |||
| private: | |||
| OwnedArray <ComponentTypeHandler> handlers; | |||
| }; | |||
| juce_ImplementSingleton_SingleThreaded (ComponentTypeManager); | |||
| //============================================================================== | |||
| ComponentDocument::ComponentDocument (Project* project_, const File& cppFile_) | |||
| : project (project_), cppFile (cppFile_), root (componentDocumentTag) | |||
| { | |||
| checkRootObject(); | |||
| } | |||
| ComponentDocument::~ComponentDocument() | |||
| @@ -61,5 +194,201 @@ bool ComponentDocument::isComponentFile (const File& file) | |||
| bool ComponentDocument::save() | |||
| { | |||
| //XXX | |||
| return false; | |||
| } | |||
| bool ComponentDocument::reload() | |||
| { | |||
| //XXX | |||
| return true; | |||
| } | |||
| bool ComponentDocument::hasChangedSinceLastSave() | |||
| { | |||
| //XXX | |||
| return false; | |||
| } | |||
| void ComponentDocument::checkRootObject() | |||
| { | |||
| jassert (root.hasType (componentDocumentTag)); | |||
| if (! getComponentGroup().isValid()) | |||
| root.addChild (ValueTree (componentGroupTag), -1, 0); | |||
| } | |||
| //============================================================================== | |||
| const int menuItemOffset = 0x63451fa4; | |||
| void ComponentDocument::addNewComponentMenuItems (PopupMenu& menu) const | |||
| { | |||
| const StringArray typeNames (ComponentTypeManager::getInstance()->getTypeNames()); | |||
| for (int i = 0; i < typeNames.size(); ++i) | |||
| menu.addItem (i + menuItemOffset, "New " + typeNames[i]); | |||
| } | |||
| void ComponentDocument::performNewComponentMenuItem (int menuResultCode) | |||
| { | |||
| const StringArray typeNames (ComponentTypeManager::getInstance()->getTypeNames()); | |||
| if (menuResultCode >= menuItemOffset && menuResultCode < menuItemOffset + typeNames.size()) | |||
| { | |||
| ComponentTypeHandler* handler = ComponentTypeManager::getInstance()->getHandler (menuResultCode - menuItemOffset); | |||
| jassert (handler != 0); | |||
| if (handler != 0) | |||
| getComponentGroup().addChild (handler->createNewItem(), -1, getUndoManager()); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| ValueTree ComponentDocument::getComponentGroup() const | |||
| { | |||
| return root.getChildWithName (componentGroupTag); | |||
| } | |||
| int ComponentDocument::getNumComponents() const | |||
| { | |||
| return getComponentGroup().getNumChildren(); | |||
| } | |||
| const ValueTree ComponentDocument::getComponent (int index) const | |||
| { | |||
| return getComponentGroup().getChild (index); | |||
| } | |||
| Component* ComponentDocument::createComponent (int index) const | |||
| { | |||
| const ValueTree v (getComponentGroup().getChild (index)); | |||
| if (v.isValid()) | |||
| { | |||
| Component* c = ComponentTypeManager::getInstance()->createFromStoredType (v); | |||
| c->getProperties().set (idProperty, v[idProperty]); | |||
| jassert (c->getProperties()[idProperty].toString().isNotEmpty()); | |||
| return c; | |||
| } | |||
| return 0; | |||
| } | |||
| void ComponentDocument::updateComponent (Component* comp) const | |||
| { | |||
| const ValueTree v (getComponentState (comp)); | |||
| if (v.isValid()) | |||
| { | |||
| ComponentTypeHandler* handler = ComponentTypeManager::getInstance()->getHandlerFor (v.getType()); | |||
| jassert (handler != 0); | |||
| if (handler != 0) | |||
| handler->updateComponent (comp, v); | |||
| } | |||
| } | |||
| bool ComponentDocument::containsComponent (Component* comp) const | |||
| { | |||
| const ValueTree comps (getComponentGroup()); | |||
| for (int i = 0; i < comps.getNumChildren(); ++i) | |||
| if (isStateForComponent (comps.getChild(i), comp)) | |||
| return true; | |||
| return false; | |||
| } | |||
| const ValueTree ComponentDocument::getComponentState (Component* comp) const | |||
| { | |||
| jassert (comp != 0); | |||
| const ValueTree comps (getComponentGroup()); | |||
| for (int i = 0; i < comps.getNumChildren(); ++i) | |||
| if (isStateForComponent (comps.getChild(i), comp)) | |||
| return comps.getChild(i); | |||
| jassertfalse; | |||
| return ValueTree::invalid; | |||
| } | |||
| bool ComponentDocument::isStateForComponent (const ValueTree& storedState, Component* comp) const | |||
| { | |||
| jassert (comp != 0); | |||
| jassert (! storedState [idProperty].isVoid()); | |||
| return storedState [idProperty] == comp->getProperties() [idProperty]; | |||
| } | |||
| //============================================================================== | |||
| class ComponentDocument::DragHandler | |||
| { | |||
| public: | |||
| DragHandler (ComponentDocument& document_, | |||
| const Array<Component*>& items, | |||
| const MouseEvent& e, | |||
| int zones_) | |||
| : document (document_), | |||
| zones (zones_) | |||
| { | |||
| for (int i = 0; i < items.size(); ++i) | |||
| { | |||
| jassert (items.getUnchecked(i) != 0); | |||
| const ValueTree v (document.getComponentState (items.getUnchecked(i))); | |||
| draggedComponents.add (v); | |||
| originalPositions.add (stringToComponentBounds (v [compBoundsProperty])); | |||
| } | |||
| } | |||
| ~DragHandler() | |||
| { | |||
| } | |||
| void drag (const MouseEvent& e) | |||
| { | |||
| for (int i = 0; i < draggedComponents.size(); ++i) | |||
| { | |||
| if (zones == 0) | |||
| move (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i)); | |||
| else | |||
| resize (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i)); | |||
| } | |||
| } | |||
| void move (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos) | |||
| { | |||
| v.setProperty (compBoundsProperty, componentBoundsToString (originalPos + distance), document.getUndoManager()); | |||
| } | |||
| void resize (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos) | |||
| { | |||
| } | |||
| private: | |||
| ComponentDocument& document; | |||
| Array <ValueTree> draggedComponents; | |||
| Array <Rectangle<int> > originalPositions; | |||
| const int zones; | |||
| }; | |||
| void ComponentDocument::beginDrag (const Array<Component*>& items, const MouseEvent& e, int zones) | |||
| { | |||
| dragger = new DragHandler (*this, items, e, zones); | |||
| } | |||
| void ComponentDocument::continueDrag (const MouseEvent& e) | |||
| { | |||
| if (dragger != 0) | |||
| dragger->drag (e); | |||
| } | |||
| void ComponentDocument::endDrag (const MouseEvent& e) | |||
| { | |||
| if (dragger != 0) | |||
| { | |||
| dragger->drag (e); | |||
| dragger = 0; | |||
| } | |||
| } | |||
| @@ -41,8 +41,38 @@ public: | |||
| static bool isComponentFile (const File& file); | |||
| bool save(); | |||
| bool reload(); | |||
| bool hasChangedSinceLastSave(); | |||
| typedef SelectedItemSet<uint32> SelectedItems; | |||
| //============================================================================== | |||
| int getNumComponents() const; | |||
| const ValueTree getComponent (int index) const; | |||
| Component* createComponent (int index) const; | |||
| void updateComponent (Component* comp) const; | |||
| bool containsComponent (Component* comp) const; | |||
| const ValueTree getComponentState (Component* comp) const; | |||
| bool isStateForComponent (const ValueTree& storedState, Component* comp) const; | |||
| void addNewComponentMenuItems (PopupMenu& menu) const; | |||
| void performNewComponentMenuItem (int menuResultCode); | |||
| //============================================================================== | |||
| enum ResizeZones | |||
| { | |||
| zoneL = 1, | |||
| zoneR = 2, | |||
| zoneT = 4, | |||
| zoneB = 8 | |||
| }; | |||
| void beginDrag (const Array<Component*>& items, const MouseEvent& e, int zones); | |||
| void continueDrag (const MouseEvent& e); | |||
| void endDrag (const MouseEvent& e); | |||
| //============================================================================== | |||
| ValueTree& getRoot() { return root; } | |||
| UndoManager* getUndoManager() throw() { return &undoManager; } | |||
| private: | |||
| @@ -50,6 +80,12 @@ private: | |||
| File cppFile; | |||
| ValueTree root; | |||
| UndoManager undoManager; | |||
| class DragHandler; | |||
| ScopedPointer <DragHandler> dragger; | |||
| void checkRootObject(); | |||
| ValueTree getComponentGroup() const; | |||
| }; | |||
| @@ -27,6 +27,416 @@ | |||
| #include "jucer_ComponentEditor.h" | |||
| //============================================================================== | |||
| class ComponentCanvas : public Component, | |||
| public ValueTree::Listener | |||
| { | |||
| public: | |||
| ComponentCanvas (ComponentEditor& editor_) | |||
| : editor (editor_) | |||
| { | |||
| setOpaque (true); | |||
| addAndMakeVisible (componentHolder = new Component()); | |||
| addAndMakeVisible (overlay = new OverlayComponent (*this)); | |||
| setSize (500, 500); | |||
| getDocument().getRoot().addListener (this); | |||
| updateComponents(); | |||
| } | |||
| ~ComponentCanvas() | |||
| { | |||
| getDocument().getRoot().removeListener (this); | |||
| deleteAllChildren(); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.fillAll (Colours::white); | |||
| } | |||
| void resized() | |||
| { | |||
| componentHolder->setSize (getWidth(), getHeight()); | |||
| overlay->setSize (getWidth(), getHeight()); | |||
| } | |||
| void zoom (float newScale, const Point<int>& centre) | |||
| { | |||
| } | |||
| ComponentEditor& getEditor() { return editor; } | |||
| ComponentDocument& getDocument() { return editor.getDocument(); } | |||
| ComponentDocument::SelectedItems& getSelection() { return selection; } | |||
| Component* findComponentFor (const ValueTree& state) | |||
| { | |||
| ComponentDocument& doc = getDocument(); | |||
| for (int i = componentHolder->getNumChildComponents(); --i >= 0;) | |||
| { | |||
| Component* c = componentHolder->getChildComponent (i); | |||
| if (doc.isStateForComponent (state, c)) | |||
| return c; | |||
| } | |||
| return 0; | |||
| } | |||
| void updateComponents() | |||
| { | |||
| ComponentDocument& doc = getDocument(); | |||
| int i; | |||
| for (i = componentHolder->getNumChildComponents(); --i >= 0;) | |||
| { | |||
| Component* c = componentHolder->getChildComponent (i); | |||
| if (! doc.containsComponent (c)) | |||
| { | |||
| selection.deselect (c->getComponentUID()); | |||
| delete c; | |||
| } | |||
| } | |||
| const int num = doc.getNumComponents(); | |||
| for (i = 0; i < num; ++i) | |||
| { | |||
| const ValueTree v (doc.getComponent (i)); | |||
| Component* c = findComponentFor (v); | |||
| if (c == 0) | |||
| { | |||
| c = doc.createComponent (i); | |||
| componentHolder->addAndMakeVisible (c); | |||
| } | |||
| else | |||
| { | |||
| doc.updateComponent (c); | |||
| } | |||
| } | |||
| } | |||
| void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property) | |||
| { | |||
| updateComponents(); | |||
| } | |||
| void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) | |||
| { | |||
| updateComponents(); | |||
| } | |||
| void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) | |||
| { | |||
| } | |||
| Component* getComponentHolder() const { return componentHolder; } | |||
| const Array<Component*> getSelectedComps() const | |||
| { | |||
| Array <Component*> comps; | |||
| for (int i = 0; i < selection.getNumSelected(); ++i) | |||
| { | |||
| Component* c = getComponentForUID (selection.getSelectedItem (i)); | |||
| jassert (c != 0); | |||
| if (c != 0) | |||
| comps.add (c); | |||
| } | |||
| return comps; | |||
| } | |||
| private: | |||
| ComponentEditor& editor; | |||
| //============================================================================== | |||
| class ComponentSelectorFrame : public Component, | |||
| public ComponentListener | |||
| { | |||
| public: | |||
| ComponentSelectorFrame (ComponentCanvas& canvas_, | |||
| Component* componentToAttachTo) | |||
| : canvas (canvas_), | |||
| component (componentToAttachTo), | |||
| borderThickness (4) | |||
| { | |||
| componentMovedOrResized (*componentToAttachTo, true, true); | |||
| componentToAttachTo->addComponentListener (this); | |||
| } | |||
| ~ComponentSelectorFrame() | |||
| { | |||
| if (component != 0) | |||
| component->removeComponentListener (this); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.setColour (Colours::red.withAlpha (0.1f)); | |||
| g.drawRect (0, 0, getWidth(), getHeight(), borderThickness); | |||
| } | |||
| void mouseEnter (const MouseEvent& e) | |||
| { | |||
| repaint(); | |||
| updateDragZone (e.getPosition()); | |||
| } | |||
| void mouseExit (const MouseEvent& e) | |||
| { | |||
| repaint(); | |||
| updateDragZone (e.getPosition()); | |||
| } | |||
| void mouseMove (const MouseEvent& e) | |||
| { | |||
| updateDragZone (e.getPosition()); | |||
| } | |||
| void mouseDown (const MouseEvent& e) | |||
| { | |||
| jassert (component != 0); | |||
| if (component != 0) | |||
| { | |||
| updateDragZone (e.getPosition()); | |||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, dragZone); | |||
| } | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if (component != 0) | |||
| canvas.getDocument().continueDrag (e); | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| if (component != 0) | |||
| canvas.getDocument().endDrag (e); | |||
| updateDragZone (e.getPosition()); | |||
| } | |||
| void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) | |||
| { | |||
| if (component != 0) | |||
| setBounds (component->getBounds().expanded (borderThickness, borderThickness)); | |||
| } | |||
| void resized() | |||
| { | |||
| } | |||
| uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); } | |||
| private: | |||
| ComponentCanvas& canvas; | |||
| Component::SafePointer<Component> component; | |||
| int dragZone; | |||
| const int borderThickness; | |||
| void updateDragZone (const Point<int>& p) | |||
| { | |||
| int newZone = 0; | |||
| Rectangle<int> r (0, 0, getWidth(), getHeight()); | |||
| r = r.reduced (borderThickness, borderThickness); | |||
| if (! r.contains (p)) | |||
| { | |||
| const int bw = jmax (borderThickness, proportionOfWidth (0.1f), jmin (10, proportionOfWidth (0.33f))); | |||
| const int bh = jmax (borderThickness, proportionOfHeight (0.1f), jmin (10, proportionOfHeight (0.33f))); | |||
| if (p.getX() < bw) | |||
| newZone |= ComponentDocument::zoneL; | |||
| else if (p.getX() >= getWidth() - bw) | |||
| newZone |= ComponentDocument::zoneR; | |||
| if (p.getY() < bh) | |||
| newZone |= ComponentDocument::zoneT; | |||
| else if (p.getY() >= getHeight() - bh) | |||
| newZone |= ComponentDocument::zoneB; | |||
| } | |||
| if (dragZone != newZone) | |||
| { | |||
| dragZone = newZone; | |||
| MouseCursor::StandardCursorType mc = MouseCursor::NormalCursor; | |||
| switch (newZone) | |||
| { | |||
| case (ComponentDocument::zoneL | ComponentDocument::zoneT): mc = MouseCursor::TopLeftCornerResizeCursor; break; | |||
| case ComponentDocument::zoneT: mc = MouseCursor::TopEdgeResizeCursor; break; | |||
| case (ComponentDocument::zoneR | ComponentDocument::zoneT): mc = MouseCursor::TopRightCornerResizeCursor; break; | |||
| case ComponentDocument::zoneL: mc = MouseCursor::LeftEdgeResizeCursor; break; | |||
| case ComponentDocument::zoneR: mc = MouseCursor::RightEdgeResizeCursor; break; | |||
| case (ComponentDocument::zoneL | ComponentDocument::zoneB): mc = MouseCursor::BottomLeftCornerResizeCursor; break; | |||
| case ComponentDocument::zoneB: mc = MouseCursor::BottomEdgeResizeCursor; break; | |||
| case (ComponentDocument::zoneR | ComponentDocument::zoneB): mc = MouseCursor::BottomRightCornerResizeCursor; break; | |||
| default: mc = MouseCursor::NormalCursor; break; | |||
| } | |||
| setMouseCursor (mc); | |||
| } | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class OverlayComponent : public Component, | |||
| public LassoSource <ComponentDocument::SelectedItems::ItemType>, | |||
| public ChangeListener | |||
| { | |||
| public: | |||
| OverlayComponent (ComponentCanvas& canvas_) | |||
| : canvas (canvas_) | |||
| { | |||
| setAlwaysOnTop (true); | |||
| setWantsKeyboardFocus (true); | |||
| canvas.getSelection().addChangeListener (this); | |||
| } | |||
| ~OverlayComponent() | |||
| { | |||
| canvas.getSelection().removeChangeListener (this); | |||
| lasso = 0; | |||
| deleteAllChildren(); | |||
| } | |||
| void mouseDown (const MouseEvent& e) | |||
| { | |||
| lasso = 0; | |||
| if (e.mods.isPopupMenu()) | |||
| { | |||
| PopupMenu m; | |||
| canvas.getDocument().addNewComponentMenuItems (m); | |||
| const int r = m.show(); | |||
| canvas.getDocument().performNewComponentMenuItem (r); | |||
| } | |||
| else | |||
| { | |||
| Component* underMouse = canvas.getComponentHolder()->getComponentAt (e.x, e.y); | |||
| if (underMouse == canvas.getComponentHolder()) | |||
| underMouse = 0; | |||
| if (underMouse == 0 || e.mods.isAltDown()) | |||
| { | |||
| addAndMakeVisible (lasso = new LassoComponent <ComponentDocument::SelectedItems::ItemType>()); | |||
| lasso->beginLasso (e, this); | |||
| } | |||
| else | |||
| { | |||
| mouseDownCompUID = underMouse->getComponentUID(); | |||
| mouseDownResult = canvas.getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods); | |||
| } | |||
| } | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if (lasso != 0) | |||
| lasso->dragLasso (e); | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| if (lasso != 0) | |||
| { | |||
| lasso->endLasso(); | |||
| lasso = 0; | |||
| if (e.mouseWasClicked()) | |||
| canvas.getSelection().deselectAll(); | |||
| } | |||
| else | |||
| { | |||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult); | |||
| } | |||
| } | |||
| void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height) | |||
| { | |||
| const Rectangle<int> lassoArea (x, y, width, height); | |||
| for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;) | |||
| { | |||
| Component* c = canvas.getComponentHolder()->getChildComponent(i); | |||
| if (c != this && c->getBounds().intersects (lassoArea)) | |||
| itemsFound.add (c->getComponentUID()); | |||
| } | |||
| } | |||
| ComponentDocument::SelectedItems& getLassoSelection() { return canvas.getSelection(); } | |||
| void changeListenerCallback (void*) | |||
| { | |||
| updateSelectedComponentOverlays(); | |||
| } | |||
| private: | |||
| ComponentCanvas& canvas; | |||
| ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso; | |||
| bool mouseDownResult; | |||
| uint32 mouseDownCompUID; | |||
| ComponentSelectorFrame* getSelectorFrameFor (Component* c) const | |||
| { | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| ComponentSelectorFrame* overlay = dynamic_cast <ComponentSelectorFrame*> (getChildComponent(i)); | |||
| if (overlay != 0 && overlay->getTargetComponentUID() == c->getComponentUID()) | |||
| return overlay; | |||
| } | |||
| return 0; | |||
| } | |||
| void updateSelectedComponentOverlays() | |||
| { | |||
| ComponentDocument::SelectedItems& selection = canvas.getSelection(); | |||
| int i; | |||
| for (i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| ComponentSelectorFrame* overlay = dynamic_cast <ComponentSelectorFrame*> (getChildComponent(i)); | |||
| if (overlay != 0 && ! selection.isSelected (overlay->getTargetComponentUID())) | |||
| delete overlay; | |||
| } | |||
| for (i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;) | |||
| { | |||
| Component* c = canvas.getComponentHolder()->getChildComponent(i); | |||
| if (c != this && selection.isSelected (c->getComponentUID()) && getSelectorFrameFor (c) == 0) | |||
| addAndMakeVisible (new ComponentSelectorFrame (canvas, c)); | |||
| } | |||
| } | |||
| }; | |||
| Component* componentHolder; | |||
| OverlayComponent* overlay; | |||
| ComponentDocument::SelectedItems selection; | |||
| Component* getComponentForUID (const uint32 uid) const | |||
| { | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| if (componentHolder->getChildComponent (i)->getComponentUID() == uid) | |||
| return componentHolder->getChildComponent (i); | |||
| return 0; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| ComponentEditor::ComponentEditor (OpenDocumentManager::Document* document, | |||
| Project* project_, ComponentDocument* componentDocument_) | |||
| @@ -37,6 +447,9 @@ ComponentEditor::ComponentEditor (OpenDocumentManager::Document* document, | |||
| jassert (componentDocument != 0); | |||
| setOpaque (true); | |||
| addAndMakeVisible (viewport = new Viewport()); | |||
| viewport->setViewedComponent (new ComponentCanvas (*this)); | |||
| } | |||
| ComponentEditor::~ComponentEditor() | |||
| @@ -51,4 +464,5 @@ void ComponentEditor::paint (Graphics& g) | |||
| void ComponentEditor::resized() | |||
| { | |||
| viewport->setBounds (0, 0, getWidth(), getHeight()); | |||
| } | |||
| @@ -52,6 +52,8 @@ public: | |||
| private: | |||
| Project* project; | |||
| ComponentDocument* componentDocument; | |||
| Viewport* viewport; | |||
| }; | |||
| @@ -27,6 +27,7 @@ | |||
| #include "jucer_SourceCodeEditor.h" | |||
| #include "Drawable Editor/jucer_DrawableEditor.h" | |||
| #include "jucer_ItemPreviewComponent.h" | |||
| #include "Component Editor/jucer_ComponentEditor.h" | |||
| //============================================================================== | |||
| @@ -47,7 +48,7 @@ public: | |||
| bool loadedOk() const { return true; } | |||
| bool isForFile (const File& file) const { return modDetector.getFile() == file; } | |||
| bool isForNode (const ValueTree& node) const { return false; } | |||
| bool refersToProject (Project& project) const { return false; } | |||
| bool refersToProject (Project& project) const { return false; } | |||
| const String getName() const { return modDetector.getFile().getFileName(); } | |||
| const String getType() const { return modDetector.getFile().getFileExtension() + " file"; } | |||
| bool needsSaving() const { return codeDoc != 0 && codeDoc->hasChangedSinceSavePoint(); } | |||
| @@ -91,6 +92,66 @@ private: | |||
| CPlusPlusCodeTokeniser cppTokeniser; | |||
| }; | |||
| //============================================================================== | |||
| class ComponentDocumentType : public OpenDocumentManager::Document | |||
| { | |||
| public: | |||
| ComponentDocumentType (Project* project_, const File& file_) | |||
| : project (project_), | |||
| modDetector (file_) | |||
| { | |||
| reloadFromFile(); | |||
| } | |||
| ~ComponentDocumentType() | |||
| { | |||
| componentDoc = 0; | |||
| } | |||
| static bool isComponentFile (const File& file) { return ComponentDocument::isComponentFile (file); } | |||
| bool loadedOk() const { return componentDoc != 0; } | |||
| bool isForFile (const File& file) const { return modDetector.getFile() == file; } | |||
| bool isForNode (const ValueTree& node) const { return false; } | |||
| bool refersToProject (Project& p) const { return project == &p; } | |||
| const String getType() const { return "Jucer Component"; } | |||
| const String getName() const { return modDetector.getFile().getFileName(); } | |||
| bool needsSaving() const { return componentDoc != 0 && componentDoc->hasChangedSinceLastSave(); } | |||
| bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); } | |||
| void reloadFromFile() | |||
| { | |||
| modDetector.updateHash(); | |||
| if (componentDoc == 0) | |||
| componentDoc = new ComponentDocument (project, modDetector.getFile()); | |||
| if (! componentDoc->reload()) | |||
| componentDoc = 0; | |||
| } | |||
| bool save() | |||
| { | |||
| return componentDoc->save(); | |||
| } | |||
| Component* createEditor() | |||
| { | |||
| if (componentDoc == 0) | |||
| { | |||
| jassertfalse; | |||
| return 0; | |||
| } | |||
| return new ComponentEditor (this, project, componentDoc); | |||
| } | |||
| private: | |||
| Project* project; | |||
| FileModificationDetector modDetector; | |||
| ScopedPointer <ComponentDocument> componentDoc; | |||
| }; | |||
| //============================================================================== | |||
| class DrawableDocumentType : public OpenDocumentManager::Document | |||
| { | |||
| @@ -112,7 +173,7 @@ public: | |||
| bool loadedOk() const { return drawableDoc != 0; } | |||
| bool isForFile (const File& file) const { return modDetector.getFile() == file; } | |||
| bool isForNode (const ValueTree& node) const { return false; } | |||
| bool refersToProject (Project& p) const { return project == &p; } | |||
| bool refersToProject (Project& p) const { return project == &p; } | |||
| const String getType() const { return "Drawable"; } | |||
| const String getName() const { return modDetector.getFile().getFileName(); } | |||
| bool needsSaving() const { return drawableDoc != 0 && drawableDoc->hasChangedSinceLastSave(); } | |||
| @@ -165,7 +226,7 @@ public: | |||
| bool loadedOk() const { return true; } | |||
| bool isForFile (const File& file_) const { return file == file_; } | |||
| bool isForNode (const ValueTree& node_) const { return false; } | |||
| bool refersToProject (Project& p) const { return project == &p; } | |||
| bool refersToProject (Project& p) const { return project == &p; } | |||
| bool needsSaving() const { return false; } | |||
| bool save() { return true; } | |||
| bool hasFileBeenModifiedExternally() { return fileModificationTime != file.getLastModificationTime(); } | |||
| @@ -227,7 +288,9 @@ OpenDocumentManager::Document* OpenDocumentManager::getDocumentForFile (Project* | |||
| Document* d = 0; | |||
| if (DrawableDocumentType::isDrawableFile (file)) | |||
| if (ComponentDocumentType::isComponentFile (file)) | |||
| d = new ComponentDocumentType (project, file); | |||
| else if (DrawableDocumentType::isDrawableFile (file)) | |||
| d = new DrawableDocumentType (project, file); | |||
| else if (SourceCodeEditor::isTextFile (file)) | |||
| d = new SourceCodeDocument (file); | |||
| @@ -73,7 +73,7 @@ public: | |||
| void menuItemSelected (int menuItemID, int topLevelMenuIndex); | |||
| ApplicationCommandTarget* getNextCommandTarget(); | |||
| void getAllCommands (Array <CommandID>& commands); | |||
| void getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result); | |||
| void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); | |||
| bool perform (const InvocationInfo& info); | |||
| bool tryToQuitApplication(); | |||
| @@ -67,7 +67,7 @@ public: | |||
| //============================================================================== | |||
| ApplicationCommandTarget* getNextCommandTarget(); | |||
| void getAllCommands (Array <CommandID>& commands); | |||
| void getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result); | |||
| void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); | |||
| bool perform (const InvocationInfo& info); | |||
| static JucerDocumentHolder* getActiveDocumentHolder(); | |||
| @@ -67,7 +67,7 @@ public: | |||
| //============================================================================== | |||
| ApplicationCommandTarget* getNextCommandTarget(); | |||
| void getAllCommands (Array <CommandID>& commands); | |||
| void getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result); | |||
| void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); | |||
| bool isCommandActive (const CommandID commandID); | |||
| bool perform (const InvocationInfo& info); | |||
| @@ -15965,18 +15965,18 @@ ValueTree ValueTree::SharedObject::getChildWithName (const String& typeToMatch) | |||
| { | |||
| for (int i = 0; i < children.size(); ++i) | |||
| if (children.getUnchecked(i)->type == typeToMatch) | |||
| return (SharedObject*) children.getUnchecked(i); | |||
| return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i))); | |||
| return (SharedObject*) 0; | |||
| return ValueTree::invalid; | |||
| } | |||
| ValueTree ValueTree::SharedObject::getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const | |||
| { | |||
| for (int i = 0; i < children.size(); ++i) | |||
| if (children.getUnchecked(i)->getProperty (propertyName) == propertyValue) | |||
| return (SharedObject*) children.getUnchecked(i); | |||
| return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i))); | |||
| return (SharedObject*) 0; | |||
| return ValueTree::invalid; | |||
| } | |||
| bool ValueTree::SharedObject::isAChildOf (const SharedObject* const possibleParent) const | |||
| @@ -16058,6 +16058,8 @@ void ValueTree::SharedObject::removeAllChildren (UndoManager* const undoManager) | |||
| removeChild (children.size() - 1, undoManager); | |||
| } | |||
| ValueTree ValueTree::invalid ((ValueTree::SharedObject*) 0); | |||
| ValueTree::ValueTree (const String& type_) | |||
| : object (new ValueTree::SharedObject (type_)) | |||
| { | |||
| @@ -16123,7 +16125,7 @@ const String ValueTree::getType() const | |||
| ValueTree ValueTree::getParent() const | |||
| { | |||
| return object != 0 ? ValueTree (object->parent) : ValueTree ((SharedObject*) 0); | |||
| return ValueTree (object != 0 ? object->parent : (SharedObject*) 0); | |||
| } | |||
| const var& ValueTree::operator[] (const var::identifier& name) const | |||
| @@ -16230,17 +16232,17 @@ int ValueTree::getNumChildren() const | |||
| ValueTree ValueTree::getChild (int index) const | |||
| { | |||
| return object != 0 ? (SharedObject*) object->children [index] : ValueTree ((SharedObject*) 0); | |||
| return ValueTree (object != 0 ? (SharedObject*) object->children [index] : (SharedObject*) 0); | |||
| } | |||
| ValueTree ValueTree::getChildWithName (const String& type) const | |||
| { | |||
| return object != 0 ? object->getChildWithName (type) : ValueTree ((SharedObject*) 0); | |||
| return object != 0 ? object->getChildWithName (type) : ValueTree::invalid; | |||
| } | |||
| ValueTree ValueTree::getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const | |||
| { | |||
| return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree ((SharedObject*) 0); | |||
| return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid; | |||
| } | |||
| bool ValueTree::isAChildOf (const ValueTree& possibleParent) const | |||
| @@ -16361,7 +16363,7 @@ ValueTree ValueTree::readFromStream (InputStream& input) | |||
| String type (input.readString()); | |||
| if (type.isEmpty()) | |||
| return ValueTree ((SharedObject*) 0); | |||
| return ValueTree::invalid; | |||
| ValueTree v (type); | |||
| @@ -39322,14 +39324,14 @@ Component* Component::removeChildComponent (const int index) | |||
| void Component::removeAllChildren() | |||
| { | |||
| for (int i = childComponentList_.size(); --i >= 0;) | |||
| removeChildComponent (i); | |||
| while (childComponentList_.size() > 0) | |||
| removeChildComponent (childComponentList_.size() - 1); | |||
| } | |||
| void Component::deleteAllChildren() | |||
| { | |||
| for (int i = childComponentList_.size(); --i >= 0;) | |||
| delete (removeChildComponent (i)); | |||
| while (childComponentList_.size() > 0) | |||
| delete (removeChildComponent (childComponentList_.size() - 1)); | |||
| } | |||
| int Component::getNumChildComponents() const throw() | |||
| @@ -92220,6 +92222,10 @@ namespace zlibNamespace | |||
| # define inflateInit_ z_inflateInit_ | |||
| # define inflate z_inflate | |||
| # define inflateEnd z_inflateEnd | |||
| # define inflatePrime z_inflatePrime | |||
| # define inflateGetHeader z_inflateGetHeader | |||
| # define adler32_combine z_adler32_combine | |||
| # define crc32_combine z_crc32_combine | |||
| # define deflateInit2_ z_deflateInit2_ | |||
| # define deflateSetDictionary z_deflateSetDictionary | |||
| # define deflateCopy z_deflateCopy | |||
| @@ -43,7 +43,7 @@ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 51 | |||
| #define JUCE_BUILDNUMBER 13 | |||
| #define JUCE_BUILDNUMBER 14 | |||
| #define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) | |||
| @@ -6772,6 +6772,8 @@ public: | |||
| } | |||
| } | |||
| static ValueTree invalid; | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| @@ -6838,7 +6840,7 @@ private: | |||
| ListenerList <Listener> listeners; | |||
| public: | |||
| ValueTree (SharedObject* const object_); // (can be made private when VC6 support is finally dropped) | |||
| explicit ValueTree (SharedObject* const object_); // (can be made private when VC6 support is finally dropped) | |||
| }; | |||
| #endif // __JUCE_VALUETREE_JUCEHEADER__ | |||
| @@ -25411,6 +25413,8 @@ class JUCE_API SelectedItemSet : public ChangeBroadcaster | |||
| { | |||
| public: | |||
| typedef SelectableItemType ItemType; | |||
| SelectedItemSet() | |||
| { | |||
| } | |||
| @@ -296,18 +296,18 @@ ValueTree ValueTree::SharedObject::getChildWithName (const String& typeToMatch) | |||
| { | |||
| for (int i = 0; i < children.size(); ++i) | |||
| if (children.getUnchecked(i)->type == typeToMatch) | |||
| return (SharedObject*) children.getUnchecked(i); | |||
| return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i))); | |||
| return (SharedObject*) 0; | |||
| return ValueTree::invalid; | |||
| } | |||
| ValueTree ValueTree::SharedObject::getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const | |||
| { | |||
| for (int i = 0; i < children.size(); ++i) | |||
| if (children.getUnchecked(i)->getProperty (propertyName) == propertyValue) | |||
| return (SharedObject*) children.getUnchecked(i); | |||
| return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i))); | |||
| return (SharedObject*) 0; | |||
| return ValueTree::invalid; | |||
| } | |||
| bool ValueTree::SharedObject::isAChildOf (const SharedObject* const possibleParent) const | |||
| @@ -391,6 +391,8 @@ void ValueTree::SharedObject::removeAllChildren (UndoManager* const undoManager) | |||
| //============================================================================== | |||
| ValueTree ValueTree::invalid ((ValueTree::SharedObject*) 0); | |||
| ValueTree::ValueTree (const String& type_) | |||
| : object (new ValueTree::SharedObject (type_)) | |||
| { | |||
| @@ -456,7 +458,7 @@ const String ValueTree::getType() const | |||
| ValueTree ValueTree::getParent() const | |||
| { | |||
| return object != 0 ? ValueTree (object->parent) : ValueTree ((SharedObject*) 0); | |||
| return ValueTree (object != 0 ? object->parent : (SharedObject*) 0); | |||
| } | |||
| const var& ValueTree::operator[] (const var::identifier& name) const | |||
| @@ -565,17 +567,17 @@ int ValueTree::getNumChildren() const | |||
| ValueTree ValueTree::getChild (int index) const | |||
| { | |||
| return object != 0 ? (SharedObject*) object->children [index] : ValueTree ((SharedObject*) 0); | |||
| return ValueTree (object != 0 ? (SharedObject*) object->children [index] : (SharedObject*) 0); | |||
| } | |||
| ValueTree ValueTree::getChildWithName (const String& type) const | |||
| { | |||
| return object != 0 ? object->getChildWithName (type) : ValueTree ((SharedObject*) 0); | |||
| return object != 0 ? object->getChildWithName (type) : ValueTree::invalid; | |||
| } | |||
| ValueTree ValueTree::getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const | |||
| { | |||
| return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree ((SharedObject*) 0); | |||
| return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid; | |||
| } | |||
| bool ValueTree::isAChildOf (const ValueTree& possibleParent) const | |||
| @@ -699,7 +701,7 @@ ValueTree ValueTree::readFromStream (InputStream& input) | |||
| String type (input.readString()); | |||
| if (type.isEmpty()) | |||
| return ValueTree ((SharedObject*) 0); | |||
| return ValueTree::invalid; | |||
| ValueTree v (type); | |||
| @@ -377,6 +377,9 @@ public: | |||
| } | |||
| } | |||
| /** An invalid ValueTree that can be used if you need to return one as an error condition, etc. */ | |||
| static ValueTree invalid; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| @@ -445,7 +448,7 @@ private: | |||
| public: | |||
| /** @internal */ | |||
| ValueTree (SharedObject* const object_); // (can be made private when VC6 support is finally dropped) | |||
| explicit ValueTree (SharedObject* const object_); // (can be made private when VC6 support is finally dropped) | |||
| }; | |||
| @@ -33,7 +33,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 51 | |||
| #define JUCE_BUILDNUMBER 13 | |||
| #define JUCE_BUILDNUMBER 14 | |||
| /** Current Juce version number. | |||
| @@ -1207,14 +1207,14 @@ Component* Component::removeChildComponent (const int index) | |||
| //============================================================================== | |||
| void Component::removeAllChildren() | |||
| { | |||
| for (int i = childComponentList_.size(); --i >= 0;) | |||
| removeChildComponent (i); | |||
| while (childComponentList_.size() > 0) | |||
| removeChildComponent (childComponentList_.size() - 1); | |||
| } | |||
| void Component::deleteAllChildren() | |||
| { | |||
| for (int i = childComponentList_.size(); --i >= 0;) | |||
| delete (removeChildComponent (i)); | |||
| while (childComponentList_.size() > 0) | |||
| delete (removeChildComponent (childComponentList_.size() - 1)); | |||
| } | |||
| //============================================================================== | |||
| @@ -28,6 +28,10 @@ | |||
| # define inflateInit_ z_inflateInit_ | |||
| # define inflate z_inflate | |||
| # define inflateEnd z_inflateEnd | |||
| # define inflatePrime z_inflatePrime | |||
| # define inflateGetHeader z_inflateGetHeader | |||
| # define adler32_combine z_adler32_combine | |||
| # define crc32_combine z_crc32_combine | |||
| # define deflateInit2_ z_deflateInit2_ | |||
| # define deflateSetDictionary z_deflateSetDictionary | |||
| # define deflateCopy z_deflateCopy | |||
| @@ -48,6 +48,9 @@ template <class SelectableItemType> | |||
| class JUCE_API SelectedItemSet : public ChangeBroadcaster | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| typedef SelectableItemType ItemType; | |||
| //============================================================================== | |||
| /** Creates an empty set. */ | |||
| SelectedItemSet() | |||