| @@ -68,6 +68,8 @@ public: | |||
| anotherInstanceStarted (commandLine); | |||
| theMainWindow->reloadLastProject(); | |||
| theMainWindow->getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | |||
| } | |||
| void shutdown() | |||
| @@ -146,6 +146,13 @@ public: | |||
| void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | |||
| { | |||
| DrawablePath::ValueTreeWrapper wrapper (item.getState()); | |||
| props.add (new FillTypePropertyComponent (item.getDocument().getUndoManager(), | |||
| "Fill", wrapper.getMainFillState())); | |||
| props.add (new FillTypePropertyComponent (item.getDocument().getUndoManager(), | |||
| "Stroke", wrapper.getStrokeFillState())); | |||
| } | |||
| void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | |||
| @@ -146,6 +146,10 @@ public: | |||
| return getDocument().getCoordsFor (state); | |||
| } | |||
| void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray<OverlayItemComponent>& existingComps) | |||
| { | |||
| } | |||
| SelectedItems& getSelection() | |||
| { | |||
| return editor.getSelection(); | |||
| @@ -207,6 +207,77 @@ public: | |||
| return RelativeRectangle(); | |||
| } | |||
| class ControlPointComponent : public OverlayItemComponent | |||
| { | |||
| public: | |||
| ControlPointComponent (DrawableEditorCanvas* canvas) | |||
| : OverlayItemComponent (canvas) | |||
| { | |||
| } | |||
| ~ControlPointComponent() | |||
| { | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.fillAll (Colours::pink); | |||
| } | |||
| void mouseDown (const MouseEvent& e) | |||
| { | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| } | |||
| void updatePosition (const RelativePoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder) | |||
| { | |||
| const Point<float> p (point.resolve (nameFinder)); | |||
| setBoundsInTargetSpace (Rectangle<int> (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 5, 5)); | |||
| } | |||
| }; | |||
| void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray<OverlayItemComponent>& comps) | |||
| { | |||
| if (drawable == 0) | |||
| { | |||
| comps.clear(); | |||
| return; | |||
| } | |||
| DrawableTypeInstance item (getDocument(), state); | |||
| Array<RelativePoint> points; | |||
| item.getAllControlPoints (points); | |||
| Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (state).getID()); | |||
| DrawableComposite* parentDrawable = d->getParent(); | |||
| comps.removeRange (points.size(), comps.size()); | |||
| BigInteger requiredIndexes; | |||
| requiredIndexes.setRange (0, points.size(), true); | |||
| for (int i = 0; i < points.size(); ++i) | |||
| { | |||
| ControlPointComponent* c = dynamic_cast <ControlPointComponent*> (comps[i]); | |||
| if (c == 0) | |||
| { | |||
| c = new ControlPointComponent (this); | |||
| comps.set (i, c); | |||
| parent->addAndMakeVisible (c); | |||
| } | |||
| c->updatePosition (points.getReference(i), parentDrawable); | |||
| } | |||
| } | |||
| SelectedItems& getSelection() | |||
| { | |||
| return editor.getSelection(); | |||
| @@ -114,12 +114,14 @@ public: | |||
| const Rectangle<int> bounds (canvas->getObjectPosition (objectState)); | |||
| setBoundsInTargetSpace (bounds.expanded (borderThickness, borderThickness)); | |||
| for (int i = sizeGuides.size(); --i >= 0;) | |||
| int i; | |||
| for (i = sizeGuides.size(); --i >= 0;) | |||
| { | |||
| sizeGuides.getUnchecked(i)->setVisible (isVisible()); | |||
| sizeGuides.getUnchecked(i)->updatePosition (bounds); | |||
| } | |||
| canvas->updateExtraComponentsForObject (objectState, getParentComponent(), extraEditorComps); | |||
| return true; | |||
| } | |||
| @@ -200,6 +202,7 @@ private: | |||
| const int borderThickness; | |||
| OwnedArray <SizeGuideComponent> sizeGuides; | |||
| bool isDragging; | |||
| OwnedArray <OverlayItemComponent> extraEditorComps; | |||
| const Rectangle<int> getCentreArea() const | |||
| { | |||
| @@ -399,6 +402,8 @@ public: | |||
| getSelection().removeChangeListener (this); | |||
| lasso = 0; | |||
| resizers.clear(); | |||
| markersX.clear(); | |||
| markersY.clear(); | |||
| deleteAllChildren(); | |||
| } | |||
| @@ -563,6 +568,21 @@ public: | |||
| } | |||
| } | |||
| void update() | |||
| { | |||
| updateResizeFrames(); | |||
| updateMarkers(); | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| EditorCanvasBase* canvas; | |||
| ScopedPointer <LassoComponent <SelectedItems::ItemType> > lasso; | |||
| bool mouseDownResult, isDraggingClickedComp; | |||
| SelectedItems::ItemType mouseDownCompUID; | |||
| OwnedArray <ResizeFrame> resizers; | |||
| OwnedArray <MarkerComponent> markersX, markersY; | |||
| void updateResizeFrames() | |||
| { | |||
| SelectedItems& selection = getSelection(); | |||
| @@ -610,49 +630,34 @@ public: | |||
| } | |||
| } | |||
| void update() | |||
| void updateMarkers (OwnedArray <MarkerComponent>& markers, const bool isX) | |||
| { | |||
| updateResizeFrames(); | |||
| updateMarkers(); | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| EditorCanvasBase* canvas; | |||
| ScopedPointer <LassoComponent <SelectedItems::ItemType> > lasso; | |||
| bool mouseDownResult, isDraggingClickedComp; | |||
| SelectedItems::ItemType mouseDownCompUID; | |||
| OwnedArray <ResizeFrame> resizers; | |||
| MarkerListBase& markerList = canvas->getMarkerList (isX); | |||
| const int num = markerList.size(); | |||
| void updateMarkers (bool isX) | |||
| { | |||
| Array<ValueTree> requiredMarkers; | |||
| requiredMarkers.ensureStorageAllocated (num); | |||
| MarkerListBase& markerList = canvas->getMarkerList (isX); | |||
| const int num = markerList.size(); | |||
| int i; | |||
| for (i = 0; i < num; ++i) | |||
| requiredMarkers.add (markerList.getMarker (i)); | |||
| for (i = getNumChildComponents(); --i >= 0;) | |||
| for (i = markers.size(); --i >= 0;) | |||
| { | |||
| MarkerComponent* marker = dynamic_cast <MarkerComponent*> (getChildComponent(i)); | |||
| MarkerComponent* marker = markers.getUnchecked (i); | |||
| const int index = requiredMarkers.indexOf (marker->marker); | |||
| if (marker != 0 && marker->isX == isX) | |||
| if (index >= 0) | |||
| { | |||
| if (requiredMarkers.contains (marker->marker)) | |||
| { | |||
| marker->setVisible (true); | |||
| marker->updatePosition(); | |||
| requiredMarkers.removeValue (marker->marker); | |||
| } | |||
| marker->updatePosition(); | |||
| requiredMarkers.removeValue (marker->marker); | |||
| } | |||
| else | |||
| { | |||
| if (marker->isMouseButtonDown()) | |||
| marker->setBounds (-1, -1, 1, 1); | |||
| else | |||
| { | |||
| if (marker->isMouseButtonDown()) | |||
| marker->setBounds (-1, -1, 1, 1); | |||
| else | |||
| delete marker; | |||
| } | |||
| markers.remove (i); | |||
| } | |||
| } | |||
| @@ -661,6 +666,7 @@ private: | |||
| MarkerComponent* marker = new MarkerComponent (canvas, requiredMarkers.getReference(i), | |||
| isX, isX ? canvas->border.getTop() | |||
| : canvas->border.getLeft()); | |||
| markers.add (marker); | |||
| addAndMakeVisible (marker); | |||
| marker->updatePosition(); | |||
| } | |||
| @@ -668,8 +674,8 @@ private: | |||
| void updateMarkers() | |||
| { | |||
| updateMarkers (true); | |||
| updateMarkers (false); | |||
| updateMarkers (markersX, true); | |||
| updateMarkers (markersY, false); | |||
| } | |||
| }; | |||
| @@ -64,27 +64,29 @@ public: | |||
| void hideSizeGuides(); | |||
| //============================================================================== | |||
| virtual UndoManager& getUndoManager() = 0; | |||
| virtual void documentChanged() = 0; | |||
| virtual Component* createComponentHolder() = 0; | |||
| virtual const Rectangle<int> getCanvasBounds() = 0; | |||
| virtual void setCanvasBounds (const Rectangle<int>& newBounds) = 0; | |||
| virtual bool canResizeCanvas() const = 0; | |||
| virtual MarkerListBase& getMarkerList (bool isX) = 0; | |||
| virtual double limitMarkerPosition (double pos) = 0; | |||
| virtual const SelectedItems::ItemType findObjectIdAt (const Point<int>& position) = 0; | |||
| virtual void showPopupMenu (bool isClickOnSelectedObject) = 0; | |||
| virtual void objectDoubleClicked (const MouseEvent& e, const ValueTree& state) = 0; | |||
| virtual const ValueTree getObjectState (const String& objectId) = 0; | |||
| virtual RelativeRectangle getObjectCoords (const ValueTree& state) = 0; | |||
| virtual const Rectangle<int> getObjectPosition (const ValueTree& state) = 0; | |||
| virtual bool hasSizeGuides() const = 0; | |||
| virtual RelativeRectangle getObjectCoords (const ValueTree& state) = 0; | |||
| virtual MarkerListBase& getMarkerList (bool isX) = 0; | |||
| virtual double limitMarkerPosition (double pos) = 0; | |||
| virtual SelectedItems& getSelection() = 0; | |||
| virtual UndoManager& getUndoManager() = 0; | |||
| virtual void deselectNonDraggableObjects() = 0; | |||
| virtual void findLassoItemsInArea (Array <SelectedItems::ItemType>& itemsFound, const Rectangle<int>& area) = 0; | |||
| virtual Component* createComponentHolder() = 0; | |||
| class DragOperation | |||
| { | |||
| @@ -126,6 +128,10 @@ public: | |||
| EditorCanvasBase* canvas; | |||
| }; | |||
| //============================================================================== | |||
| virtual void updateExtraComponentsForObject (const ValueTree& state, Component* parent, | |||
| OwnedArray<OverlayItemComponent>& existingComps) = 0; | |||
| protected: | |||
| //============================================================================== | |||
| const BorderSize border; | |||
| @@ -172,6 +172,11 @@ public: | |||
| roundToInt (line.end - line.start) + extraEndLength * 2, 1)); | |||
| } | |||
| bool updatePosition() | |||
| { | |||
| return true; | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.fillAll (alignmentMarkerColour); | |||
| @@ -37,15 +37,43 @@ public: | |||
| : fillState (fillState_), | |||
| undoManager (undoManager_) | |||
| { | |||
| addAndMakeVisible (&colourPicker); | |||
| colourButton.setButtonText ("Colour"); | |||
| colourButton.setConnectedEdges (TextButton::ConnectedOnRight); | |||
| gradientButton.setButtonText ("Gradient"); | |||
| gradientButton.setConnectedEdges (TextButton::ConnectedOnRight | TextButton::ConnectedOnLeft); | |||
| imageButton.setButtonText ("Image"); | |||
| imageButton.setConnectedEdges (TextButton::ConnectedOnLeft); | |||
| addAndMakeVisible (&colourButton); | |||
| addAndMakeVisible (&gradientButton); | |||
| addAndMakeVisible (&imageButton); | |||
| addChildComponent (&colourPicker); | |||
| colourPicker.setCurrentColour (Colours::green); | |||
| colourPicker.setName ("Colour"); | |||
| colourPicker.addChangeListener (this); | |||
| addChildComponent (&gradientPicker); | |||
| gradientPicker.addChangeListener (this); | |||
| fillState.addListener (this); | |||
| colourButton.setRadioGroupId (123); | |||
| gradientButton.setRadioGroupId (123); | |||
| imageButton.setRadioGroupId (123); | |||
| colourButton.addButtonListener (this); | |||
| gradientButton.addButtonListener (this); | |||
| imageButton.addButtonListener (this); | |||
| refresh(); | |||
| } | |||
| ~PopupFillSelector() | |||
| { | |||
| colourButton.removeButtonListener (this); | |||
| gradientButton.removeButtonListener (this); | |||
| imageButton.removeButtonListener (this); | |||
| } | |||
| static void showAt (Component* comp, const ValueTree& fill, UndoManager* undoManager) | |||
| @@ -59,32 +87,84 @@ public: | |||
| void resized() | |||
| { | |||
| colourPicker.setBounds (0, 0, getWidth(), getHeight()); | |||
| const int y = 2, w = 80, h = 22; | |||
| gradientButton.setBounds (getWidth() / 2 - w / 2, y, w, h); | |||
| colourButton.setBounds (gradientButton.getX() - w, y, w, h); | |||
| imageButton.setBounds (gradientButton.getRight(), y, w, h); | |||
| const Rectangle<int> content (2, y + h + 4, getWidth() - 4, getHeight() - (y + h + 6)); | |||
| colourPicker.setBounds (content); | |||
| gradientPicker.setBounds (content); | |||
| } | |||
| void buttonClicked (Button*) | |||
| void buttonClicked (Button* b) | |||
| { | |||
| if (b == &colourButton) | |||
| { | |||
| setFillType (colourPicker.getCurrentColour()); | |||
| } | |||
| else if (b == &gradientButton) | |||
| { | |||
| setFillType (gradientPicker.getGradient()); | |||
| } | |||
| else if (b == &imageButton) | |||
| { | |||
| setFillType (FillType (*StoredSettings::getInstance()->getFallbackImage(), | |||
| AffineTransform::identity)); | |||
| } | |||
| } | |||
| void changeListenerCallback (void* source) | |||
| const FillType readFillType (RelativePoint* gp1, RelativePoint* gp2) const | |||
| { | |||
| const FillType currentFill (Drawable::ValueTreeWrapperBase::readFillType (fillState)); | |||
| return Drawable::ValueTreeWrapperBase::readFillType (fillState, gp1, gp2, 0); | |||
| } | |||
| if (currentFill.isColour()) | |||
| void setFillType (const FillType& newFill) | |||
| { | |||
| RelativePoint gp1, gp2; | |||
| const FillType currentFill (readFillType (&gp1, &gp2)); | |||
| if (currentFill != newFill) | |||
| { | |||
| const FillType newFill (colourPicker.getCurrentColour()); | |||
| if (undoManager != 0) | |||
| undoManager->undoCurrentTransactionOnly(); | |||
| if (currentFill != newFill) | |||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, undoManager); | |||
| Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, undoManager); | |||
| refresh(); | |||
| } | |||
| } | |||
| void changeListenerCallback (void*) | |||
| { | |||
| const FillType currentFill (readFillType (0, 0)); | |||
| if (currentFill.isColour()) | |||
| setFillType (colourPicker.getCurrentColour()); | |||
| else if (currentFill.isGradient()) | |||
| setFillType (gradientPicker.getGradient()); | |||
| } | |||
| void refresh() | |||
| { | |||
| const FillType newFill (Drawable::ValueTreeWrapperBase::readFillType (fillState)); | |||
| const FillType newFill (readFillType (0, 0)); | |||
| colourPicker.setVisible (newFill.isColour()); | |||
| gradientPicker.setVisible (newFill.isGradient()); | |||
| if (newFill.isColour()) | |||
| { | |||
| colourButton.setToggleState (true, false); | |||
| colourPicker.setCurrentColour (newFill.colour); | |||
| } | |||
| else if (newFill.isGradient()) | |||
| { | |||
| gradientButton.setToggleState (true, false); | |||
| gradientPicker.setGradient (*newFill.gradient); | |||
| } | |||
| else | |||
| { | |||
| imageButton.setToggleState (true, false); | |||
| } | |||
| } | |||
| void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { refresh(); } | |||
| @@ -92,9 +172,215 @@ public: | |||
| void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {} | |||
| private: | |||
| //============================================================================== | |||
| class GradientDesigner : public Component, | |||
| public ChangeBroadcaster, | |||
| private ChangeListener | |||
| { | |||
| public: | |||
| GradientDesigner() | |||
| : gradient (Colours::red, 0.0f, 0.0f, Colours::blue, 200.0f, 200.0f, false), | |||
| selectedPoint (-1), | |||
| dragging (false), | |||
| draggingNewPoint (false), | |||
| draggingPos (0) | |||
| { | |||
| addChildComponent (&colourPicker); | |||
| colourPicker.addChangeListener (this); | |||
| } | |||
| ~GradientDesigner() | |||
| { | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.fillAll (getLookAndFeel().findColour (ColourSelector::backgroundColourId)); | |||
| g.fillCheckerBoard (previewArea.getX(), previewArea.getY(), | |||
| previewArea.getWidth(), previewArea.getHeight(), 10, 10, | |||
| Colour (0xffdddddd), Colour (0xffffffff)); | |||
| FillType f (gradient); | |||
| f.gradient->point1.setXY ((float) previewArea.getX(), (float) previewArea.getCentreY()); | |||
| f.gradient->point2.setXY ((float) previewArea.getRight(), (float) previewArea.getCentreY()); | |||
| g.setFillType (f); | |||
| g.fillRect (previewArea); | |||
| Path marker; | |||
| const float headSize = 4.5f; | |||
| marker.addLineSegment (0.0f, -2.0f, 0.0f, previewArea.getHeight() + 2.0f, 1.5f); | |||
| marker.addTriangle (0.0f, 1.0f, -headSize, -headSize, headSize, -headSize); | |||
| for (int i = 0; i < gradient.getNumColours(); ++i) | |||
| { | |||
| const double pos = gradient.getColourPosition (i); | |||
| const Colour col (gradient.getColour (i)); | |||
| const AffineTransform t (AffineTransform::translation (previewArea.getX() + 0.5f + (float) (previewArea.getWidth() * pos), | |||
| (float) previewArea.getY())); | |||
| g.setColour (Colours::black.withAlpha (0.8f)); | |||
| g.strokePath (marker, PathStrokeType (i == selectedPoint ? 2.0f : 1.5f), t); | |||
| g.setColour (i == selectedPoint ? Colours::lightblue : Colours::white); | |||
| g.fillPath (marker, t); | |||
| } | |||
| } | |||
| void resized() | |||
| { | |||
| previewArea.setBounds (7, 8, getWidth() - 14, 24); | |||
| colourPicker.setBounds (0, previewArea.getBottom() + 8, | |||
| getWidth(), getHeight() - previewArea.getBottom() - 8); | |||
| } | |||
| void mouseDown (const MouseEvent& e) | |||
| { | |||
| dragging = false; | |||
| draggingNewPoint = false; | |||
| int point = getPointAt (e.x); | |||
| if (point >= 0) | |||
| setSelectedPoint (point); | |||
| } | |||
| void mouseDrag (const MouseEvent& e) | |||
| { | |||
| if ((! dragging) && ! e.mouseWasClicked()) | |||
| { | |||
| preDragGradient = gradient; | |||
| const int mouseDownPoint = getPointAt (e.getMouseDownX()); | |||
| if (mouseDownPoint >= 0) | |||
| { | |||
| if (mouseDownPoint > 0 && mouseDownPoint < gradient.getNumColours() - 1) | |||
| { | |||
| dragging = true; | |||
| draggingNewPoint = false; | |||
| draggingColour = gradient.getColour (mouseDownPoint); | |||
| preDragGradient.removeColour (mouseDownPoint); | |||
| selectedPoint = -1; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| dragging = true; | |||
| draggingNewPoint = true; | |||
| selectedPoint = -1; | |||
| } | |||
| } | |||
| if (dragging) | |||
| { | |||
| draggingPos = jlimit (0.001, 0.999, (e.x - previewArea.getX()) / (double) previewArea.getWidth()); | |||
| gradient = preDragGradient; | |||
| if (previewArea.expanded (6, 6).contains (e.x, e.y)) | |||
| { | |||
| if (draggingNewPoint) | |||
| draggingColour = preDragGradient.getColourAtPosition (draggingPos); | |||
| selectedPoint = gradient.addColour (draggingPos, draggingColour); | |||
| updatePicker(); | |||
| } | |||
| else | |||
| { | |||
| selectedPoint = -1; | |||
| } | |||
| sendChangeMessage (this); | |||
| repaint (previewArea.expanded (30, 30)); | |||
| } | |||
| } | |||
| void mouseUp (const MouseEvent& e) | |||
| { | |||
| dragging = false; | |||
| } | |||
| const ColourGradient& getGradient() const throw() { return gradient; } | |||
| void setGradient (const ColourGradient& newGradient) | |||
| { | |||
| if (newGradient != gradient) | |||
| { | |||
| gradient = newGradient; | |||
| if (selectedPoint < 0) | |||
| selectedPoint = 0; | |||
| updatePicker(); | |||
| sendChangeMessage (this); | |||
| repaint(); | |||
| } | |||
| } | |||
| void setSelectedPoint (int newIndex) | |||
| { | |||
| if (selectedPoint != newIndex) | |||
| { | |||
| selectedPoint = newIndex; | |||
| updatePicker(); | |||
| repaint(); | |||
| } | |||
| } | |||
| void changeListenerCallback (void*) | |||
| { | |||
| if (selectedPoint >= 0 && (! dragging) && gradient.getColour (selectedPoint) != colourPicker.getCurrentColour()) | |||
| { | |||
| gradient.setColour (selectedPoint, colourPicker.getCurrentColour()); | |||
| repaint (previewArea); | |||
| sendChangeMessage (this); | |||
| } | |||
| } | |||
| private: | |||
| StoredSettings::ColourSelectorWithSwatches colourPicker; | |||
| ColourGradient gradient; | |||
| int selectedPoint; | |||
| bool dragging, draggingNewPoint; | |||
| double draggingPos; | |||
| Colour draggingColour; | |||
| ColourGradient preDragGradient; | |||
| Rectangle<int> previewArea; | |||
| void updatePicker() | |||
| { | |||
| colourPicker.setVisible (selectedPoint >= 0); | |||
| if (selectedPoint >= 0) | |||
| colourPicker.setCurrentColour (gradient.getColour (selectedPoint)); | |||
| } | |||
| int getPointAt (const int x) const | |||
| { | |||
| int best = -1; | |||
| double bestDiff = 6; | |||
| for (int i = gradient.getNumColours(); --i >= 0;) | |||
| { | |||
| const double pos = previewArea.getX() + previewArea.getWidth() * gradient.getColourPosition (i); | |||
| const double diff = std::abs (pos - x); | |||
| if (diff < bestDiff) | |||
| { | |||
| bestDiff = diff; | |||
| best = i; | |||
| } | |||
| } | |||
| return best; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| StoredSettings::ColourSelectorWithSwatches colourPicker; | |||
| GradientDesigner gradientPicker; | |||
| ValueTree fillState; | |||
| UndoManager* undoManager; | |||
| TextButton colourButton, gradientButton, imageButton; | |||
| }; | |||
| @@ -149,7 +435,7 @@ public: | |||
| void refresh() | |||
| { | |||
| const FillType newFill (Drawable::ValueTreeWrapperBase::readFillType (fillState)); | |||
| const FillType newFill (Drawable::ValueTreeWrapperBase::readFillType (fillState, 0, 0, 0)); | |||
| if (newFill != fillType) | |||
| { | |||
| @@ -185,6 +471,7 @@ public: | |||
| : PropertyComponent (name), | |||
| editor (fill, undoManager) | |||
| { | |||
| jassert (fill.isValid()); | |||
| addAndMakeVisible (&editor); | |||
| } | |||
| @@ -134,3 +134,11 @@ const StringArray& StoredSettings::getFontNames() | |||
| return fontNames; | |||
| } | |||
| Image* StoredSettings::getFallbackImage() | |||
| { | |||
| if (fallbackImage == 0) | |||
| fallbackImage = ImageFileFormat::loadFrom (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize); | |||
| return fallbackImage; | |||
| } | |||
| @@ -53,7 +53,6 @@ public: | |||
| const File getLastKnownJuceFolder() const; | |||
| void setLastKnownJuceFolder (const File& file); | |||
| const StringArray& getFontNames(); | |||
| //============================================================================== | |||
| @@ -69,12 +68,17 @@ public: | |||
| void setSwatchColour (int index, const Colour& newColour) const { StoredSettings::getInstance()->swatchColours.set (index, newColour); } | |||
| }; | |||
| Image* getFallbackImage(); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| ScopedPointer<PropertiesFile> props; | |||
| StringArray fontNames; | |||
| ScopedPointer<Image> fallbackImage; | |||
| }; | |||
| @@ -36166,7 +36166,12 @@ void AudioProcessorPlayer::audioDeviceIOCallback (const float** inputChannelData | |||
| const ScopedLock sl (lock); | |||
| if (processor != 0) | |||
| processor->processBlock (buffer, incomingMidi); | |||
| { | |||
| const ScopedLock sl (processor->getCallbackLock()); | |||
| if (! processor->isSuspended()) | |||
| processor->processBlock (buffer, incomingMidi); | |||
| } | |||
| } | |||
| void AudioProcessorPlayer::audioDeviceAboutToStart (AudioIODevice* device) | |||
| @@ -78867,19 +78872,15 @@ ColourGradient::ColourGradient() throw() | |||
| #endif | |||
| } | |||
| ColourGradient::ColourGradient (const Colour& colour1, | |||
| const float x1_, | |||
| const float y1_, | |||
| const Colour& colour2, | |||
| const float x2_, | |||
| const float y2_, | |||
| ColourGradient::ColourGradient (const Colour& colour1, const float x1_, const float y1_, | |||
| const Colour& colour2, const float x2_, const float y2_, | |||
| const bool isRadial_) | |||
| : point1 (x1_, y1_), | |||
| point2 (x2_, y2_), | |||
| isRadial (isRadial_) | |||
| { | |||
| colours.add (ColourPoint (0, colour1)); | |||
| colours.add (ColourPoint (1 << 16, colour2)); | |||
| colours.add (ColourPoint (0.0, colour1)); | |||
| colours.add (ColourPoint (1.0, colour2)); | |||
| } | |||
| ColourGradient::~ColourGradient() | |||
| @@ -78903,13 +78904,12 @@ void ColourGradient::clearColours() | |||
| colours.clear(); | |||
| } | |||
| void ColourGradient::addColour (const double proportionAlongGradient, | |||
| const Colour& colour) | |||
| int ColourGradient::addColour (const double proportionAlongGradient, const Colour& colour) | |||
| { | |||
| // must be within the two end-points | |||
| jassert (proportionAlongGradient >= 0 && proportionAlongGradient <= 1.0); | |||
| const uint32 pos = jlimit (0, 65535, roundToInt (proportionAlongGradient * 65536.0)); | |||
| const double pos = jlimit (0.0, 1.0, proportionAlongGradient); | |||
| int i; | |||
| for (i = 0; i < colours.size(); ++i) | |||
| @@ -78917,6 +78917,13 @@ void ColourGradient::addColour (const double proportionAlongGradient, | |||
| break; | |||
| colours.insert (i, ColourPoint (pos, colour)); | |||
| return i; | |||
| } | |||
| void ColourGradient::removeColour (int index) | |||
| { | |||
| jassert (index > 0 && index < colours.size() - 1); | |||
| colours.remove (index); | |||
| } | |||
| void ColourGradient::multiplyOpacity (const float multiplier) throw() | |||
| @@ -78936,7 +78943,7 @@ int ColourGradient::getNumColours() const throw() | |||
| double ColourGradient::getColourPosition (const int index) const throw() | |||
| { | |||
| if (((unsigned int) index) < (unsigned int) colours.size()) | |||
| return jlimit (0.0, 1.0, colours.getReference (index).position / 65535.0); | |||
| return colours.getReference (index).position; | |||
| return 0; | |||
| } | |||
| @@ -78949,26 +78956,31 @@ const Colour ColourGradient::getColour (const int index) const throw() | |||
| return Colour(); | |||
| } | |||
| const Colour ColourGradient::getColourAtPosition (const float position) const throw() | |||
| void ColourGradient::setColour (int index, const Colour& newColour) throw() | |||
| { | |||
| jassert (colours.getReference(0).position == 0); // the first colour specified has to go at position 0 | |||
| if (((unsigned int) index) < (unsigned int) colours.size()) | |||
| colours.getReference (index).colour = newColour; | |||
| } | |||
| const int integerPos = jlimit (0, 65535, roundToInt (position * 65536.0f)); | |||
| const Colour ColourGradient::getColourAtPosition (const double position) const throw() | |||
| { | |||
| jassert (colours.getReference(0).position == 0); // the first colour specified has to go at position 0 | |||
| if (integerPos <= 0 || colours.size() <= 1) | |||
| return getColour (0); | |||
| if (position <= 0 || colours.size() <= 1) | |||
| return colours.getReference(0).colour; | |||
| int i = colours.size() - 1; | |||
| while (integerPos < (int) colours.getReference(i).position) | |||
| while (position < colours.getReference(i).position) | |||
| --i; | |||
| const ColourPoint& p1 = colours.getReference (i); | |||
| if (i >= colours.size() - 1) | |||
| return colours.getReference(i).colour; | |||
| return p1.colour; | |||
| const ColourPoint& p1 = colours.getReference (i); | |||
| const ColourPoint& p2 = colours.getReference (i + 1); | |||
| return p1.colour.interpolatedWith (p2.colour, (integerPos - p1.position) / (float) (p2.position - p1.position)); | |||
| return p1.colour.interpolatedWith (p2.colour, (float) ((position - p1.position) / (p2.position - p1.position))); | |||
| } | |||
| int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock <PixelARGB>& lookupTable) const | |||
| @@ -78994,7 +79006,7 @@ int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlo | |||
| for (int j = 1; j < colours.size(); ++j) | |||
| { | |||
| const ColourPoint& p = colours.getReference (j); | |||
| const int numToDo = ((p.position * (numEntries - 1)) >> 16) - index; | |||
| const int numToDo = roundToInt (p.position * (numEntries - 1)) - index; | |||
| const PixelARGB pix2 (p.colour.getPixelARGB()); | |||
| for (int i = 0; i < numToDo; ++i) | |||
| @@ -83854,10 +83866,8 @@ Drawable* Drawable::createFromValueTree (const ValueTree& tree, ImageProvider* i | |||
| const Identifier Drawable::ValueTreeWrapperBase::idProperty ("id"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::type ("type"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::x1 ("x1"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::x2 ("x2"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::y1 ("y1"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::y2 ("y2"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::gradientPoint1 ("point1"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::gradientPoint2 ("point2"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::colour ("colour"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::radial ("radial"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::colours ("colours"); | |||
| @@ -83884,7 +83894,8 @@ void Drawable::ValueTreeWrapperBase::setID (const String& newID, UndoManager* un | |||
| state.setProperty (idProperty, newID, undoManager); | |||
| } | |||
| const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v) | |||
| const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v, RelativePoint* gp1, RelativePoint* gp2, | |||
| RelativeCoordinate::NamedCoordinateFinder* nameFinder) | |||
| { | |||
| const String newType (v[type].toString()); | |||
| @@ -83896,9 +83907,17 @@ const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v) | |||
| } | |||
| else if (newType == "gradient") | |||
| { | |||
| RelativePoint p1 (v [gradientPoint1]), p2 (v [gradientPoint2]); | |||
| ColourGradient g; | |||
| g.point1.setXY (v[x1], v[y1]); | |||
| g.point2.setXY (v[x2], v[y2]); | |||
| if (gp1 != 0) | |||
| *gp1 = p1; | |||
| if (gp2 != 0) | |||
| *gp2 = p2; | |||
| g.point1 = p1.resolve (nameFinder); | |||
| g.point2 = p2.resolve (nameFinder); | |||
| g.isRadial = v[radial]; | |||
| StringArray colourSteps; | |||
| @@ -83919,26 +83938,24 @@ const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v) | |||
| return FillType(); | |||
| } | |||
| void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType& fillType, UndoManager* const undoManager) | |||
| void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType& fillType, | |||
| const RelativePoint* gp1, const RelativePoint* gp2, | |||
| UndoManager* const undoManager) | |||
| { | |||
| if (fillType.isColour()) | |||
| { | |||
| v.setProperty (type, "solid", undoManager); | |||
| v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager); | |||
| v.removeProperty (x1, undoManager); | |||
| v.removeProperty (x2, undoManager); | |||
| v.removeProperty (y1, undoManager); | |||
| v.removeProperty (y2, undoManager); | |||
| v.removeProperty (gradientPoint1, undoManager); | |||
| v.removeProperty (gradientPoint2, undoManager); | |||
| v.removeProperty (radial, undoManager); | |||
| v.removeProperty (colours, undoManager); | |||
| } | |||
| else if (fillType.isGradient()) | |||
| { | |||
| v.setProperty (type, "gradient", undoManager); | |||
| v.setProperty (x1, fillType.gradient->point1.getX(), undoManager); | |||
| v.setProperty (y1, fillType.gradient->point1.getY(), undoManager); | |||
| v.setProperty (x2, fillType.gradient->point2.getX(), undoManager); | |||
| v.setProperty (y2, fillType.gradient->point2.getY(), undoManager); | |||
| v.setProperty (gradientPoint1, gp1 != 0 ? gp1->toString() : fillType.gradient->point1.toString(), undoManager); | |||
| v.setProperty (gradientPoint2, gp2 != 0 ? gp2->toString() : fillType.gradient->point2.toString(), undoManager); | |||
| v.setProperty (radial, fillType.gradient->isRadial, undoManager); | |||
| String s; | |||
| @@ -83955,10 +83972,8 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||
| jassertfalse; //xxx todo | |||
| v.removeProperty (x1, undoManager); | |||
| v.removeProperty (x2, undoManager); | |||
| v.removeProperty (y1, undoManager); | |||
| v.removeProperty (y2, undoManager); | |||
| v.removeProperty (gradientPoint1, undoManager); | |||
| v.removeProperty (gradientPoint2, undoManager); | |||
| v.removeProperty (radial, undoManager); | |||
| v.removeProperty (colours, undoManager); | |||
| v.removeProperty (colour, undoManager); | |||
| @@ -83969,7 +83984,9 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||
| } | |||
| } | |||
| void Drawable::ValueTreeWrapperBase::replaceFillType (const Identifier& tag, const FillType& fillType, UndoManager* const undoManager) | |||
| void Drawable::ValueTreeWrapperBase::replaceFillType (const Identifier& tag, const FillType& fillType, | |||
| const RelativePoint* gp1, const RelativePoint* gp2, | |||
| UndoManager* const undoManager) | |||
| { | |||
| ValueTree v (state.getChildWithName (tag)); | |||
| @@ -83979,7 +83996,7 @@ void Drawable::ValueTreeWrapperBase::replaceFillType (const Identifier& tag, con | |||
| v = state.getChildWithName (tag); | |||
| } | |||
| writeFillType (v, fillType, undoManager); | |||
| writeFillType (v, fillType, gp1, gp2, undoManager); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -85114,24 +85131,46 @@ DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | |||
| jassert (state.hasType (valueTreeType)); | |||
| } | |||
| const FillType DrawablePath::ValueTreeWrapper::getMainFill() const | |||
| ValueTree DrawablePath::ValueTreeWrapper::getMainFillState() | |||
| { | |||
| ValueTree v (state.getChildWithName (fill)); | |||
| if (v.isValid()) | |||
| return v; | |||
| setMainFill (Colours::black, 0, 0, 0); | |||
| return getMainFillState(); | |||
| } | |||
| ValueTree DrawablePath::ValueTreeWrapper::getStrokeFillState() | |||
| { | |||
| ValueTree v (state.getChildWithName (stroke)); | |||
| if (v.isValid()) | |||
| return v; | |||
| setStrokeFill (Colours::black, 0, 0, 0); | |||
| return getStrokeFillState(); | |||
| } | |||
| const FillType DrawablePath::ValueTreeWrapper::getMainFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | |||
| { | |||
| return readFillType (state.getChildWithName (fill)); | |||
| return readFillType (state.getChildWithName (fill), 0, 0, nameFinder); | |||
| } | |||
| void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, UndoManager* undoManager) | |||
| void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const RelativePoint* gp1, | |||
| const RelativePoint* gp2, UndoManager* undoManager) | |||
| { | |||
| replaceFillType (fill, newFill, undoManager); | |||
| replaceFillType (fill, newFill, gp1, gp2, undoManager); | |||
| } | |||
| const FillType DrawablePath::ValueTreeWrapper::getStrokeFill() const | |||
| const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | |||
| { | |||
| return readFillType (state.getChildWithName (stroke)); | |||
| return readFillType (state.getChildWithName (stroke), 0, 0, nameFinder); | |||
| } | |||
| void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, UndoManager* undoManager) | |||
| void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, | |||
| const RelativePoint* gp2, UndoManager* undoManager) | |||
| { | |||
| replaceFillType (stroke, newFill, undoManager); | |||
| replaceFillType (stroke, newFill, gp1, gp2, undoManager); | |||
| } | |||
| const PathStrokeType DrawablePath::ValueTreeWrapper::getStrokeType() const | |||
| @@ -85175,7 +85214,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree | |||
| setName (v.getID()); | |||
| bool needsRedraw = false; | |||
| const FillType newFill (v.getMainFill()); | |||
| const FillType newFill (v.getMainFill (parent)); | |||
| if (mainFill != newFill) | |||
| { | |||
| @@ -85183,7 +85222,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree | |||
| mainFill = newFill; | |||
| } | |||
| const FillType newStrokeFill (v.getStrokeFill()); | |||
| const FillType newStrokeFill (v.getStrokeFill (parent)); | |||
| if (strokeFill != newStrokeFill) | |||
| { | |||
| @@ -85224,8 +85263,8 @@ const ValueTree DrawablePath::createValueTree (ImageProvider*) const | |||
| ValueTreeWrapper v (tree); | |||
| v.setID (getName(), 0); | |||
| v.setMainFill (mainFill, 0); | |||
| v.setStrokeFill (strokeFill, 0); | |||
| v.setMainFill (mainFill, 0, 0, 0); | |||
| v.setStrokeFill (strokeFill, 0, 0, 0); | |||
| v.setStrokeType (strokeType, 0); | |||
| if (relativePath != 0) | |||
| @@ -253051,8 +253090,8 @@ public: | |||
| { | |||
| //DBG (responseHeader); | |||
| StringArray lines; | |||
| lines.addLines (responseHeader); | |||
| headerLines.clear(); | |||
| headerLines.addLines (responseHeader); | |||
| const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||
| .substring (0, 3).getIntValue(); | |||
| @@ -253060,7 +253099,7 @@ public: | |||
| //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | |||
| //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | |||
| String location (findHeaderItem (lines, "Location:")); | |||
| String location (findHeaderItem (headerLines, "Location:")); | |||
| if (statusCode >= 300 && statusCode < 400 | |||
| && location.isNotEmpty()) | |||
| @@ -253101,6 +253140,7 @@ public: | |||
| } | |||
| int readPosition; | |||
| StringArray headerLines; | |||
| juce_UseDebuggingNewOperator | |||
| @@ -253250,13 +253290,11 @@ void* juce_openInternetFile (const String& url, | |||
| void* callbackContext, | |||
| int timeOutMs) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream(); | |||
| ScopedPointer<JUCE_HTTPSocketStream> s (new JUCE_HTTPSocketStream()); | |||
| if (s->open (url, headers, postData, isPost, | |||
| callback, callbackContext, timeOutMs)) | |||
| return s; | |||
| if (s->open (url, headers, postData, isPost, callback, callbackContext, timeOutMs)) | |||
| return s.release(); | |||
| delete s; | |||
| return 0; | |||
| } | |||
| @@ -253267,46 +253305,46 @@ void juce_closeInternetFile (void* handle) | |||
| int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||
| if (s != 0) | |||
| return s->read (buffer, bytesToRead); | |||
| JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| return 0; | |||
| return s != 0 ? s->read (buffer, bytesToRead) : 0; | |||
| } | |||
| int64 juce_getInternetFileContentLength (void* handle) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||
| JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| if (s != 0) | |||
| { | |||
| //xxx todo | |||
| jassertfalse; | |||
| jassertfalse | |||
| } | |||
| return -1; | |||
| } | |||
| void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) | |||
| bool juce_getInternetFileHeaders (void* handle, StringPairArray& headers) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||
| JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| if (s != 0) | |||
| { | |||
| // xxx todo | |||
| jassertfalse; | |||
| 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 previousValue (headers [key]); | |||
| headers.set (key, previousValue.isEmpty() ? value : (previousValue + ";" + value)); | |||
| } | |||
| } | |||
| } | |||
| int juce_seekInInternetFile (void* handle, int newPosition) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||
| JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| if (s != 0) | |||
| return s->readPosition; | |||
| return 0; | |||
| return s != 0 ? s->readPosition : 0; | |||
| } | |||
| #endif | |||
| @@ -64,7 +64,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 52 | |||
| #define JUCE_BUILDNUMBER 6 | |||
| #define JUCE_BUILDNUMBER 7 | |||
| /** Current Juce version number. | |||
| @@ -23179,9 +23179,13 @@ public: | |||
| of the distance along the line between the two points | |||
| at which the colour should occur. | |||
| @param colour the colour that should be used at this point | |||
| @returns the index at which the new point was added | |||
| */ | |||
| void addColour (double proportionAlongGradient, | |||
| const Colour& colour); | |||
| int addColour (double proportionAlongGradient, | |||
| const Colour& colour); | |||
| /** Removes one of the colours from the gradient. */ | |||
| void removeColour (int index); | |||
| /** Multiplies the alpha value of all the colours by the given scale factor */ | |||
| void multiplyOpacity (float multiplier) throw(); | |||
| @@ -23196,15 +23200,19 @@ public: | |||
| double getColourPosition (int index) const throw(); | |||
| /** Returns the colour that was added with a given index. | |||
| The index is from 0 to getNumColours() - 1. The return value will be between 0.0 and 1.0 | |||
| The index is from 0 to getNumColours() - 1. | |||
| */ | |||
| const Colour getColour (int index) const throw(); | |||
| /** Changes the colour at a given index. | |||
| The index is from 0 to getNumColours() - 1. | |||
| */ | |||
| void setColour (int index, const Colour& newColour) throw(); | |||
| /** Returns the an interpolated colour at any position along the gradient. | |||
| @param position the position along the gradient, between 0 and 1 | |||
| */ | |||
| const Colour getColourAtPosition (float position) const throw(); | |||
| const Colour getColourAtPosition (double position) const throw(); | |||
| /** Creates a set of interpolated premultiplied ARGB values. | |||
| This will resize the HeapBlock, fill it with the colours, and will return the number of | |||
| @@ -23237,14 +23245,14 @@ private: | |||
| { | |||
| ColourPoint() throw() {} | |||
| ColourPoint (uint32 position_, const Colour& colour_) throw() | |||
| ColourPoint (const double position_, const Colour& colour_) throw() | |||
| : position (position_), colour (colour_) | |||
| {} | |||
| bool operator== (const ColourPoint& other) const throw() { return position == other.position && colour == other.colour; } | |||
| bool operator!= (const ColourPoint& other) const throw() { return position != other.position || colour != other.colour; } | |||
| uint32 position; | |||
| double position; | |||
| Colour colour; | |||
| }; | |||
| @@ -42586,14 +42594,20 @@ public: | |||
| void setID (const String& newID, UndoManager* undoManager); | |||
| static const Identifier idProperty; | |||
| static const FillType readFillType (const ValueTree& v); | |||
| static void writeFillType (ValueTree& v, const FillType& fillType, UndoManager* undoManager); | |||
| static const FillType readFillType (const ValueTree& v, RelativePoint* gradientPoint1, RelativePoint* gradientPoint2, | |||
| RelativeCoordinate::NamedCoordinateFinder* nameFinder); | |||
| static void writeFillType (ValueTree& v, const FillType& fillType, | |||
| const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | |||
| UndoManager* undoManager); | |||
| protected: | |||
| ValueTree state; | |||
| static const Identifier type, x1, x2, y1, y2, colour, radial, colours; | |||
| static const Identifier type, gradientPoint1, gradientPoint2, colour, radial, colours; | |||
| void replaceFillType (const Identifier& tag, const FillType& fillType, UndoManager* undoManager); | |||
| void replaceFillType (const Identifier& tag, const FillType& fillType, | |||
| const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | |||
| UndoManager* undoManager); | |||
| }; | |||
| juce_UseDebuggingNewOperator | |||
| @@ -58593,11 +58607,15 @@ public: | |||
| public: | |||
| ValueTreeWrapper (const ValueTree& state); | |||
| const FillType getMainFill() const; | |||
| void setMainFill (const FillType& newFill, UndoManager* undoManager); | |||
| const FillType getMainFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const; | |||
| ValueTree getMainFillState(); | |||
| void setMainFill (const FillType& newFill, const RelativePoint* gradientPoint1, | |||
| const RelativePoint* gradientPoint2, UndoManager* undoManager); | |||
| const FillType getStrokeFill() const; | |||
| void setStrokeFill (const FillType& newFill, UndoManager* undoManager); | |||
| const FillType getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const; | |||
| ValueTree getStrokeFillState(); | |||
| void setStrokeFill (const FillType& newFill, const RelativePoint* gradientPoint1, | |||
| const RelativePoint* gradientPoint2, UndoManager* undoManager); | |||
| const PathStrokeType getStrokeType() const; | |||
| void setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager); | |||
| @@ -133,7 +133,12 @@ void AudioProcessorPlayer::audioDeviceIOCallback (const float** inputChannelData | |||
| const ScopedLock sl (lock); | |||
| if (processor != 0) | |||
| processor->processBlock (buffer, incomingMidi); | |||
| { | |||
| const ScopedLock sl (processor->getCallbackLock()); | |||
| if (! processor->isSuspended()) | |||
| processor->processBlock (buffer, incomingMidi); | |||
| } | |||
| } | |||
| void AudioProcessorPlayer::audioDeviceAboutToStart (AudioIODevice* device) | |||
| @@ -33,7 +33,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 52 | |||
| #define JUCE_BUILDNUMBER 6 | |||
| #define JUCE_BUILDNUMBER 7 | |||
| /** Current Juce version number. | |||
| @@ -38,19 +38,15 @@ ColourGradient::ColourGradient() throw() | |||
| #endif | |||
| } | |||
| ColourGradient::ColourGradient (const Colour& colour1, | |||
| const float x1_, | |||
| const float y1_, | |||
| const Colour& colour2, | |||
| const float x2_, | |||
| const float y2_, | |||
| ColourGradient::ColourGradient (const Colour& colour1, const float x1_, const float y1_, | |||
| const Colour& colour2, const float x2_, const float y2_, | |||
| const bool isRadial_) | |||
| : point1 (x1_, y1_), | |||
| point2 (x2_, y2_), | |||
| isRadial (isRadial_) | |||
| { | |||
| colours.add (ColourPoint (0, colour1)); | |||
| colours.add (ColourPoint (1 << 16, colour2)); | |||
| colours.add (ColourPoint (0.0, colour1)); | |||
| colours.add (ColourPoint (1.0, colour2)); | |||
| } | |||
| ColourGradient::~ColourGradient() | |||
| @@ -75,13 +71,12 @@ void ColourGradient::clearColours() | |||
| colours.clear(); | |||
| } | |||
| void ColourGradient::addColour (const double proportionAlongGradient, | |||
| const Colour& colour) | |||
| int ColourGradient::addColour (const double proportionAlongGradient, const Colour& colour) | |||
| { | |||
| // must be within the two end-points | |||
| jassert (proportionAlongGradient >= 0 && proportionAlongGradient <= 1.0); | |||
| const uint32 pos = jlimit (0, 65535, roundToInt (proportionAlongGradient * 65536.0)); | |||
| const double pos = jlimit (0.0, 1.0, proportionAlongGradient); | |||
| int i; | |||
| for (i = 0; i < colours.size(); ++i) | |||
| @@ -89,6 +84,13 @@ void ColourGradient::addColour (const double proportionAlongGradient, | |||
| break; | |||
| colours.insert (i, ColourPoint (pos, colour)); | |||
| return i; | |||
| } | |||
| void ColourGradient::removeColour (int index) | |||
| { | |||
| jassert (index > 0 && index < colours.size() - 1); | |||
| colours.remove (index); | |||
| } | |||
| void ColourGradient::multiplyOpacity (const float multiplier) throw() | |||
| @@ -109,7 +111,7 @@ int ColourGradient::getNumColours() const throw() | |||
| double ColourGradient::getColourPosition (const int index) const throw() | |||
| { | |||
| if (((unsigned int) index) < (unsigned int) colours.size()) | |||
| return jlimit (0.0, 1.0, colours.getReference (index).position / 65535.0); | |||
| return colours.getReference (index).position; | |||
| return 0; | |||
| } | |||
| @@ -122,26 +124,31 @@ const Colour ColourGradient::getColour (const int index) const throw() | |||
| return Colour(); | |||
| } | |||
| const Colour ColourGradient::getColourAtPosition (const float position) const throw() | |||
| void ColourGradient::setColour (int index, const Colour& newColour) throw() | |||
| { | |||
| jassert (colours.getReference(0).position == 0); // the first colour specified has to go at position 0 | |||
| if (((unsigned int) index) < (unsigned int) colours.size()) | |||
| colours.getReference (index).colour = newColour; | |||
| } | |||
| const int integerPos = jlimit (0, 65535, roundToInt (position * 65536.0f)); | |||
| const Colour ColourGradient::getColourAtPosition (const double position) const throw() | |||
| { | |||
| jassert (colours.getReference(0).position == 0); // the first colour specified has to go at position 0 | |||
| if (integerPos <= 0 || colours.size() <= 1) | |||
| return getColour (0); | |||
| if (position <= 0 || colours.size() <= 1) | |||
| return colours.getReference(0).colour; | |||
| int i = colours.size() - 1; | |||
| while (integerPos < (int) colours.getReference(i).position) | |||
| while (position < colours.getReference(i).position) | |||
| --i; | |||
| const ColourPoint& p1 = colours.getReference (i); | |||
| if (i >= colours.size() - 1) | |||
| return colours.getReference(i).colour; | |||
| return p1.colour; | |||
| const ColourPoint& p1 = colours.getReference (i); | |||
| const ColourPoint& p2 = colours.getReference (i + 1); | |||
| return p1.colour.interpolatedWith (p2.colour, (integerPos - p1.position) / (float) (p2.position - p1.position)); | |||
| return p1.colour.interpolatedWith (p2.colour, (float) ((position - p1.position) / (p2.position - p1.position))); | |||
| } | |||
| //============================================================================== | |||
| @@ -168,7 +175,7 @@ int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlo | |||
| for (int j = 1; j < colours.size(); ++j) | |||
| { | |||
| const ColourPoint& p = colours.getReference (j); | |||
| const int numToDo = ((p.position * (numEntries - 1)) >> 16) - index; | |||
| const int numToDo = roundToInt (p.position * (numEntries - 1)) - index; | |||
| const PixelARGB pix2 (p.colour.getPixelARGB()); | |||
| for (int i = 0; i < numToDo; ++i) | |||
| @@ -90,9 +90,13 @@ public: | |||
| of the distance along the line between the two points | |||
| at which the colour should occur. | |||
| @param colour the colour that should be used at this point | |||
| @returns the index at which the new point was added | |||
| */ | |||
| void addColour (double proportionAlongGradient, | |||
| const Colour& colour); | |||
| int addColour (double proportionAlongGradient, | |||
| const Colour& colour); | |||
| /** Removes one of the colours from the gradient. */ | |||
| void removeColour (int index); | |||
| /** Multiplies the alpha value of all the colours by the given scale factor */ | |||
| void multiplyOpacity (float multiplier) throw(); | |||
| @@ -108,15 +112,19 @@ public: | |||
| double getColourPosition (int index) const throw(); | |||
| /** Returns the colour that was added with a given index. | |||
| The index is from 0 to getNumColours() - 1. The return value will be between 0.0 and 1.0 | |||
| The index is from 0 to getNumColours() - 1. | |||
| */ | |||
| const Colour getColour (int index) const throw(); | |||
| /** Changes the colour at a given index. | |||
| The index is from 0 to getNumColours() - 1. | |||
| */ | |||
| void setColour (int index, const Colour& newColour) throw(); | |||
| /** Returns the an interpolated colour at any position along the gradient. | |||
| @param position the position along the gradient, between 0 and 1 | |||
| */ | |||
| const Colour getColourAtPosition (float position) const throw(); | |||
| const Colour getColourAtPosition (double position) const throw(); | |||
| //============================================================================== | |||
| /** Creates a set of interpolated premultiplied ARGB values. | |||
| @@ -152,14 +160,14 @@ private: | |||
| { | |||
| ColourPoint() throw() {} | |||
| ColourPoint (uint32 position_, const Colour& colour_) throw() | |||
| ColourPoint (const double position_, const Colour& colour_) throw() | |||
| : position (position_), colour (colour_) | |||
| {} | |||
| bool operator== (const ColourPoint& other) const throw() { return position == other.position && colour == other.colour; } | |||
| bool operator!= (const ColourPoint& other) const throw() { return position != other.position || colour != other.colour; } | |||
| uint32 position; | |||
| double position; | |||
| Colour colour; | |||
| }; | |||
| @@ -158,10 +158,8 @@ Drawable* Drawable::createFromValueTree (const ValueTree& tree, ImageProvider* i | |||
| //============================================================================== | |||
| const Identifier Drawable::ValueTreeWrapperBase::idProperty ("id"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::type ("type"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::x1 ("x1"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::x2 ("x2"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::y1 ("y1"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::y2 ("y2"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::gradientPoint1 ("point1"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::gradientPoint2 ("point2"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::colour ("colour"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::radial ("radial"); | |||
| const Identifier Drawable::ValueTreeWrapperBase::colours ("colours"); | |||
| @@ -188,7 +186,8 @@ void Drawable::ValueTreeWrapperBase::setID (const String& newID, UndoManager* un | |||
| state.setProperty (idProperty, newID, undoManager); | |||
| } | |||
| const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v) | |||
| const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v, RelativePoint* gp1, RelativePoint* gp2, | |||
| RelativeCoordinate::NamedCoordinateFinder* nameFinder) | |||
| { | |||
| const String newType (v[type].toString()); | |||
| @@ -200,9 +199,17 @@ const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v) | |||
| } | |||
| else if (newType == "gradient") | |||
| { | |||
| RelativePoint p1 (v [gradientPoint1]), p2 (v [gradientPoint2]); | |||
| ColourGradient g; | |||
| g.point1.setXY (v[x1], v[y1]); | |||
| g.point2.setXY (v[x2], v[y2]); | |||
| if (gp1 != 0) | |||
| *gp1 = p1; | |||
| if (gp2 != 0) | |||
| *gp2 = p2; | |||
| g.point1 = p1.resolve (nameFinder); | |||
| g.point2 = p2.resolve (nameFinder); | |||
| g.isRadial = v[radial]; | |||
| StringArray colourSteps; | |||
| @@ -223,26 +230,24 @@ const FillType Drawable::ValueTreeWrapperBase::readFillType (const ValueTree& v) | |||
| return FillType(); | |||
| } | |||
| void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType& fillType, UndoManager* const undoManager) | |||
| void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType& fillType, | |||
| const RelativePoint* gp1, const RelativePoint* gp2, | |||
| UndoManager* const undoManager) | |||
| { | |||
| if (fillType.isColour()) | |||
| { | |||
| v.setProperty (type, "solid", undoManager); | |||
| v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager); | |||
| v.removeProperty (x1, undoManager); | |||
| v.removeProperty (x2, undoManager); | |||
| v.removeProperty (y1, undoManager); | |||
| v.removeProperty (y2, undoManager); | |||
| v.removeProperty (gradientPoint1, undoManager); | |||
| v.removeProperty (gradientPoint2, undoManager); | |||
| v.removeProperty (radial, undoManager); | |||
| v.removeProperty (colours, undoManager); | |||
| } | |||
| else if (fillType.isGradient()) | |||
| { | |||
| v.setProperty (type, "gradient", undoManager); | |||
| v.setProperty (x1, fillType.gradient->point1.getX(), undoManager); | |||
| v.setProperty (y1, fillType.gradient->point1.getY(), undoManager); | |||
| v.setProperty (x2, fillType.gradient->point2.getX(), undoManager); | |||
| v.setProperty (y2, fillType.gradient->point2.getY(), undoManager); | |||
| v.setProperty (gradientPoint1, gp1 != 0 ? gp1->toString() : fillType.gradient->point1.toString(), undoManager); | |||
| v.setProperty (gradientPoint2, gp2 != 0 ? gp2->toString() : fillType.gradient->point2.toString(), undoManager); | |||
| v.setProperty (radial, fillType.gradient->isRadial, undoManager); | |||
| String s; | |||
| @@ -259,10 +264,8 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||
| jassertfalse; //xxx todo | |||
| v.removeProperty (x1, undoManager); | |||
| v.removeProperty (x2, undoManager); | |||
| v.removeProperty (y1, undoManager); | |||
| v.removeProperty (y2, undoManager); | |||
| v.removeProperty (gradientPoint1, undoManager); | |||
| v.removeProperty (gradientPoint2, undoManager); | |||
| v.removeProperty (radial, undoManager); | |||
| v.removeProperty (colours, undoManager); | |||
| v.removeProperty (colour, undoManager); | |||
| @@ -273,7 +276,9 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||
| } | |||
| } | |||
| void Drawable::ValueTreeWrapperBase::replaceFillType (const Identifier& tag, const FillType& fillType, UndoManager* const undoManager) | |||
| void Drawable::ValueTreeWrapperBase::replaceFillType (const Identifier& tag, const FillType& fillType, | |||
| const RelativePoint* gp1, const RelativePoint* gp2, | |||
| UndoManager* const undoManager) | |||
| { | |||
| ValueTree v (state.getChildWithName (tag)); | |||
| @@ -283,7 +288,7 @@ void Drawable::ValueTreeWrapperBase::replaceFillType (const Identifier& tag, con | |||
| v = state.getChildWithName (tag); | |||
| } | |||
| writeFillType (v, fillType, undoManager); | |||
| writeFillType (v, fillType, gp1, gp2, undoManager); | |||
| } | |||
| @@ -248,14 +248,20 @@ public: | |||
| void setID (const String& newID, UndoManager* undoManager); | |||
| static const Identifier idProperty; | |||
| static const FillType readFillType (const ValueTree& v); | |||
| static void writeFillType (ValueTree& v, const FillType& fillType, UndoManager* undoManager); | |||
| static const FillType readFillType (const ValueTree& v, RelativePoint* gradientPoint1, RelativePoint* gradientPoint2, | |||
| RelativeCoordinate::NamedCoordinateFinder* nameFinder); | |||
| static void writeFillType (ValueTree& v, const FillType& fillType, | |||
| const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | |||
| UndoManager* undoManager); | |||
| protected: | |||
| ValueTree state; | |||
| static const Identifier type, x1, x2, y1, y2, colour, radial, colours; | |||
| static const Identifier type, gradientPoint1, gradientPoint2, colour, radial, colours; | |||
| void replaceFillType (const Identifier& tag, const FillType& fillType, UndoManager* undoManager); | |||
| void replaceFillType (const Identifier& tag, const FillType& fillType, | |||
| const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | |||
| UndoManager* undoManager); | |||
| }; | |||
| //============================================================================== | |||
| @@ -197,24 +197,46 @@ DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | |||
| jassert (state.hasType (valueTreeType)); | |||
| } | |||
| const FillType DrawablePath::ValueTreeWrapper::getMainFill() const | |||
| ValueTree DrawablePath::ValueTreeWrapper::getMainFillState() | |||
| { | |||
| return readFillType (state.getChildWithName (fill)); | |||
| ValueTree v (state.getChildWithName (fill)); | |||
| if (v.isValid()) | |||
| return v; | |||
| setMainFill (Colours::black, 0, 0, 0); | |||
| return getMainFillState(); | |||
| } | |||
| ValueTree DrawablePath::ValueTreeWrapper::getStrokeFillState() | |||
| { | |||
| ValueTree v (state.getChildWithName (stroke)); | |||
| if (v.isValid()) | |||
| return v; | |||
| setStrokeFill (Colours::black, 0, 0, 0); | |||
| return getStrokeFillState(); | |||
| } | |||
| const FillType DrawablePath::ValueTreeWrapper::getMainFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | |||
| { | |||
| return readFillType (state.getChildWithName (fill), 0, 0, nameFinder); | |||
| } | |||
| void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, UndoManager* undoManager) | |||
| void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const RelativePoint* gp1, | |||
| const RelativePoint* gp2, UndoManager* undoManager) | |||
| { | |||
| replaceFillType (fill, newFill, undoManager); | |||
| replaceFillType (fill, newFill, gp1, gp2, undoManager); | |||
| } | |||
| const FillType DrawablePath::ValueTreeWrapper::getStrokeFill() const | |||
| const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | |||
| { | |||
| return readFillType (state.getChildWithName (stroke)); | |||
| return readFillType (state.getChildWithName (stroke), 0, 0, nameFinder); | |||
| } | |||
| void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, UndoManager* undoManager) | |||
| void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, | |||
| const RelativePoint* gp2, UndoManager* undoManager) | |||
| { | |||
| replaceFillType (stroke, newFill, undoManager); | |||
| replaceFillType (stroke, newFill, gp1, gp2, undoManager); | |||
| } | |||
| const PathStrokeType DrawablePath::ValueTreeWrapper::getStrokeType() const | |||
| @@ -258,7 +280,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree | |||
| setName (v.getID()); | |||
| bool needsRedraw = false; | |||
| const FillType newFill (v.getMainFill()); | |||
| const FillType newFill (v.getMainFill (parent)); | |||
| if (mainFill != newFill) | |||
| { | |||
| @@ -266,7 +288,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree | |||
| mainFill = newFill; | |||
| } | |||
| const FillType newStrokeFill (v.getStrokeFill()); | |||
| const FillType newStrokeFill (v.getStrokeFill (parent)); | |||
| if (strokeFill != newStrokeFill) | |||
| { | |||
| @@ -307,8 +329,8 @@ const ValueTree DrawablePath::createValueTree (ImageProvider*) const | |||
| ValueTreeWrapper v (tree); | |||
| v.setID (getName(), 0); | |||
| v.setMainFill (mainFill, 0); | |||
| v.setStrokeFill (strokeFill, 0); | |||
| v.setMainFill (mainFill, 0, 0, 0); | |||
| v.setStrokeFill (strokeFill, 0, 0, 0); | |||
| v.setStrokeType (strokeType, 0); | |||
| if (relativePath != 0) | |||
| @@ -128,11 +128,15 @@ public: | |||
| public: | |||
| ValueTreeWrapper (const ValueTree& state); | |||
| const FillType getMainFill() const; | |||
| void setMainFill (const FillType& newFill, UndoManager* undoManager); | |||
| const FillType getStrokeFill() const; | |||
| void setStrokeFill (const FillType& newFill, UndoManager* undoManager); | |||
| const FillType getMainFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const; | |||
| ValueTree getMainFillState(); | |||
| void setMainFill (const FillType& newFill, const RelativePoint* gradientPoint1, | |||
| const RelativePoint* gradientPoint2, UndoManager* undoManager); | |||
| const FillType getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const; | |||
| ValueTree getStrokeFillState(); | |||
| void setStrokeFill (const FillType& newFill, const RelativePoint* gradientPoint1, | |||
| const RelativePoint* gradientPoint2, UndoManager* undoManager); | |||
| const PathStrokeType getStrokeType() const; | |||
| void setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager); | |||
| @@ -1,482 +1,481 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-10 by Raw Material Software Ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the GNU General | |||
| Public License (Version 2), as published by the Free Software Foundation. | |||
| A copy of the license is included in the JUCE distribution, or can be found | |||
| online at www.gnu.org/licenses. | |||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.rawmaterialsoftware.com/juce for more information. | |||
| ============================================================================== | |||
| */ | |||
| // (This file gets included by juce_linux_NativeCode.cpp, rather than being | |||
| // compiled on its own). | |||
| #if JUCE_INCLUDED_FILE | |||
| //============================================================================== | |||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) | |||
| { | |||
| int numResults = 0; | |||
| const int s = socket (AF_INET, SOCK_DGRAM, 0); | |||
| if (s != -1) | |||
| { | |||
| char buf [1024]; | |||
| struct ifconf ifc; | |||
| ifc.ifc_len = sizeof (buf); | |||
| ifc.ifc_buf = buf; | |||
| ioctl (s, SIOCGIFCONF, &ifc); | |||
| for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i) | |||
| { | |||
| struct ifreq ifr; | |||
| strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name); | |||
| if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0 | |||
| && (ifr.ifr_flags & IFF_LOOPBACK) == 0 | |||
| && ioctl (s, SIOCGIFHWADDR, &ifr) == 0 | |||
| && numResults < maxNum) | |||
| { | |||
| int64 a = 0; | |||
| for (int j = 6; --j >= 0;) | |||
| a = (a << 8) | (uint8) ifr.ifr_hwaddr.sa_data [littleEndian ? j : (5 - j)]; | |||
| *addresses++ = a; | |||
| ++numResults; | |||
| } | |||
| } | |||
| close (s); | |||
| } | |||
| return numResults; | |||
| } | |||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| jassertfalse; // xxx todo | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| /** A HTTP input stream that uses sockets. | |||
| */ | |||
| class JUCE_HTTPSocketStream | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| JUCE_HTTPSocketStream() | |||
| : readPosition (0), | |||
| socketHandle (-1), | |||
| levelsOfRedirection (0), | |||
| timeoutSeconds (15) | |||
| { | |||
| } | |||
| ~JUCE_HTTPSocketStream() | |||
| { | |||
| closeSocket(); | |||
| } | |||
| //============================================================================== | |||
| bool open (const String& url, | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost, | |||
| URL::OpenStreamProgressCallback* callback, | |||
| void* callbackContext, | |||
| int timeOutMs) | |||
| { | |||
| closeSocket(); | |||
| uint32 timeOutTime = Time::getMillisecondCounter(); | |||
| if (timeOutMs == 0) | |||
| timeOutTime += 60000; | |||
| else if (timeOutMs < 0) | |||
| timeOutTime = 0xffffffff; | |||
| else | |||
| timeOutTime += timeOutMs; | |||
| String hostName, hostPath; | |||
| int hostPort; | |||
| if (! decomposeURL (url, hostName, hostPath, hostPort)) | |||
| return false; | |||
| const struct hostent* host = 0; | |||
| int port = 0; | |||
| String proxyName, proxyPath; | |||
| int proxyPort = 0; | |||
| String proxyURL (getenv ("http_proxy")); | |||
| if (proxyURL.startsWithIgnoreCase ("http://")) | |||
| { | |||
| if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | |||
| return false; | |||
| host = gethostbyname (proxyName.toUTF8()); | |||
| port = proxyPort; | |||
| } | |||
| else | |||
| { | |||
| host = gethostbyname (hostName.toUTF8()); | |||
| port = hostPort; | |||
| } | |||
| if (host == 0) | |||
| return false; | |||
| struct sockaddr_in address; | |||
| zerostruct (address); | |||
| memcpy (&address.sin_addr, host->h_addr, host->h_length); | |||
| address.sin_family = host->h_addrtype; | |||
| address.sin_port = htons (port); | |||
| socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); | |||
| if (socketHandle == -1) | |||
| return false; | |||
| int receiveBufferSize = 16384; | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); | |||
| #if JUCE_MAC | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); | |||
| #endif | |||
| if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, | |||
| proxyName, proxyPort, | |||
| hostPath, url, | |||
| headers, postData, | |||
| isPost)); | |||
| size_t totalHeaderSent = 0; | |||
| while (totalHeaderSent < requestHeader.getSize()) | |||
| { | |||
| if (Time::getMillisecondCounter() > timeOutTime) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); | |||
| if (send (socketHandle, | |||
| ((const char*) requestHeader.getData()) + totalHeaderSent, | |||
| numToSend, 0) | |||
| != numToSend) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| totalHeaderSent += numToSend; | |||
| if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| } | |||
| const String responseHeader (readResponse (timeOutTime)); | |||
| if (responseHeader.isNotEmpty()) | |||
| { | |||
| //DBG (responseHeader); | |||
| StringArray lines; | |||
| lines.addLines (responseHeader); | |||
| const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||
| .substring (0, 3).getIntValue(); | |||
| //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | |||
| //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | |||
| String location (findHeaderItem (lines, "Location:")); | |||
| if (statusCode >= 300 && statusCode < 400 | |||
| && location.isNotEmpty()) | |||
| { | |||
| if (! location.startsWithIgnoreCase ("http://")) | |||
| location = "http://" + location; | |||
| if (levelsOfRedirection++ < 3) | |||
| return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs); | |||
| } | |||
| else | |||
| { | |||
| levelsOfRedirection = 0; | |||
| return true; | |||
| } | |||
| } | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| int read (void* buffer, int bytesToRead) | |||
| { | |||
| fd_set readbits; | |||
| FD_ZERO (&readbits); | |||
| FD_SET (socketHandle, &readbits); | |||
| struct timeval tv; | |||
| tv.tv_sec = timeoutSeconds; | |||
| tv.tv_usec = 0; | |||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||
| return 0; // (timeout) | |||
| const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||
| readPosition += bytesRead; | |||
| return bytesRead; | |||
| } | |||
| //============================================================================== | |||
| int readPosition; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| int socketHandle, levelsOfRedirection; | |||
| const int timeoutSeconds; | |||
| //============================================================================== | |||
| void closeSocket() | |||
| { | |||
| if (socketHandle >= 0) | |||
| close (socketHandle); | |||
| socketHandle = -1; | |||
| } | |||
| const MemoryBlock createRequestHeader (const String& hostName, | |||
| const int hostPort, | |||
| const String& proxyName, | |||
| const int proxyPort, | |||
| const String& hostPath, | |||
| const String& originalURL, | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost) | |||
| { | |||
| String header (isPost ? "POST " : "GET "); | |||
| if (proxyName.isEmpty()) | |||
| { | |||
| header << hostPath << " HTTP/1.0\r\nHost: " | |||
| << hostName << ':' << hostPort; | |||
| } | |||
| else | |||
| { | |||
| header << originalURL << " HTTP/1.0\r\nHost: " | |||
| << proxyName << ':' << proxyPort; | |||
| } | |||
| header << "\r\nUser-Agent: JUCE/" | |||
| << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | |||
| << "\r\nConnection: Close\r\nContent-Length: " | |||
| << postData.getSize() << "\r\n" | |||
| << headers << "\r\n"; | |||
| MemoryBlock mb; | |||
| mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); | |||
| mb.append (postData.getData(), postData.getSize()); | |||
| return mb; | |||
| } | |||
| const String readResponse (const uint32 timeOutTime) | |||
| { | |||
| int bytesRead = 0, numConsecutiveLFs = 0; | |||
| MemoryBlock buffer (1024, true); | |||
| while (numConsecutiveLFs < 2 && bytesRead < 32768 | |||
| && Time::getMillisecondCounter() <= timeOutTime) | |||
| { | |||
| fd_set readbits; | |||
| FD_ZERO (&readbits); | |||
| FD_SET (socketHandle, &readbits); | |||
| struct timeval tv; | |||
| tv.tv_sec = timeoutSeconds; | |||
| tv.tv_usec = 0; | |||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||
| return String::empty; // (timeout) | |||
| buffer.ensureSize (bytesRead + 8, true); | |||
| char* const dest = (char*) buffer.getData() + bytesRead; | |||
| if (recv (socketHandle, dest, 1, 0) == -1) | |||
| return String::empty; | |||
| const char lastByte = *dest; | |||
| ++bytesRead; | |||
| if (lastByte == '\n') | |||
| ++numConsecutiveLFs; | |||
| else if (lastByte != '\r') | |||
| numConsecutiveLFs = 0; | |||
| } | |||
| const String header (String::fromUTF8 ((const char*) buffer.getData())); | |||
| if (header.startsWithIgnoreCase ("HTTP/")) | |||
| return header.trimEnd(); | |||
| return String::empty; | |||
| } | |||
| //============================================================================== | |||
| static bool decomposeURL (const String& url, | |||
| String& host, String& path, int& port) | |||
| { | |||
| if (! url.startsWithIgnoreCase ("http://")) | |||
| return false; | |||
| const int nextSlash = url.indexOfChar (7, '/'); | |||
| int nextColon = url.indexOfChar (7, ':'); | |||
| if (nextColon > nextSlash && nextSlash > 0) | |||
| nextColon = -1; | |||
| if (nextColon >= 0) | |||
| { | |||
| host = url.substring (7, nextColon); | |||
| if (nextSlash >= 0) | |||
| port = url.substring (nextColon + 1, nextSlash).getIntValue(); | |||
| else | |||
| port = url.substring (nextColon + 1).getIntValue(); | |||
| } | |||
| else | |||
| { | |||
| port = 80; | |||
| if (nextSlash >= 0) | |||
| host = url.substring (7, nextSlash); | |||
| else | |||
| host = url.substring (7); | |||
| } | |||
| if (nextSlash >= 0) | |||
| path = url.substring (nextSlash); | |||
| else | |||
| path = "/"; | |||
| return true; | |||
| } | |||
| //============================================================================== | |||
| static const String findHeaderItem (const StringArray& lines, const String& itemName) | |||
| { | |||
| for (int i = 0; i < lines.size(); ++i) | |||
| if (lines[i].startsWithIgnoreCase (itemName)) | |||
| return lines[i].substring (itemName.length()).trim(); | |||
| return String::empty; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| void* juce_openInternetFile (const String& url, | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost, | |||
| URL::OpenStreamProgressCallback* callback, | |||
| void* callbackContext, | |||
| int timeOutMs) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream(); | |||
| if (s->open (url, headers, postData, isPost, | |||
| callback, callbackContext, timeOutMs)) | |||
| return s; | |||
| delete s; | |||
| return 0; | |||
| } | |||
| void juce_closeInternetFile (void* handle) | |||
| { | |||
| delete static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| } | |||
| int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||
| if (s != 0) | |||
| return s->read (buffer, bytesToRead); | |||
| return 0; | |||
| } | |||
| int64 juce_getInternetFileContentLength (void* handle) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||
| if (s != 0) | |||
| { | |||
| //xxx todo | |||
| jassertfalse; | |||
| } | |||
| return -1; | |||
| } | |||
| void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||
| if (s != 0) | |||
| { | |||
| // xxx todo | |||
| jassertfalse; | |||
| } | |||
| } | |||
| int juce_seekInInternetFile (void* handle, int newPosition) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||
| if (s != 0) | |||
| return s->readPosition; | |||
| return 0; | |||
| } | |||
| #endif | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-10 by Raw Material Software Ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the GNU General | |||
| Public License (Version 2), as published by the Free Software Foundation. | |||
| A copy of the license is included in the JUCE distribution, or can be found | |||
| online at www.gnu.org/licenses. | |||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.rawmaterialsoftware.com/juce for more information. | |||
| ============================================================================== | |||
| */ | |||
| // (This file gets included by juce_linux_NativeCode.cpp, rather than being | |||
| // compiled on its own). | |||
| #if JUCE_INCLUDED_FILE | |||
| //============================================================================== | |||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) | |||
| { | |||
| int numResults = 0; | |||
| const int s = socket (AF_INET, SOCK_DGRAM, 0); | |||
| if (s != -1) | |||
| { | |||
| char buf [1024]; | |||
| struct ifconf ifc; | |||
| ifc.ifc_len = sizeof (buf); | |||
| ifc.ifc_buf = buf; | |||
| ioctl (s, SIOCGIFCONF, &ifc); | |||
| for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i) | |||
| { | |||
| struct ifreq ifr; | |||
| strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name); | |||
| if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0 | |||
| && (ifr.ifr_flags & IFF_LOOPBACK) == 0 | |||
| && ioctl (s, SIOCGIFHWADDR, &ifr) == 0 | |||
| && numResults < maxNum) | |||
| { | |||
| int64 a = 0; | |||
| for (int j = 6; --j >= 0;) | |||
| a = (a << 8) | (uint8) ifr.ifr_hwaddr.sa_data [littleEndian ? j : (5 - j)]; | |||
| *addresses++ = a; | |||
| ++numResults; | |||
| } | |||
| } | |||
| close (s); | |||
| } | |||
| return numResults; | |||
| } | |||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| jassertfalse; // xxx todo | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| /** A HTTP input stream that uses sockets. | |||
| */ | |||
| class JUCE_HTTPSocketStream | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| JUCE_HTTPSocketStream() | |||
| : readPosition (0), | |||
| socketHandle (-1), | |||
| levelsOfRedirection (0), | |||
| timeoutSeconds (15) | |||
| { | |||
| } | |||
| ~JUCE_HTTPSocketStream() | |||
| { | |||
| closeSocket(); | |||
| } | |||
| //============================================================================== | |||
| bool open (const String& url, | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost, | |||
| URL::OpenStreamProgressCallback* callback, | |||
| void* callbackContext, | |||
| int timeOutMs) | |||
| { | |||
| closeSocket(); | |||
| uint32 timeOutTime = Time::getMillisecondCounter(); | |||
| if (timeOutMs == 0) | |||
| timeOutTime += 60000; | |||
| else if (timeOutMs < 0) | |||
| timeOutTime = 0xffffffff; | |||
| else | |||
| timeOutTime += timeOutMs; | |||
| String hostName, hostPath; | |||
| int hostPort; | |||
| if (! decomposeURL (url, hostName, hostPath, hostPort)) | |||
| return false; | |||
| const struct hostent* host = 0; | |||
| int port = 0; | |||
| String proxyName, proxyPath; | |||
| int proxyPort = 0; | |||
| String proxyURL (getenv ("http_proxy")); | |||
| if (proxyURL.startsWithIgnoreCase ("http://")) | |||
| { | |||
| if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | |||
| return false; | |||
| host = gethostbyname (proxyName.toUTF8()); | |||
| port = proxyPort; | |||
| } | |||
| else | |||
| { | |||
| host = gethostbyname (hostName.toUTF8()); | |||
| port = hostPort; | |||
| } | |||
| if (host == 0) | |||
| return false; | |||
| struct sockaddr_in address; | |||
| zerostruct (address); | |||
| memcpy (&address.sin_addr, host->h_addr, host->h_length); | |||
| address.sin_family = host->h_addrtype; | |||
| address.sin_port = htons (port); | |||
| socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); | |||
| if (socketHandle == -1) | |||
| return false; | |||
| int receiveBufferSize = 16384; | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); | |||
| #if JUCE_MAC | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); | |||
| #endif | |||
| if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, | |||
| proxyName, proxyPort, | |||
| hostPath, url, | |||
| headers, postData, | |||
| isPost)); | |||
| size_t totalHeaderSent = 0; | |||
| while (totalHeaderSent < requestHeader.getSize()) | |||
| { | |||
| if (Time::getMillisecondCounter() > timeOutTime) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); | |||
| if (send (socketHandle, | |||
| ((const char*) requestHeader.getData()) + totalHeaderSent, | |||
| numToSend, 0) | |||
| != numToSend) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| totalHeaderSent += numToSend; | |||
| if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| } | |||
| const String responseHeader (readResponse (timeOutTime)); | |||
| if (responseHeader.isNotEmpty()) | |||
| { | |||
| //DBG (responseHeader); | |||
| headerLines.clear(); | |||
| headerLines.addLines (responseHeader); | |||
| const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||
| .substring (0, 3).getIntValue(); | |||
| //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | |||
| //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | |||
| String location (findHeaderItem (headerLines, "Location:")); | |||
| if (statusCode >= 300 && statusCode < 400 | |||
| && location.isNotEmpty()) | |||
| { | |||
| if (! location.startsWithIgnoreCase ("http://")) | |||
| location = "http://" + location; | |||
| if (levelsOfRedirection++ < 3) | |||
| return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs); | |||
| } | |||
| else | |||
| { | |||
| levelsOfRedirection = 0; | |||
| return true; | |||
| } | |||
| } | |||
| closeSocket(); | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| int read (void* buffer, int bytesToRead) | |||
| { | |||
| fd_set readbits; | |||
| FD_ZERO (&readbits); | |||
| FD_SET (socketHandle, &readbits); | |||
| struct timeval tv; | |||
| tv.tv_sec = timeoutSeconds; | |||
| tv.tv_usec = 0; | |||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||
| return 0; // (timeout) | |||
| const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||
| readPosition += bytesRead; | |||
| return bytesRead; | |||
| } | |||
| //============================================================================== | |||
| int readPosition; | |||
| StringArray headerLines; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| int socketHandle, levelsOfRedirection; | |||
| const int timeoutSeconds; | |||
| //============================================================================== | |||
| void closeSocket() | |||
| { | |||
| if (socketHandle >= 0) | |||
| close (socketHandle); | |||
| socketHandle = -1; | |||
| } | |||
| const MemoryBlock createRequestHeader (const String& hostName, | |||
| const int hostPort, | |||
| const String& proxyName, | |||
| const int proxyPort, | |||
| const String& hostPath, | |||
| const String& originalURL, | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost) | |||
| { | |||
| String header (isPost ? "POST " : "GET "); | |||
| if (proxyName.isEmpty()) | |||
| { | |||
| header << hostPath << " HTTP/1.0\r\nHost: " | |||
| << hostName << ':' << hostPort; | |||
| } | |||
| else | |||
| { | |||
| header << originalURL << " HTTP/1.0\r\nHost: " | |||
| << proxyName << ':' << proxyPort; | |||
| } | |||
| header << "\r\nUser-Agent: JUCE/" | |||
| << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | |||
| << "\r\nConnection: Close\r\nContent-Length: " | |||
| << postData.getSize() << "\r\n" | |||
| << headers << "\r\n"; | |||
| MemoryBlock mb; | |||
| mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); | |||
| mb.append (postData.getData(), postData.getSize()); | |||
| return mb; | |||
| } | |||
| const String readResponse (const uint32 timeOutTime) | |||
| { | |||
| int bytesRead = 0, numConsecutiveLFs = 0; | |||
| MemoryBlock buffer (1024, true); | |||
| while (numConsecutiveLFs < 2 && bytesRead < 32768 | |||
| && Time::getMillisecondCounter() <= timeOutTime) | |||
| { | |||
| fd_set readbits; | |||
| FD_ZERO (&readbits); | |||
| FD_SET (socketHandle, &readbits); | |||
| struct timeval tv; | |||
| tv.tv_sec = timeoutSeconds; | |||
| tv.tv_usec = 0; | |||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||
| return String::empty; // (timeout) | |||
| buffer.ensureSize (bytesRead + 8, true); | |||
| char* const dest = (char*) buffer.getData() + bytesRead; | |||
| if (recv (socketHandle, dest, 1, 0) == -1) | |||
| return String::empty; | |||
| const char lastByte = *dest; | |||
| ++bytesRead; | |||
| if (lastByte == '\n') | |||
| ++numConsecutiveLFs; | |||
| else if (lastByte != '\r') | |||
| numConsecutiveLFs = 0; | |||
| } | |||
| const String header (String::fromUTF8 ((const char*) buffer.getData())); | |||
| if (header.startsWithIgnoreCase ("HTTP/")) | |||
| return header.trimEnd(); | |||
| return String::empty; | |||
| } | |||
| //============================================================================== | |||
| static bool decomposeURL (const String& url, | |||
| String& host, String& path, int& port) | |||
| { | |||
| if (! url.startsWithIgnoreCase ("http://")) | |||
| return false; | |||
| const int nextSlash = url.indexOfChar (7, '/'); | |||
| int nextColon = url.indexOfChar (7, ':'); | |||
| if (nextColon > nextSlash && nextSlash > 0) | |||
| nextColon = -1; | |||
| if (nextColon >= 0) | |||
| { | |||
| host = url.substring (7, nextColon); | |||
| if (nextSlash >= 0) | |||
| port = url.substring (nextColon + 1, nextSlash).getIntValue(); | |||
| else | |||
| port = url.substring (nextColon + 1).getIntValue(); | |||
| } | |||
| else | |||
| { | |||
| port = 80; | |||
| if (nextSlash >= 0) | |||
| host = url.substring (7, nextSlash); | |||
| else | |||
| host = url.substring (7); | |||
| } | |||
| if (nextSlash >= 0) | |||
| path = url.substring (nextSlash); | |||
| else | |||
| path = "/"; | |||
| return true; | |||
| } | |||
| //============================================================================== | |||
| static const String findHeaderItem (const StringArray& lines, const String& itemName) | |||
| { | |||
| for (int i = 0; i < lines.size(); ++i) | |||
| if (lines[i].startsWithIgnoreCase (itemName)) | |||
| return lines[i].substring (itemName.length()).trim(); | |||
| return String::empty; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| void* juce_openInternetFile (const String& url, | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost, | |||
| URL::OpenStreamProgressCallback* callback, | |||
| void* callbackContext, | |||
| int timeOutMs) | |||
| { | |||
| ScopedPointer<JUCE_HTTPSocketStream> s (new JUCE_HTTPSocketStream()); | |||
| if (s->open (url, headers, postData, isPost, callback, callbackContext, timeOutMs)) | |||
| return s.release(); | |||
| return 0; | |||
| } | |||
| void juce_closeInternetFile (void* handle) | |||
| { | |||
| delete static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| } | |||
| int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| return s != 0 ? s->read (buffer, bytesToRead) : 0; | |||
| } | |||
| int64 juce_getInternetFileContentLength (void* handle) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| if (s != 0) | |||
| { | |||
| //xxx todo | |||
| jassertfalse | |||
| } | |||
| return -1; | |||
| } | |||
| bool juce_getInternetFileHeaders (void* handle, StringPairArray& headers) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| if (s != 0) | |||
| { | |||
| 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 previousValue (headers [key]); | |||
| headers.set (key, previousValue.isEmpty() ? value : (previousValue + ";" + value)); | |||
| } | |||
| } | |||
| } | |||
| int juce_seekInInternetFile (void* handle, int newPosition) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||
| return s != 0 ? s->readPosition : 0; | |||
| } | |||
| #endif | |||