| @@ -45,10 +45,16 @@ public: | |||
| tb->setButtonText (state ["text"].toString()); | |||
| } | |||
| const ValueTree createNewItem (ComponentDocument& document) | |||
| void initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||
| { | |||
| ValueTree v (ComponentTypeHandler::createNewItem (document)); | |||
| v.setProperty ("text", "New Toggle Button", 0); | |||
| return v; | |||
| ComponentTypeHandler::initialiseNewItem (state, document); | |||
| state.setProperty ("text", "New Toggle Button", 0); | |||
| } | |||
| void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props) | |||
| { | |||
| ComponentTypeHandler::createPropertyEditors (state, document, props); | |||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | |||
| props.getLast()->setTooltip ("The button's text."); | |||
| } | |||
| }; | |||
| @@ -45,10 +45,16 @@ public: | |||
| tb->setButtonText (state ["text"].toString()); | |||
| } | |||
| const ValueTree createNewItem (ComponentDocument& document) | |||
| void initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||
| { | |||
| ValueTree v (ComponentTypeHandler::createNewItem (document)); | |||
| v.setProperty ("text", "New Toggle Button", 0); | |||
| return v; | |||
| ComponentTypeHandler::initialiseNewItem (state, document); | |||
| state.setProperty ("text", "New Toggle Button", 0); | |||
| } | |||
| void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props) | |||
| { | |||
| ComponentTypeHandler::createPropertyEditors (state, document, props); | |||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | |||
| props.getLast()->setTooltip ("The button's text."); | |||
| } | |||
| }; | |||
| @@ -40,17 +40,6 @@ static const char* const compNameProperty = "name"; | |||
| static const char* const metadataTagStart = "JUCER_" "COMPONENT_METADATA_START"; // written like this to avoid thinking this file is a component! | |||
| static const char* const metadataTagEnd = "JUCER_" "COMPONENT_METADATA_END"; | |||
| //============================================================================== | |||
| static const String componentBoundsToString (const Rectangle<int>& bounds) | |||
| { | |||
| return bounds.toString(); | |||
| } | |||
| static const Rectangle<int> stringToComponentBounds (const String& s) | |||
| { | |||
| return Rectangle<int>::fromString (s); | |||
| } | |||
| //============================================================================== | |||
| ComponentTypeHandler::ComponentTypeHandler (const String& name_, const String& xmlTag_, | |||
| const String& memberNameRoot_) | |||
| @@ -63,24 +52,37 @@ ComponentTypeHandler::~ComponentTypeHandler() | |||
| { | |||
| } | |||
| Value ComponentTypeHandler::getValue (const var::identifier& name, ValueTree& state, ComponentDocument& document) const | |||
| { | |||
| return state.getPropertyAsValue (name, document.getUndoManager()); | |||
| } | |||
| void ComponentTypeHandler::updateComponent (Component* comp, const ValueTree& state) | |||
| { | |||
| comp->setBounds (stringToComponentBounds (state [compBoundsProperty])); | |||
| if (comp->getParentComponent() != 0) | |||
| { | |||
| PositionedRectangle pos (state [compBoundsProperty].toString()); | |||
| comp->setBounds (pos.getRectangle (comp->getParentComponent()->getLocalBounds())); | |||
| } | |||
| comp->setName (state [compNameProperty]); | |||
| } | |||
| const ValueTree ComponentTypeHandler::createNewItem (ComponentDocument& document) | |||
| void ComponentTypeHandler::initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||
| { | |||
| ValueTree v (getXmlTag()); | |||
| v.setProperty (idProperty, createAlphaNumericUID(), 0); | |||
| v.setProperty (compNameProperty, String::empty, 0); | |||
| v.setProperty (memberNameProperty, document.getNonExistentMemberName (getMemberNameRoot()), 0); | |||
| v.setProperty (compBoundsProperty, | |||
| componentBoundsToString (getDefaultSize().withPosition (Point<int> (Random::getSystemRandom().nextInt (100) + 100, | |||
| Random::getSystemRandom().nextInt (100) + 100))), 0); | |||
| return v; | |||
| state.setProperty (compNameProperty, String::empty, 0); | |||
| state.setProperty (memberNameProperty, document.getNonExistentMemberName (getMemberNameRoot()), 0); | |||
| state.setProperty (compBoundsProperty, | |||
| getDefaultSize().withPosition (Point<int> (Random::getSystemRandom().nextInt (100) + 100, | |||
| Random::getSystemRandom().nextInt (100) + 100)).toString(), 0); | |||
| } | |||
| void ComponentTypeHandler::createPropertyEditors (ValueTree& state, ComponentDocument& document, | |||
| Array <PropertyComponent*>& props) | |||
| { | |||
| props.add (new TextPropertyComponent (getValue (compBoundsProperty, state, document), "Bounds", 512, false)); | |||
| props.getLast()->setTooltip ("The component's position."); | |||
| } | |||
| //============================================================================== | |||
| class ComponentTypeManager : public DeletedAtShutdown | |||
| @@ -326,7 +328,13 @@ void ComponentDocument::performNewComponentMenuItem (int menuResultCode) | |||
| jassert (handler != 0); | |||
| if (handler != 0) | |||
| getComponentGroup().addChild (handler->createNewItem (*this), -1, getUndoManager()); | |||
| { | |||
| ValueTree state (handler->getXmlTag()); | |||
| state.setProperty (idProperty, createAlphaNumericUID(), 0); | |||
| handler->initialiseNewItem (state, *this); | |||
| getComponentGroup().addChild (state, -1, getUndoManager()); | |||
| } | |||
| } | |||
| } | |||
| @@ -413,6 +421,20 @@ const ValueTree ComponentDocument::getComponentState (Component* comp) const | |||
| return ValueTree::invalid; | |||
| } | |||
| void ComponentDocument::getComponentProperties (Array <PropertyComponent*>& props, Component* comp) | |||
| { | |||
| ValueTree v (getComponentState (comp)); | |||
| if (v.isValid()) | |||
| { | |||
| ComponentTypeHandler* handler = ComponentTypeManager::getInstance()->getHandlerFor (v.getType()); | |||
| jassert (handler != 0); | |||
| if (handler != 0) | |||
| handler->createPropertyEditors (v, *this, props); | |||
| } | |||
| } | |||
| bool ComponentDocument::isStateForComponent (const ValueTree& storedState, Component* comp) const | |||
| { | |||
| jassert (comp != 0); | |||
| @@ -438,6 +460,12 @@ const String ComponentDocument::getNonExistentMemberName (String suggestedName) | |||
| return suggestedName; | |||
| } | |||
| //============================================================================== | |||
| UndoManager* ComponentDocument::getUndoManager() | |||
| { | |||
| return &undoManager; | |||
| } | |||
| //============================================================================== | |||
| class ComponentDocument::DragHandler | |||
| { | |||
| @@ -452,21 +480,37 @@ public: | |||
| for (int i = 0; i < items.size(); ++i) | |||
| { | |||
| Component* comp = items.getUnchecked(i); | |||
| jassert (items.getUnchecked(i) != 0); | |||
| jassert (comp != 0); | |||
| parentComponentSize.setSize (comp->getParentWidth(), comp->getParentHeight()); | |||
| jassert (! parentComponentSize.isEmpty()); | |||
| const ValueTree v (document.getComponentState (comp)); | |||
| draggedComponents.add (v); | |||
| const Rectangle<int> pos (stringToComponentBounds (v [compBoundsProperty])); | |||
| originalPositions.add (pos); | |||
| PositionedRectangle pos (v [compBoundsProperty].toString()); | |||
| originalPositions.add (pos.getRectangle (parentComponentSize)); | |||
| const Rectangle<float> floatPos ((float) pos.getX(), (float) pos.getY(), | |||
| (float) pos.getWidth(), (float) pos.getHeight()); | |||
| verticalSnapPositions.add (floatPos.getX()); | |||
| verticalSnapPositions.add (floatPos.getCentreX()); | |||
| verticalSnapPositions.add (floatPos.getRight()); | |||
| horizontalSnapPositions.add (floatPos.getY()); | |||
| verticalSnapPositions.add (floatPos.getCentreY()); | |||
| horizontalSnapPositions.add (floatPos.getBottom()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingLeftEdge()) | |||
| verticalSnapPositions.add (floatPos.getX()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingLeftEdge() || zone.isDraggingRightEdge()) | |||
| verticalSnapPositions.add (floatPos.getCentreX()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingRightEdge()) | |||
| verticalSnapPositions.add (floatPos.getRight()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingTopEdge()) | |||
| horizontalSnapPositions.add (floatPos.getY()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingTopEdge() || zone.isDraggingBottomEdge()) | |||
| verticalSnapPositions.add (floatPos.getCentreY()); | |||
| if (zone.isDraggingWholeObject() || zone.isDraggingBottomEdge()) | |||
| horizontalSnapPositions.add (floatPos.getBottom()); | |||
| } | |||
| document.beginNewTransaction(); | |||
| @@ -488,7 +532,11 @@ public: | |||
| void move (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos) | |||
| { | |||
| Rectangle<int> newBounds (zone.resizeRectangleBy (originalPos, distance)); | |||
| v.setProperty (compBoundsProperty, componentBoundsToString (newBounds), document.getUndoManager()); | |||
| PositionedRectangle pr (v [compBoundsProperty].toString()); | |||
| pr.updateFrom (newBounds, parentComponentSize); | |||
| v.setProperty (compBoundsProperty, pr.toString(), document.getUndoManager()); | |||
| } | |||
| const Array<float> getVerticalSnapPositions (const Point<int>& distance) const | |||
| @@ -510,6 +558,7 @@ public: | |||
| } | |||
| private: | |||
| Rectangle<int> parentComponentSize; | |||
| ComponentDocument& document; | |||
| Array <ValueTree> draggedComponents; | |||
| Array <Rectangle<int> > originalPositions; | |||
| @@ -59,6 +59,7 @@ public: | |||
| void updateComponent (Component* comp) const; | |||
| bool containsComponent (Component* comp) const; | |||
| const ValueTree getComponentState (Component* comp) const; | |||
| void getComponentProperties (Array <PropertyComponent*>& props, Component* comp); | |||
| bool isStateForComponent (const ValueTree& storedState, Component* comp) const; | |||
| void addNewComponentMenuItems (PopupMenu& menu) const; | |||
| @@ -72,7 +73,7 @@ public: | |||
| //============================================================================== | |||
| ValueTree& getRoot() { return root; } | |||
| UndoManager* getUndoManager() throw() { return &undoManager; } | |||
| UndoManager* getUndoManager(); | |||
| void beginNewTransaction(); | |||
| void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property); | |||
| @@ -114,7 +115,10 @@ public: | |||
| virtual const Rectangle<int> getDefaultSize() = 0; | |||
| virtual void updateComponent (Component* comp, const ValueTree& state); | |||
| virtual const ValueTree createNewItem (ComponentDocument& document); | |||
| virtual void initialiseNewItem (ValueTree& state, ComponentDocument& document); | |||
| virtual void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props); | |||
| Value getValue (const var::identifier& name, ValueTree& state, ComponentDocument& document) const; | |||
| //============================================================================== | |||
| protected: | |||
| @@ -28,11 +28,12 @@ | |||
| //============================================================================== | |||
| class ComponentCanvas : public Component, | |||
| public ValueTree::Listener | |||
| class ComponentEditor::Canvas : public Component, | |||
| public ValueTree::Listener, | |||
| public Timer | |||
| { | |||
| public: | |||
| ComponentCanvas (ComponentEditor& editor_) | |||
| Canvas (ComponentEditor& editor_) | |||
| : editor (editor_), borderThickness (4) | |||
| { | |||
| setOpaque (true); | |||
| @@ -45,7 +46,7 @@ public: | |||
| updateComponents(); | |||
| } | |||
| ~ComponentCanvas() | |||
| ~Canvas() | |||
| { | |||
| getDocument().getRoot().removeListener (this); | |||
| deleteAllChildren(); | |||
| @@ -60,10 +61,9 @@ public: | |||
| void resized() | |||
| { | |||
| componentHolder->setBounds (borderThickness, borderThickness, | |||
| getWidth() - borderThickness * 2, getHeight() - borderThickness * 2); | |||
| componentHolder->setBounds (getLocalBounds().reduced (borderThickness, borderThickness)); | |||
| overlay->setBounds (componentHolder->getBounds()); | |||
| updateComponents(); | |||
| } | |||
| void zoom (float newScale, const Point<int>& centre) | |||
| @@ -121,6 +121,8 @@ public: | |||
| doc.updateComponent (c); | |||
| } | |||
| } | |||
| startTimer (500); | |||
| } | |||
| void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property) | |||
| @@ -141,7 +143,8 @@ public: | |||
| const Array<Component*> getSelectedComps() const | |||
| { | |||
| Array <Component*> comps; | |||
| Array<Component*> comps; | |||
| for (int i = 0; i < selection.getNumSelected(); ++i) | |||
| { | |||
| Component* c = getComponentForUID (selection.getSelectedItem (i)); | |||
| @@ -153,6 +156,25 @@ public: | |||
| return comps; | |||
| } | |||
| void getSelectedItemProperties (Array <PropertyComponent*>& props) | |||
| { | |||
| //xxx needs to handle multiple selections.. | |||
| if (selection.getNumSelected() == 1) | |||
| { | |||
| Component* c = getComponentForUID (selection.getSelectedItem (0)); | |||
| getDocument().getComponentProperties (props, c); | |||
| } | |||
| } | |||
| void timerCallback() | |||
| { | |||
| stopTimer(); | |||
| if (! Component::isMouseButtonDownAnywhere()) | |||
| getDocument().beginNewTransaction(); | |||
| } | |||
| private: | |||
| ComponentEditor& editor; | |||
| const int borderThickness; | |||
| @@ -162,7 +184,7 @@ private: | |||
| public ComponentListener | |||
| { | |||
| public: | |||
| ComponentResizeFrame (ComponentCanvas& canvas_, | |||
| ComponentResizeFrame (Canvas& canvas_, | |||
| Component* componentToAttachTo) | |||
| : canvas (canvas_), | |||
| component (componentToAttachTo), | |||
| @@ -240,7 +262,7 @@ private: | |||
| uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); } | |||
| private: | |||
| ComponentCanvas& canvas; | |||
| Canvas& canvas; | |||
| Component::SafePointer<Component> component; | |||
| ResizableBorderComponent::Zone dragZone; | |||
| const int borderThickness; | |||
| @@ -270,7 +292,7 @@ private: | |||
| public ChangeListener | |||
| { | |||
| public: | |||
| OverlayComponent (ComponentCanvas& canvas_) | |||
| OverlayComponent (Canvas& canvas_) | |||
| : canvas (canvas_) | |||
| { | |||
| setAlwaysOnTop (true); | |||
| @@ -382,7 +404,7 @@ private: | |||
| } | |||
| private: | |||
| ComponentCanvas& canvas; | |||
| Canvas& canvas; | |||
| ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso; | |||
| bool mouseDownResult, isDraggingClickedComp; | |||
| uint32 mouseDownCompUID; | |||
| @@ -436,24 +458,76 @@ private: | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class ComponentEditor::InfoPanel : public Component, | |||
| public ChangeListener | |||
| { | |||
| public: | |||
| InfoPanel (ComponentEditor& editor_) | |||
| : editor (editor_) | |||
| { | |||
| setOpaque (true); | |||
| addAndMakeVisible (props = new PropertyPanel()); | |||
| editor.getCanvas()->getSelection().addChangeListener (this); | |||
| } | |||
| ~InfoPanel() | |||
| { | |||
| editor.getCanvas()->getSelection().removeChangeListener (this); | |||
| props->clear(); | |||
| deleteAllChildren(); | |||
| } | |||
| void changeListenerCallback (void*) | |||
| { | |||
| Array <PropertyComponent*> newComps; | |||
| editor.getCanvas()->getSelectedItemProperties (newComps); | |||
| props->clear(); | |||
| props->addProperties (newComps); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.fillAll (Colour::greyLevel (0.92f)); | |||
| } | |||
| void resized() | |||
| { | |||
| props->setSize (getWidth(), getHeight()); | |||
| } | |||
| private: | |||
| ComponentEditor& editor; | |||
| PropertyPanel* props; | |||
| }; | |||
| //============================================================================== | |||
| ComponentEditor::ComponentEditor (OpenDocumentManager::Document* document, | |||
| Project* project_, ComponentDocument* componentDocument_) | |||
| : DocumentEditorComponent (document), | |||
| project (project_), | |||
| componentDocument (componentDocument_) | |||
| componentDocument (componentDocument_), | |||
| infoPanel (0) | |||
| { | |||
| setOpaque (true); | |||
| addAndMakeVisible (viewport = new Viewport()); | |||
| if (document != 0) | |||
| viewport->setViewedComponent (new ComponentCanvas (*this)); | |||
| { | |||
| viewport->setViewedComponent (new Canvas (*this)); | |||
| addAndMakeVisible (infoPanel = new InfoPanel (*this)); | |||
| } | |||
| } | |||
| ComponentEditor::~ComponentEditor() | |||
| { | |||
| deleteAndZero (infoPanel); | |||
| deleteAllChildren(); | |||
| } | |||
| @@ -464,7 +538,16 @@ void ComponentEditor::paint (Graphics& g) | |||
| void ComponentEditor::resized() | |||
| { | |||
| viewport->setBounds (0, 0, getWidth(), getHeight()); | |||
| const int infoPanelWidth = 200; | |||
| viewport->setBounds (0, 0, getWidth() - infoPanelWidth, getHeight()); | |||
| if (infoPanel != 0) | |||
| infoPanel->setBounds (getWidth() - infoPanelWidth, 0, infoPanelWidth, getHeight()); | |||
| } | |||
| ComponentEditor::Canvas* ComponentEditor::getCanvas() const | |||
| { | |||
| return dynamic_cast <Canvas*> (viewport->getViewedComponent()); | |||
| } | |||
| void ComponentEditor::getAllCommands (Array <CommandID>& commands) | |||
| @@ -53,11 +53,19 @@ public: | |||
| ComponentDocument& getDocument() const { return *componentDocument; } | |||
| Viewport* getViewport() const throw() { return viewport; } | |||
| class Canvas; | |||
| Canvas* getCanvas() const; | |||
| private: | |||
| class InfoPanel; | |||
| Project* project; | |||
| ComponentDocument* componentDocument; | |||
| Viewport* viewport; | |||
| InfoPanel* infoPanel; | |||
| }; | |||
| @@ -32624,7 +32624,7 @@ private: | |||
| originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWL_WNDPROC); | |||
| if (! pluginWantsKeys) | |||
| SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) vstHookWndProc); | |||
| SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) vstHookWndProc); | |||
| #pragma warning (pop) | |||
| @@ -32717,7 +32717,7 @@ private: | |||
| #pragma warning (disable: 4244) | |||
| if (pluginHWND != 0 && IsWindow (pluginHWND)) | |||
| SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) originalWndProc); | |||
| SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc); | |||
| #pragma warning (pop) | |||