| @@ -722,7 +722,7 @@ public: | |||
| canvas->handleUpdateNowIfNeeded(); | |||
| g.fillAll (Colours::white); | |||
| const Point<int> origin (canvas->getOrigin()); | |||
| const Point<int> origin (canvas->getScale().origin); | |||
| g.setOrigin (origin.getX(), origin.getY()); | |||
| if (origin.getX() > 0) | |||
| @@ -242,167 +242,6 @@ private: | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class EditorCanvasBase::MarkerComponent : public EditorCanvasBase::OverlayItemComponent | |||
| { | |||
| public: | |||
| MarkerComponent (EditorCanvasBase* canvas_, const ValueTree& marker_, bool isX_, int headSize_) | |||
| : OverlayItemComponent (canvas_), marker (marker_), isX (isX_), headSize (headSize_ - 2), | |||
| dragStartPos (0), isDragging (false) | |||
| { | |||
| } | |||
| ~MarkerComponent() | |||
| { | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.setColour (Colours::lightblue.withAlpha (isMouseOverOrDragging() ? 0.9f : 0.5f)); | |||
| g.fillPath (path); | |||
| } | |||
| void updatePosition() | |||
| { | |||
| RelativeCoordinate coord (getMarkerList().getCoordinate (marker)); | |||
| const int pos = roundToInt (coord.resolve (&getMarkerList())); | |||
| const int width = 8; | |||
| if (isX) | |||
| setBoundsInTargetSpace (Rectangle<int> (pos - width, -canvas->getOrigin().getY() - headSize, | |||
| width * 2, getParentHeight())); | |||
| else | |||
| setBoundsInTargetSpace (Rectangle<int> (-canvas->getOrigin().getX() - headSize, pos - width, | |||
| getParentWidth(), width * 2)); | |||
| labelText = "name: " + getMarkerList().getName (marker) + "\nposition: " + coord.toString(); | |||
| updateLabel(); | |||
| } | |||
| void updateLabel() | |||
| { | |||
| if (isMouseOverOrDragging() && (getWidth() > 1 || getHeight() > 1)) | |||
| label.update (getParentComponent(), labelText, Colours::darkgreen, | |||
| isX ? getBounds().getCentreX() : getX() + headSize, | |||
| isX ? getY() + headSize : getBounds().getCentreY(), true, true); | |||
| else | |||
| label.remove(); | |||
| } | |||
| bool hitTest (int x, int y) | |||
| { | |||
| return (isX ? y : x) < headSize; | |||
| } | |||
| void resized() | |||
| { | |||
| const float lineThickness = 1.0f; | |||
| path.clear(); | |||
| if (isX) | |||
| { | |||
| const float centre = getWidth() / 2 + 0.5f; | |||
| path.addLineSegment (Line<float> (centre, 2.0f, centre, getHeight() + 1.0f), lineThickness); | |||
| path.addTriangle (1.0f, 0.0f, centre * 2.0f - 1.0f, 0.0f, centre, headSize + 1.0f); | |||
| } | |||
| else | |||
| { | |||
| const float centre = getHeight() / 2 + 0.5f; | |||
| path.addLineSegment (Line<float> (2.0f, centre, getWidth() + 1.0f, centre), lineThickness); | |||
| path.addTriangle (0.0f, centre * 2.0f - 1.0f, 0.0f, 1.0f, headSize + 1.0f, centre); | |||
| } | |||
| updateLabel(); | |||
| } | |||
| void mouseDown (const MouseEvent& e) | |||
| { | |||
| mouseDownPos = e.getEventRelativeTo (getParentComponent()).getMouseDownPosition(); | |||
| toFront (false); | |||
| updateLabel(); | |||
| canvas->getSelection().selectOnly (getMarkerList().getId (marker)); | |||
| if (e.mods.isPopupMenu()) | |||
| { | |||
| isDragging = false; | |||
| } | |||
| else | |||
| { | |||
| isDragging = true; | |||
| canvas->getUndoManager().beginNewTransaction(); | |||
| RelativeCoordinate coord (getMarkerList().getCoordinate (marker)); | |||
| dragStartPos = coord.resolve (&getMarkerList()); | |||
| } | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if (isDragging) | |||
| { | |||
| autoScrollForMouseEvent (e); | |||
| const MouseEvent e2 (e.getEventRelativeTo (getParentComponent())); | |||
| canvas->getUndoManager().undoCurrentTransactionOnly(); | |||
| Rectangle<int> axis; | |||
| if (isX) | |||
| axis.setBounds (0, 0, getParentWidth(), headSize); | |||
| else | |||
| axis.setBounds (0, 0, headSize, getParentHeight()); | |||
| if (axis.expanded (isX ? 500 : 30, isX ? 30 : 500).contains (e.x, e.y)) | |||
| { | |||
| RelativeCoordinate coord (getMarkerList().getCoordinate (marker)); | |||
| // (can't use getDistanceFromDragStart() because it doesn't take into account auto-scrolling) | |||
| coord.moveToAbsolute (canvas->limitMarkerPosition (dragStartPos + (isX ? e2.x - mouseDownPos.getX() | |||
| : e2.y - mouseDownPos.getY())), | |||
| &getMarkerList()); | |||
| getMarkerList().setCoordinate (marker, coord); | |||
| } | |||
| else | |||
| { | |||
| getMarkerList().deleteMarker (marker); | |||
| } | |||
| } | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| canvas->getUndoManager().beginNewTransaction(); | |||
| updateLabel(); | |||
| } | |||
| void mouseEnter (const MouseEvent& e) | |||
| { | |||
| updateLabel(); | |||
| repaint(); | |||
| } | |||
| void mouseExit (const MouseEvent& e) | |||
| { | |||
| updateLabel(); | |||
| repaint(); | |||
| } | |||
| MarkerListBase& getMarkerList() { return canvas->getMarkerList (isX); } | |||
| ValueTree marker; | |||
| const bool isX; | |||
| private: | |||
| const int headSize; | |||
| Path path; | |||
| double dragStartPos; | |||
| bool isDragging; | |||
| FloatingLabelComponent label; | |||
| String labelText; | |||
| Point<int> mouseDownPos; | |||
| }; | |||
| //============================================================================== | |||
| class EditorCanvasBase::OverlayComponent : public Component, | |||
| public LassoSource <SelectedItems::ItemType>, | |||
| @@ -421,8 +260,6 @@ public: | |||
| getSelection().removeChangeListener (this); | |||
| lasso = 0; | |||
| resizers.clear(); | |||
| markersX.clear(); | |||
| markersY.clear(); | |||
| controlPoints.clear(); | |||
| deleteAllChildren(); | |||
| } | |||
| @@ -526,30 +363,13 @@ public: | |||
| void mouseDoubleClick (const MouseEvent& e) | |||
| { | |||
| const BorderSize& border = canvas->border; | |||
| const Rectangle<int> xAxis (border.getLeft(), 0, getWidth() - border.getLeftAndRight(), border.getTop()); | |||
| const Rectangle<int> yAxis (0, border.getTop(), border.getLeft(), getHeight() - border.getTopAndBottom()); | |||
| const MouseEvent e2 (e.getEventRelativeTo (canvas->getComponentHolder())); | |||
| const SelectedItems::ItemType underMouse (canvas->findObjectIdAt (canvas->screenSpaceToObjectSpace (e2.getPosition()))); | |||
| if (xAxis.contains (e.x, e.y)) | |||
| if (underMouse.isNotEmpty()) | |||
| { | |||
| canvas->getMarkerList (true).createMarker (canvas->getMarkerList (true).getNonexistentMarkerName ("Marker"), | |||
| e.x - xAxis.getX()); | |||
| } | |||
| else if (yAxis.contains (e.x, e.y)) | |||
| { | |||
| canvas->getMarkerList (false).createMarker (canvas->getMarkerList (false).getNonexistentMarkerName ("Marker"), | |||
| e.y - yAxis.getY()); | |||
| } | |||
| else | |||
| { | |||
| const MouseEvent e2 (e.getEventRelativeTo (canvas->getComponentHolder())); | |||
| const SelectedItems::ItemType underMouse (canvas->findObjectIdAt (canvas->screenSpaceToObjectSpace (e2.getPosition()))); | |||
| if (underMouse.isNotEmpty()) | |||
| { | |||
| const ValueTree state (canvas->getObjectState (underMouse)); | |||
| canvas->objectDoubleClicked (e2, state); | |||
| } | |||
| const ValueTree state (canvas->getObjectState (underMouse)); | |||
| canvas->objectDoubleClicked (e2, state); | |||
| } | |||
| } | |||
| @@ -602,7 +422,6 @@ public: | |||
| { | |||
| updateResizeFrames(); | |||
| updateControlPoints(); | |||
| updateMarkers(); | |||
| } | |||
| private: | |||
| @@ -612,7 +431,6 @@ private: | |||
| bool mouseDownResult, isDraggingClickedComp; | |||
| SelectedItems::ItemType mouseDownCompUID; | |||
| OwnedArray <ResizeFrame> resizers; | |||
| OwnedArray <MarkerComponent> markersX, markersY; | |||
| OwnedArray <OverlayItemComponent> controlPoints; | |||
| void updateResizeFrames() | |||
| @@ -678,54 +496,6 @@ private: | |||
| canvas->updateControlPointComponents (this, controlPoints); | |||
| } | |||
| void updateMarkers (OwnedArray <MarkerComponent>& markers, const bool isX) | |||
| { | |||
| MarkerListBase& markerList = canvas->getMarkerList (isX); | |||
| const int num = markerList.size(); | |||
| Array<ValueTree> requiredMarkers; | |||
| requiredMarkers.ensureStorageAllocated (num); | |||
| int i; | |||
| for (i = 0; i < num; ++i) | |||
| requiredMarkers.add (markerList.getMarker (i)); | |||
| for (i = markers.size(); --i >= 0;) | |||
| { | |||
| MarkerComponent* marker = markers.getUnchecked (i); | |||
| const int index = requiredMarkers.indexOf (marker->marker); | |||
| if (index >= 0) | |||
| { | |||
| marker->updatePosition(); | |||
| requiredMarkers.removeValue (marker->marker); | |||
| } | |||
| else | |||
| { | |||
| if (marker->isMouseButtonDown()) | |||
| marker->setBounds (-1, -1, 1, 1); | |||
| else | |||
| markers.remove (i); | |||
| } | |||
| } | |||
| for (i = requiredMarkers.size(); --i >= 0;) | |||
| { | |||
| MarkerComponent* marker = new MarkerComponent (canvas, requiredMarkers.getReference(i), | |||
| isX, isX ? canvas->border.getTop() | |||
| : canvas->border.getLeft()); | |||
| markers.add (marker); | |||
| addAndMakeVisible (marker); | |||
| marker->updatePosition(); | |||
| } | |||
| } | |||
| void updateMarkers() | |||
| { | |||
| updateMarkers (markersX, true); | |||
| updateMarkers (markersY, false); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| @@ -824,8 +594,7 @@ private: | |||
| //============================================================================== | |||
| EditorCanvasBase::EditorCanvasBase() | |||
| : border (14), | |||
| scaleFactor (1.0) | |||
| : border (8, 8, 14, 14) | |||
| { | |||
| //setOpaque (true); | |||
| } | |||
| @@ -858,32 +627,32 @@ EditorPanelBase* EditorCanvasBase::getPanel() const | |||
| const Point<int> EditorCanvasBase::screenSpaceToObjectSpace (const Point<int>& p) const | |||
| { | |||
| return p - origin; | |||
| return p - scale.origin; | |||
| } | |||
| const Point<int> EditorCanvasBase::objectSpaceToScreenSpace (const Point<int>& p) const | |||
| { | |||
| return p + origin; | |||
| return p + scale.origin; | |||
| } | |||
| const Point<float> EditorCanvasBase::screenSpaceToObjectSpace (const Point<float>& p) const | |||
| { | |||
| return p - origin.toFloat(); | |||
| return p - scale.origin.toFloat(); | |||
| } | |||
| const Point<float> EditorCanvasBase::objectSpaceToScreenSpace (const Point<float>& p) const | |||
| { | |||
| return p + origin.toFloat(); | |||
| return p + scale.origin.toFloat(); | |||
| } | |||
| const Rectangle<int> EditorCanvasBase::screenSpaceToObjectSpace (const Rectangle<int>& r) const | |||
| { | |||
| return r - origin; | |||
| return r - scale.origin; | |||
| } | |||
| const Rectangle<int> EditorCanvasBase::objectSpaceToScreenSpace (const Rectangle<int>& r) const | |||
| { | |||
| return r + origin; | |||
| return r + scale.origin; | |||
| } | |||
| void EditorCanvasBase::enableResizingMode() | |||
| @@ -909,47 +678,11 @@ bool EditorCanvasBase::isRotating() const | |||
| //============================================================================== | |||
| void EditorCanvasBase::paint (Graphics& g) | |||
| { | |||
| g.setFont (border.getTop() - 5.0f); | |||
| g.setColour (Colour::greyLevel (0.9f)); | |||
| //g.drawHorizontalLine (border.getTop() - 1, 2.0f, (float) getWidth() - border.getRight()); | |||
| //g.drawVerticalLine (border.getLeft() - 1, 2.0f, (float) getHeight() - border.getBottom()); | |||
| drawXAxis (g, Rectangle<int> (border.getLeft(), 0, componentHolder->getWidth(), border.getTop())); | |||
| drawYAxis (g, Rectangle<int> (0, border.getTop(), border.getLeft(), componentHolder->getHeight())); | |||
| } | |||
| void EditorCanvasBase::drawXAxis (Graphics& g, const Rectangle<int>& r) | |||
| void EditorCanvasBase::setScale (const Scale& newScale) | |||
| { | |||
| TickIterator ticks (-origin.getX(), r.getWidth(), 1.0, 10, 50); | |||
| float pos, tickLength; | |||
| String label; | |||
| while (ticks.getNextTick (pos, tickLength, label)) | |||
| { | |||
| if (pos > 0) | |||
| { | |||
| g.drawVerticalLine (r.getX() + (int) pos, r.getBottom() - tickLength * r.getHeight(), (float) r.getBottom()); | |||
| g.drawSingleLineText (label, r.getX() + (int) pos + 2, (int) r.getBottom() - 6); | |||
| } | |||
| } | |||
| } | |||
| void EditorCanvasBase::drawYAxis (Graphics& g, const Rectangle<int>& r) | |||
| { | |||
| TickIterator ticks (-origin.getY(), r.getHeight(), 1.0, 10, 80); | |||
| float pos, tickLength; | |||
| String label; | |||
| while (ticks.getNextTick (pos, tickLength, label)) | |||
| { | |||
| if (pos > 0) | |||
| { | |||
| g.drawHorizontalLine (r.getY() + (int) pos, r.getRight() - tickLength * r.getWidth(), (float) r.getRight()); | |||
| g.drawTextAsPath (label, AffineTransform::rotation (float_Pi / -2.0f) | |||
| .translated (r.getRight() - 6.0f, r.getY() + pos - 2.0f)); | |||
| } | |||
| } | |||
| jassertfalse; | |||
| } | |||
| const Rectangle<int> EditorCanvasBase::getContentArea() const | |||
| @@ -968,16 +701,20 @@ void EditorCanvasBase::handleAsyncUpdate() | |||
| const int newWidth = jmax (canvasBounds.getWidth(), canvasBounds.getRight()) + border.getLeftAndRight(); | |||
| const int newHeight = jmax (canvasBounds.getHeight(), canvasBounds.getBottom()) + border.getTopAndBottom(); | |||
| if (origin != newOrigin) | |||
| if (scale.origin != newOrigin) | |||
| { | |||
| repaint(); | |||
| const Point<int> oldOrigin (origin); | |||
| origin = newOrigin; | |||
| const Point<int> oldOrigin (scale.origin); | |||
| scale.origin = newOrigin; | |||
| setBounds (jmin (0, getX() + oldOrigin.getX() - origin.getX()), | |||
| jmin (0, getY() + oldOrigin.getY() - origin.getY()), | |||
| setBounds (jmin (0, getX() + oldOrigin.getX() - scale.origin.getX()), | |||
| jmin (0, getY() + oldOrigin.getY() - scale.origin.getY()), | |||
| newWidth, newHeight); | |||
| EditorPanelBase* panel = getPanel(); | |||
| if (panel != 0) | |||
| panel->updateRulers(); | |||
| } | |||
| else if (getWidth() != newWidth || getHeight() != newHeight) | |||
| { | |||
| @@ -986,6 +723,10 @@ void EditorCanvasBase::handleAsyncUpdate() | |||
| else | |||
| { | |||
| overlay->update(); | |||
| EditorPanelBase* panel = getPanel(); | |||
| if (panel != 0) | |||
| panel->updateMarkers(); | |||
| } | |||
| } | |||
| @@ -1007,7 +748,7 @@ void EditorCanvasBase::hideSizeGuides() { overlay->hideSizeGuides(); } | |||
| void EditorCanvasBase::beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone, | |||
| bool isRotating, const Point<float>& rotationCentre) | |||
| { | |||
| dragger = createDragOperation (e.getEventRelativeTo (overlay).getPosition() - origin, overlay, zone, isRotating); | |||
| dragger = createDragOperation (screenSpaceToObjectSpace (e.getEventRelativeTo (overlay).getPosition()), overlay, zone, isRotating); | |||
| dragger->setRotationCentre (rotationCentre); | |||
| repaint(); | |||
| } | |||
| @@ -1017,7 +758,7 @@ void EditorCanvasBase::continueDrag (const MouseEvent& e) | |||
| MouseEvent e2 (e.getEventRelativeTo (overlay)); | |||
| if (dragger != 0) | |||
| dragger->drag (e2, e2.getPosition() - origin); | |||
| dragger->drag (e2, screenSpaceToObjectSpace (e2.getPosition())); | |||
| } | |||
| void EditorCanvasBase::endDrag (const MouseEvent& e) | |||
| @@ -1025,7 +766,7 @@ void EditorCanvasBase::endDrag (const MouseEvent& e) | |||
| if (dragger != 0) | |||
| { | |||
| MouseEvent e2 (e.getEventRelativeTo (overlay)); | |||
| dragger->drag (e2, e2.getPosition() - origin); | |||
| dragger->drag (e2, screenSpaceToObjectSpace (e2.getPosition())); | |||
| dragger = 0; | |||
| getUndoManager().beginNewTransaction(); | |||
| @@ -1056,3 +797,9 @@ const Point<float> EditorCanvasBase::OverlayItemComponent::pointToLocalSpace (co | |||
| + (canvas->getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point<int>()) | |||
| - getPosition()).toFloat(); | |||
| } | |||
| //============================================================================== | |||
| EditorCanvasBase::Scale::Scale() | |||
| : scale (1.0) | |||
| { | |||
| } | |||
| @@ -51,8 +51,6 @@ public: | |||
| void resized(); | |||
| const Rectangle<int> getContentArea() const; | |||
| void drawXAxis (Graphics& g, const Rectangle<int>& r); | |||
| void drawYAxis (Graphics& g, const Rectangle<int>& r); | |||
| //============================================================================== | |||
| void valueTreePropertyChanged (ValueTree&, const Identifier&) { triggerAsyncUpdate(); } | |||
| @@ -63,6 +61,17 @@ public: | |||
| void showSizeGuides(); | |||
| void hideSizeGuides(); | |||
| struct Scale | |||
| { | |||
| Scale(); | |||
| Point<int> origin; | |||
| double scale; | |||
| }; | |||
| const Scale& getScale() const throw() { return scale; } | |||
| void setScale (const Scale& newScale); | |||
| //============================================================================== | |||
| virtual UndoManager& getUndoManager() = 0; | |||
| virtual void documentChanged() = 0; | |||
| @@ -123,7 +132,6 @@ public: | |||
| Component* getComponentHolder() const { return componentHolder; } | |||
| EditorPanelBase* getPanel() const; | |||
| const Point<int>& getOrigin() const throw() { return origin; } | |||
| 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; | |||
| @@ -152,8 +160,7 @@ public: | |||
| protected: | |||
| //============================================================================== | |||
| const BorderSize border; | |||
| Point<int> origin; | |||
| double scaleFactor; | |||
| Scale scale; | |||
| ValueTree controlPointEditingTarget; | |||
| friend class OverlayItemComponent; | |||
| @@ -26,46 +26,54 @@ | |||
| #ifndef __JUCER_EDITORPANEL_H_8E192A99__ | |||
| #define __JUCER_EDITORPANEL_H_8E192A99__ | |||
| #include "../../utility/jucer_TickIterator.h" | |||
| #include "jucer_EditorCanvas.h" | |||
| //============================================================================== | |||
| class EditorPanelBase : public Component | |||
| { | |||
| public: | |||
| EditorPanelBase() | |||
| : infoPanel (0), tree (0), markersVisible (true), snappingEnabled (true) | |||
| : rulerX (true), rulerY (false), markersVisible (true), snappingEnabled (true), canvas (0) | |||
| { | |||
| addAndMakeVisible (toolbar = new Toolbar()); | |||
| toolbar->setStyle (Toolbar::textOnly); | |||
| addAndMakeVisible (viewport = new CanvasViewport()); | |||
| addChildComponent (tree = new TreeView()); | |||
| tree->setRootItemVisible (true); | |||
| tree->setMultiSelectEnabled (true); | |||
| tree->setDefaultOpenness (true); | |||
| tree->setColour (TreeView::backgroundColourId, Colour::greyLevel (0.92f)); | |||
| tree->setIndentSize (15); | |||
| setOpaque (true); | |||
| background = ImageCache::getFromMemory (BinaryData::brushed_aluminium_png, BinaryData::brushed_aluminium_pngSize); | |||
| addAndMakeVisible (&toolbar); | |||
| toolbar.setStyle (Toolbar::textOnly); | |||
| addAndMakeVisible (&viewport); | |||
| addAndMakeVisible (&rulerX); | |||
| addAndMakeVisible (&rulerY); | |||
| addChildComponent (&tree); | |||
| tree.setRootItemVisible (true); | |||
| tree.setMultiSelectEnabled (true); | |||
| tree.setDefaultOpenness (true); | |||
| tree.setColour (TreeView::backgroundColourId, Colour::greyLevel (0.92f)); | |||
| tree.setIndentSize (15); | |||
| } | |||
| ~EditorPanelBase() | |||
| { | |||
| jassert (infoPanel == 0); // remember to call shutdown() | |||
| deleteAllChildren(); | |||
| } | |||
| void initialise (Component* canvas, ToolbarItemFactory& toolbarFactory, TreeViewItem* treeRootItem) | |||
| void initialise (EditorCanvasBase* canvas_, ToolbarItemFactory& toolbarFactory, TreeViewItem* treeRootItem) | |||
| { | |||
| toolbar->addDefaultItems (toolbarFactory); | |||
| viewport->setViewedComponent (canvas); | |||
| canvas = canvas_; | |||
| toolbar.addDefaultItems (toolbarFactory); | |||
| viewport.setViewedComponent (canvas); | |||
| addAndMakeVisible (infoPanel = new InfoPanel (this)); | |||
| tree->setRootItem (treeRootItem); | |||
| tree.setRootItem (treeRootItem); | |||
| resized(); | |||
| } | |||
| void shutdown() | |||
| { | |||
| tree->deleteRootItem(); | |||
| deleteAndZero (infoPanel); | |||
| tree.deleteRootItem(); | |||
| infoPanel = 0; | |||
| } | |||
| //============================================================================== | |||
| @@ -79,11 +87,11 @@ public: | |||
| void showOrHideTree() | |||
| { | |||
| tree->setVisible (! tree->isVisible()); | |||
| tree.setVisible (! tree.isVisible()); | |||
| resized(); | |||
| } | |||
| bool isTreeVisible() const { return tree->isVisible(); } | |||
| bool isTreeVisible() const { return tree.isVisible(); } | |||
| void showOrHideMarkers() | |||
| { | |||
| @@ -105,27 +113,62 @@ public: | |||
| virtual SelectedItemSet<String>& getSelection() = 0; | |||
| virtual void getSelectedItemProperties (Array<PropertyComponent*>& newComps) = 0; | |||
| void paint (Graphics& g) | |||
| { | |||
| g.setTiledImageFill (background, 0, 0, 1.0f); | |||
| g.fillAll(); | |||
| } | |||
| void resized() | |||
| { | |||
| const int toolbarHeight = 22; | |||
| toolbar->setBounds (0, 0, getWidth(), toolbarHeight); | |||
| toolbar.setBounds (0, 0, getWidth(), toolbarHeight); | |||
| int contentL = 0, contentR = getWidth(); | |||
| if (infoPanel != 0 && infoPanel->isVisible()) | |||
| { | |||
| contentR -= 200; | |||
| infoPanel->setBounds (contentR, toolbar->getBottom(), getWidth() - contentR, getHeight() - toolbar->getBottom()); | |||
| infoPanel->setBounds (contentR, toolbar.getBottom(), getWidth() - contentR, getHeight() - toolbar.getBottom()); | |||
| } | |||
| if (tree->isVisible()) | |||
| if (tree.isVisible()) | |||
| { | |||
| contentL = 200; | |||
| tree->setBounds (0, toolbar->getBottom(), contentL, getHeight() - toolbar->getBottom()); | |||
| tree.setBounds (0, toolbar.getBottom(), contentL, getHeight() - toolbar.getBottom()); | |||
| } | |||
| viewport->setBounds (contentL, toolbar->getBottom(), contentR - contentL, getHeight() - toolbar->getBottom()); | |||
| const int rulerThickness = 16; | |||
| viewport.setBounds (contentL + rulerThickness, toolbar.getBottom() + rulerThickness, | |||
| contentR - contentL - rulerThickness, | |||
| getHeight() - toolbar.getBottom() - rulerThickness); | |||
| rulerX.setBounds (viewport.getX(), viewport.getY() - rulerThickness, viewport.getWidth(), rulerThickness); | |||
| rulerY.setBounds (viewport.getX() - rulerThickness, viewport.getY(), rulerThickness, viewport.getHeight()); | |||
| updateRulers(); | |||
| } | |||
| void updateRulers() | |||
| { | |||
| if (canvas != 0) | |||
| { | |||
| rulerX.update (canvas->getScale(), canvas->getComponentHolder()); | |||
| rulerY.update (canvas->getScale(), canvas->getComponentHolder()); | |||
| } | |||
| updateMarkers(); | |||
| } | |||
| void updateMarkers() | |||
| { | |||
| if (canvas != 0) | |||
| { | |||
| const int vw = viewport.getMaximumVisibleWidth(); | |||
| const int vh = viewport.getMaximumVisibleHeight(); | |||
| rulerX.updateMarkers (canvas->getMarkerList (true), canvas, vw, vh); | |||
| rulerY.updateMarkers (canvas->getMarkerList (false), canvas, vw, vh); | |||
| } | |||
| } | |||
| private: | |||
| @@ -175,12 +218,315 @@ private: | |||
| PropertyPanel* props; | |||
| }; | |||
| //============================================================================== | |||
| class RulerComponent : public Component | |||
| { | |||
| public: | |||
| RulerComponent (const bool isX_) | |||
| : isX (isX_), range (0.0, 100.0), canvas (0) | |||
| { | |||
| } | |||
| ~RulerComponent() | |||
| { | |||
| } | |||
| void update (const EditorCanvasBase::Scale& scale, Component* contentHolder) | |||
| { | |||
| const Point<int> origin (contentHolder->relativePositionToOtherComponent (this, scale.origin)); | |||
| const double start = isX ? origin.getX() : origin.getY(); | |||
| const Range<double> newRange (-start * scale.scale, | |||
| ((isX ? getWidth() : getHeight()) - start) * scale.scale); | |||
| if (range != newRange) | |||
| { | |||
| range = newRange; | |||
| repaint(); | |||
| } | |||
| } | |||
| void updateMarkers (MarkerListBase& markerList, EditorCanvasBase* canvas_, const int viewportWidth, const int viewportHeight) | |||
| { | |||
| canvas = canvas_; | |||
| const int num = markerList.size(); | |||
| Array<ValueTree> requiredMarkers; | |||
| requiredMarkers.ensureStorageAllocated (num); | |||
| int i; | |||
| for (i = 0; i < num; ++i) | |||
| requiredMarkers.add (markerList.getMarker (i)); | |||
| for (i = markers.size(); --i >= 0;) | |||
| { | |||
| MarkerComponent* marker = markers.getUnchecked (i); | |||
| const int index = requiredMarkers.indexOf (marker->marker); | |||
| if (index >= 0) | |||
| { | |||
| marker->updatePosition (viewportWidth, viewportHeight); | |||
| requiredMarkers.removeValue (marker->marker); | |||
| } | |||
| else | |||
| { | |||
| if (marker->isMouseButtonDown()) | |||
| marker->setBounds (-1, -1, 1, 1); | |||
| else | |||
| markers.remove (i); | |||
| } | |||
| } | |||
| for (i = requiredMarkers.size(); --i >= 0;) | |||
| { | |||
| MarkerComponent* marker = new MarkerComponent (*this, canvas, requiredMarkers.getReference(i), | |||
| isX, isX ? getHeight() : getWidth()); | |||
| markers.add (marker); | |||
| getParentComponent()->addAndMakeVisible (marker); | |||
| marker->updatePosition (viewportWidth, viewportHeight); | |||
| } | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.setFont (10.0f); | |||
| g.setColour (Colour::greyLevel (0.9f)); | |||
| TickIterator ticks (range.getStart(), range.getEnd(), range.getLength() / (isX ? getWidth() : getHeight()), | |||
| 10, isX ? 50 : 80); | |||
| float pos, tickLength; | |||
| String label; | |||
| while (ticks.getNextTick (pos, tickLength, label)) | |||
| { | |||
| if (pos > 0) | |||
| { | |||
| if (isX) | |||
| { | |||
| g.drawVerticalLine ((int) pos, getHeight() - tickLength * getHeight(), (float) getHeight()); | |||
| g.drawSingleLineText (label, (int) pos + 2, getHeight() - 6); | |||
| } | |||
| else | |||
| { | |||
| g.drawHorizontalLine ((int) pos, getWidth() - tickLength * getWidth(), (float) getWidth()); | |||
| g.drawTextAsPath (label, AffineTransform::rotation (float_Pi / -2.0f) | |||
| .translated (getWidth() - 6.0f, pos - 2.0f)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void mouseDoubleClick (const MouseEvent& e) | |||
| { | |||
| if (isX) | |||
| canvas->getMarkerList (true).createMarker (canvas->getMarkerList (true).getNonexistentMarkerName ("Marker"), | |||
| xToPosition (e.x)); | |||
| else | |||
| canvas->getMarkerList (false).createMarker (canvas->getMarkerList (false).getNonexistentMarkerName ("Marker"), | |||
| xToPosition (e.y)); | |||
| } | |||
| double xToPosition (const int x) const | |||
| { | |||
| return range.getStart() + x * range.getLength() / (isX ? getWidth() : getHeight()); | |||
| } | |||
| int positionToX (const double position) const | |||
| { | |||
| const float proportion = (float) ((position - range.getStart()) / range.getLength()); | |||
| return isX ? proportionOfWidth (proportion) : proportionOfHeight (proportion); | |||
| } | |||
| //============================================================================== | |||
| class MarkerComponent : public Component | |||
| { | |||
| public: | |||
| MarkerComponent (RulerComponent& ruler_, EditorCanvasBase* const canvas_, | |||
| const ValueTree& marker_, bool isX_, int headSize_) | |||
| : ruler (ruler_), canvas (canvas_), marker (marker_), isX (isX_), headSize (headSize_ - 2), | |||
| isDragging (false) | |||
| { | |||
| } | |||
| ~MarkerComponent() | |||
| { | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.setColour (Colours::lightblue.withAlpha (isMouseOverOrDragging() ? 0.9f : 0.5f)); | |||
| g.fillPath (path); | |||
| } | |||
| void updatePosition (const int viewportWidth, const int viewportHeight) | |||
| { | |||
| RelativeCoordinate coord (getMarkerList().getCoordinate (marker)); | |||
| const double pos = coord.resolve (&getMarkerList()); | |||
| if (! ruler.range.contains (pos)) | |||
| { | |||
| setVisible (false); | |||
| } | |||
| else | |||
| { | |||
| setVisible (true); | |||
| Point<int> anchorPoint; | |||
| if (isX) | |||
| anchorPoint.setXY (ruler.positionToX (pos), ruler.getHeight()); | |||
| else | |||
| anchorPoint.setXY (ruler.getWidth(), ruler.positionToX (pos)); | |||
| Component* const parent = getParentComponent(); | |||
| anchorPoint = ruler.relativePositionToOtherComponent (parent, anchorPoint); | |||
| const int width = 8; | |||
| if (isX) | |||
| setBounds (anchorPoint.getX() - width, anchorPoint.getY() - headSize, width * 2, viewportHeight + headSize); | |||
| else | |||
| setBounds (anchorPoint.getX() - headSize, anchorPoint.getY() - width, viewportWidth + headSize, width * 2); | |||
| } | |||
| labelText = "name: " + getMarkerList().getName (marker) + "\nposition: " + coord.toString(); | |||
| updateLabel(); | |||
| } | |||
| void updateLabel() | |||
| { | |||
| if (isMouseOverOrDragging() && isVisible() && (getWidth() > 1 || getHeight() > 1)) | |||
| label.update (getParentComponent(), labelText, Colours::darkgreen, | |||
| isX ? getBounds().getCentreX() : getX() + headSize, | |||
| isX ? getY() + headSize : getBounds().getCentreY(), true, true); | |||
| else | |||
| label.remove(); | |||
| } | |||
| bool hitTest (int x, int y) | |||
| { | |||
| return (isX ? y : x) < headSize; | |||
| } | |||
| void resized() | |||
| { | |||
| const float lineThickness = 1.0f; | |||
| path.clear(); | |||
| if (isX) | |||
| { | |||
| const float centre = getWidth() / 2 + 0.5f; | |||
| path.addLineSegment (Line<float> (centre, 2.0f, centre, getHeight() + 1.0f), lineThickness); | |||
| path.addTriangle (1.0f, 0.0f, centre * 2.0f - 1.0f, 0.0f, centre, headSize + 1.0f); | |||
| } | |||
| else | |||
| { | |||
| const float centre = getHeight() / 2 + 0.5f; | |||
| path.addLineSegment (Line<float> (2.0f, centre, getWidth() + 1.0f, centre), lineThickness); | |||
| path.addTriangle (0.0f, centre * 2.0f - 1.0f, 0.0f, 1.0f, headSize + 1.0f, centre); | |||
| } | |||
| updateLabel(); | |||
| } | |||
| void mouseDown (const MouseEvent& e) | |||
| { | |||
| mouseDownPos = e.getMouseDownPosition(); | |||
| toFront (false); | |||
| updateLabel(); | |||
| canvas->getSelection().selectOnly (getMarkerList().getId (marker)); | |||
| if (e.mods.isPopupMenu()) | |||
| { | |||
| isDragging = false; | |||
| } | |||
| else | |||
| { | |||
| isDragging = true; | |||
| canvas->getUndoManager().beginNewTransaction(); | |||
| } | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if (isDragging) | |||
| { | |||
| autoScrollForMouseEvent (e.getEventRelativeTo (canvas), isX, ! isX); | |||
| canvas->getUndoManager().undoCurrentTransactionOnly(); | |||
| Rectangle<int> axis; | |||
| if (isX) | |||
| axis.setBounds (0, 0, getParentWidth(), headSize); | |||
| else | |||
| axis.setBounds (0, 0, headSize, getParentHeight()); | |||
| if (axis.expanded (isX ? 500 : 30, isX ? 30 : 500).contains (e.x, e.y)) | |||
| { | |||
| RelativeCoordinate coord (getMarkerList().getCoordinate (marker)); | |||
| MouseEvent rulerEvent (e.getEventRelativeTo (&ruler)); | |||
| int rulerPos = isX ? (rulerEvent.x + getWidth() / 2 - mouseDownPos.getX()) | |||
| : (rulerEvent.y + getHeight() / 2 - mouseDownPos.getY()); | |||
| coord.moveToAbsolute (canvas->limitMarkerPosition (ruler.xToPosition (rulerPos)), &getMarkerList()); | |||
| getMarkerList().setCoordinate (marker, coord); | |||
| } | |||
| else | |||
| { | |||
| getMarkerList().deleteMarker (marker); | |||
| } | |||
| } | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| canvas->getUndoManager().beginNewTransaction(); | |||
| updateLabel(); | |||
| } | |||
| void mouseEnter (const MouseEvent& e) | |||
| { | |||
| updateLabel(); | |||
| repaint(); | |||
| } | |||
| void mouseExit (const MouseEvent& e) | |||
| { | |||
| updateLabel(); | |||
| repaint(); | |||
| } | |||
| MarkerListBase& getMarkerList() { return canvas->getMarkerList (isX); } | |||
| ValueTree marker; | |||
| const bool isX; | |||
| private: | |||
| RulerComponent& ruler; | |||
| EditorCanvasBase* canvas; | |||
| const int headSize; | |||
| Path path; | |||
| bool isDragging; | |||
| FloatingLabelComponent label; | |||
| String labelText; | |||
| Point<int> mouseDownPos; | |||
| }; | |||
| Range<double> range; | |||
| private: | |||
| const bool isX; | |||
| OwnedArray <MarkerComponent> markers; | |||
| EditorCanvasBase* canvas; | |||
| }; | |||
| //============================================================================== | |||
| class CanvasViewport : public Viewport | |||
| { | |||
| public: | |||
| CanvasViewport() | |||
| { | |||
| background = ImageCache::getFromMemory (BinaryData::brushed_aluminium_png, BinaryData::brushed_aluminium_pngSize); | |||
| setOpaque (true); | |||
| } | |||
| @@ -190,23 +536,36 @@ private: | |||
| void paint (Graphics& g) | |||
| { | |||
| g.setTiledImageFill (ImageCache::getFromMemory (BinaryData::brushed_aluminium_png, BinaryData::brushed_aluminium_pngSize), | |||
| 0, 0, 1.0f); | |||
| g.setTiledImageFill (background, 0, 0, 1.0f); | |||
| g.fillAll(); | |||
| } | |||
| void paintOverChildren (Graphics& g) | |||
| { | |||
| drawRecessedShadows (g, getWidth(), getHeight(), 14); | |||
| drawRecessedShadows (g, getMaximumVisibleWidth(), getMaximumVisibleHeight(), 14); | |||
| } | |||
| void visibleAreaChanged (int, int , int, int) | |||
| { | |||
| EditorPanelBase* p = dynamic_cast <EditorPanelBase*> (getParentComponent()); | |||
| if (p != 0) | |||
| p->updateRulers(); | |||
| } | |||
| private: | |||
| Image background; | |||
| }; | |||
| //============================================================================== | |||
| Toolbar* toolbar; | |||
| Viewport* viewport; | |||
| InfoPanel* infoPanel; | |||
| TreeView* tree; | |||
| Toolbar toolbar; | |||
| CanvasViewport viewport; | |||
| RulerComponent rulerX, rulerY; | |||
| ScopedPointer<InfoPanel> infoPanel; | |||
| TreeView tree; | |||
| EditorCanvasBase* canvas; | |||
| bool markersVisible, snappingEnabled; | |||
| Image background; | |||
| }; | |||
| @@ -77,14 +77,14 @@ const String createGUID (const String& seed) | |||
| } | |||
| //============================================================================== | |||
| void autoScrollForMouseEvent (const MouseEvent& e) | |||
| void autoScrollForMouseEvent (const MouseEvent& e, bool scrollX, bool scrollY) | |||
| { | |||
| Viewport* const viewport = e.eventComponent->findParentComponentOfClass ((Viewport*) 0); | |||
| if (viewport != 0) | |||
| { | |||
| const MouseEvent e2 (e.getEventRelativeTo (viewport)); | |||
| viewport->autoScroll (e2.x, e2.y, 8, 16); | |||
| viewport->autoScroll (scrollX ? e2.x : 20, scrollY ? e2.y : 20, 8, 16); | |||
| } | |||
| } | |||
| @@ -36,7 +36,7 @@ const String createGUID (const String& seed); // Turns a seed into a windows GUI | |||
| //============================================================================== | |||
| int indexOfLineStartingWith (const StringArray& lines, const String& text, int startIndex); | |||
| void autoScrollForMouseEvent (const MouseEvent& e); | |||
| void autoScrollForMouseEvent (const MouseEvent& e, bool scrollX = true, bool scrollY = true); | |||
| void drawComponentPlaceholder (Graphics& g, int w, int h, const String& text); | |||
| void drawRecessedShadows (Graphics& g, int w, int h, int shadowSize); | |||
| @@ -64,7 +64,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 52 | |||
| #define JUCE_BUILDNUMBER 16 | |||
| #define JUCE_BUILDNUMBER 17 | |||
| /** Current Juce version number. | |||
| @@ -2701,6 +2701,9 @@ protected: | |||
| @see setCurrentLogger | |||
| */ | |||
| virtual void logMessage (const String& message) = 0; | |||
| private: | |||
| static Logger* currentLogger; | |||
| }; | |||
| #endif // __JUCE_LOGGER_JUCEHEADER__ | |||
| @@ -5027,6 +5030,7 @@ private: | |||
| HeapBlock <char> data; | |||
| size_t size; | |||
| static const char* const encodingTable; | |||
| }; | |||
| #endif // __JUCE_MEMORYBLOCK_JUCEHEADER__ | |||
| @@ -14530,20 +14534,20 @@ public: | |||
| static const String getCpuVendor(); | |||
| /** Checks whether Intel MMX instructions are available. */ | |||
| static bool hasMMX(); | |||
| static bool hasMMX() throw() { return cpuFlags.hasMMX; } | |||
| /** Checks whether Intel SSE instructions are available. */ | |||
| static bool hasSSE(); | |||
| static bool hasSSE() throw() { return cpuFlags.hasSSE; } | |||
| /** Checks whether Intel SSE2 instructions are available. */ | |||
| static bool hasSSE2(); | |||
| static bool hasSSE2() throw() { return cpuFlags.hasSSE2; } | |||
| /** Checks whether AMD 3DNOW instructions are available. */ | |||
| static bool has3DNow(); | |||
| static bool has3DNow() throw() { return cpuFlags.has3DNow; } | |||
| /** Returns the number of CPUs. | |||
| */ | |||
| static int getNumCpus(); | |||
| static int getNumCpus() throw() { return cpuFlags.numCpus; } | |||
| /** Finds out how much RAM is in the machine. | |||
| @@ -14590,6 +14594,17 @@ public: | |||
| static void initialiseStats(); | |||
| private: | |||
| struct CPUFlags | |||
| { | |||
| int numCpus; | |||
| bool hasMMX : 1; | |||
| bool hasSSE : 1; | |||
| bool hasSSE2 : 1; | |||
| bool has3DNow : 1; | |||
| }; | |||
| static CPUFlags cpuFlags; | |||
| SystemStats(); | |||
| SystemStats (const SystemStats&); | |||
| SystemStats& operator= (const SystemStats&); | |||
| @@ -14973,6 +14988,9 @@ public: | |||
| protected: | |||
| BigInteger part1, part2; | |||
| private: | |||
| static const BigInteger findBestCommonDivisor (const BigInteger& p, const BigInteger& q); | |||
| }; | |||
| #endif // __JUCE_RSAKEY_JUCEHEADER__ | |||
| @@ -19419,6 +19437,7 @@ private: | |||
| const Time mouseDownTime; | |||
| const int numberOfClicks; | |||
| const bool wasMovedSinceMouseDown; | |||
| static int doubleClickTimeOutMs; | |||
| MouseEvent& operator= (const MouseEvent&); | |||
| }; | |||
| @@ -56235,6 +56254,9 @@ private: | |||
| int keyMappingOctave; | |||
| int octaveNumForMiddleC; | |||
| static const uint8 whiteNotes[]; | |||
| static const uint8 blackNotes[]; | |||
| void getKeyPos (int midiNoteNumber, int& x, int& w) const; | |||
| int xyToNote (const Point<int>& pos, float& mousePositionVelocity); | |||
| int remappedXYToNote (const Point<int>& pos, float& mousePositionVelocity) const; | |||
| @@ -33,7 +33,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 52 | |||
| #define JUCE_BUILDNUMBER 16 | |||
| #define JUCE_BUILDNUMBER 17 | |||
| /** Current Juce version number. | |||
| @@ -254,11 +254,11 @@ namespace LinuxErrorHandling | |||
| static int ioErrorHandler (Display* display) | |||
| { | |||
| DBG ("ERROR: connection to X server broken.. terminating."); | |||
| errorOccurred = true; | |||
| if (JUCEApplication::getInstance() != 0) | |||
| MessageManager::getInstance()->stopDispatchLoop(); | |||
| errorOccurred = true; | |||
| return 0; | |||
| } | |||
| @@ -463,8 +463,8 @@ void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) | |||
| for (int i = 0; i < s->headerLines.size(); ++i) | |||
| { | |||
| const String& headersEntry = s->headerLines[i]; | |||
| const String key (headersEntry.upToFirstOccurrenceOf ("; ", false, false)); | |||
| const String value (headersEntry.fromFirstOccurrenceOf ("; ", false, false)); | |||
| const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); | |||
| const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); | |||
| const String previousValue (headers [key]); | |||
| headers.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); | |||
| } | |||
| @@ -298,8 +298,8 @@ void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) | |||
| for (int i = 0; i < headersArray.size(); ++i) | |||
| { | |||
| const String& header = headersArray[i]; | |||
| const String key (header.upToFirstOccurrenceOf ("; ", false, false)); | |||
| const String value (header.fromFirstOccurrenceOf ("; ", false, false)); | |||
| const String key (header.upToFirstOccurrenceOf (": ", false, false)); | |||
| const String value (header.fromFirstOccurrenceOf (": ", false, false)); | |||
| const String previousValue (headers [key]); | |||
| headers.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); | |||