diff --git a/extras/Jucer (experimental)/Source/jucer_Main.cpp b/extras/Jucer (experimental)/Source/jucer_Main.cpp index 9925928a4b..ae593144d2 100644 --- a/extras/Jucer (experimental)/Source/jucer_Main.cpp +++ b/extras/Jucer (experimental)/Source/jucer_Main.cpp @@ -68,6 +68,8 @@ public: anotherInstanceStarted (commandLine); theMainWindow->reloadLastProject(); + + theMainWindow->getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); } void shutdown() diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h index f8c5411cb9..f296275357 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h @@ -146,6 +146,13 @@ public: void createPropertyEditors (DrawableTypeInstance& item, Array & 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) diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h index 49094ffdb3..a1ee614db0 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h @@ -146,6 +146,10 @@ public: return getDocument().getCoordsFor (state); } + void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray& existingComps) + { + } + SelectedItems& getSelection() { return editor.getSelection(); diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h index 8f55c03d56..9a8af440f6 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h @@ -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 p (point.resolve (nameFinder)); + setBoundsInTargetSpace (Rectangle (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 5, 5)); + } + }; + + void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray& comps) + { + if (drawable == 0) + { + comps.clear(); + return; + } + + DrawableTypeInstance item (getDocument(), state); + Array 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 (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(); diff --git a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp index a7a0df4f8c..8797350cbe 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp @@ -114,12 +114,14 @@ public: const Rectangle 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 sizeGuides; bool isDragging; + OwnedArray extraEditorComps; const Rectangle 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 > lasso; + bool mouseDownResult, isDraggingClickedComp; + SelectedItems::ItemType mouseDownCompUID; + OwnedArray resizers; + OwnedArray markersX, markersY; + void updateResizeFrames() { SelectedItems& selection = getSelection(); @@ -610,49 +630,34 @@ public: } } - void update() + void updateMarkers (OwnedArray & markers, const bool isX) { - updateResizeFrames(); - updateMarkers(); - } - -private: - //============================================================================== - EditorCanvasBase* canvas; - ScopedPointer > lasso; - bool mouseDownResult, isDraggingClickedComp; - SelectedItems::ItemType mouseDownCompUID; - OwnedArray resizers; + MarkerListBase& markerList = canvas->getMarkerList (isX); + const int num = markerList.size(); - void updateMarkers (bool isX) - { Array 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 (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); } }; diff --git a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h index 4004255f9d..aaf04bc51c 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h @@ -64,27 +64,29 @@ public: void hideSizeGuides(); //============================================================================== + virtual UndoManager& getUndoManager() = 0; virtual void documentChanged() = 0; + virtual Component* createComponentHolder() = 0; + virtual const Rectangle getCanvasBounds() = 0; virtual void setCanvasBounds (const Rectangle& 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& 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 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 & itemsFound, const Rectangle& area) = 0; - virtual Component* createComponentHolder() = 0; class DragOperation { @@ -126,6 +128,10 @@ public: EditorCanvasBase* canvas; }; + //============================================================================== + virtual void updateExtraComponentsForObject (const ValueTree& state, Component* parent, + OwnedArray& existingComps) = 0; + protected: //============================================================================== const BorderSize border; diff --git a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorDragOperation.h b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorDragOperation.h index ab9adf3a73..af995a175a 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorDragOperation.h +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorDragOperation.h @@ -172,6 +172,11 @@ public: roundToInt (line.end - line.start) + extraEndLength * 2, 1)); } + bool updatePosition() + { + return true; + } + void paint (Graphics& g) { g.fillAll (alignmentMarkerColour); diff --git a/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h b/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h index 4e215baefd..88add5bdf3 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h @@ -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 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 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); } diff --git a/extras/Jucer (experimental)/Source/utility/jucer_StoredSettings.cpp b/extras/Jucer (experimental)/Source/utility/jucer_StoredSettings.cpp index 514f262ef7..db67ac9a62 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_StoredSettings.cpp +++ b/extras/Jucer (experimental)/Source/utility/jucer_StoredSettings.cpp @@ -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; +} diff --git a/extras/Jucer (experimental)/Source/utility/jucer_StoredSettings.h b/extras/Jucer (experimental)/Source/utility/jucer_StoredSettings.h index 0d76cd696c..9acf11e7c1 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_StoredSettings.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_StoredSettings.h @@ -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 props; StringArray fontNames; + + ScopedPointer fallbackImage; }; diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 50506660a5..cd9ee73235 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -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 & 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 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 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 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 (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 (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 (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 (handle); - if (s != 0) - return s->readPosition; - - return 0; + return s != 0 ? s->readPosition : 0; } #endif diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 659dd92f00..40142ba5e0 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -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); diff --git a/src/audio/processors/juce_AudioProcessorPlayer.cpp b/src/audio/processors/juce_AudioProcessorPlayer.cpp index 4def407ecf..9fe28d8ae3 100644 --- a/src/audio/processors/juce_AudioProcessorPlayer.cpp +++ b/src/audio/processors/juce_AudioProcessorPlayer.cpp @@ -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) diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 0a4dcc70ae..f8be6f2b85 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -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. diff --git a/src/gui/graphics/colour/juce_ColourGradient.cpp b/src/gui/graphics/colour/juce_ColourGradient.cpp index b29a9e7cbe..f8af3886e3 100644 --- a/src/gui/graphics/colour/juce_ColourGradient.cpp +++ b/src/gui/graphics/colour/juce_ColourGradient.cpp @@ -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) diff --git a/src/gui/graphics/colour/juce_ColourGradient.h b/src/gui/graphics/colour/juce_ColourGradient.h index 1ab5788cd9..23833cce75 100644 --- a/src/gui/graphics/colour/juce_ColourGradient.h +++ b/src/gui/graphics/colour/juce_ColourGradient.h @@ -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; }; diff --git a/src/gui/graphics/drawables/juce_Drawable.cpp b/src/gui/graphics/drawables/juce_Drawable.cpp index 510a584b4e..87f773b372 100644 --- a/src/gui/graphics/drawables/juce_Drawable.cpp +++ b/src/gui/graphics/drawables/juce_Drawable.cpp @@ -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); } diff --git a/src/gui/graphics/drawables/juce_Drawable.h b/src/gui/graphics/drawables/juce_Drawable.h index f2cdc2e437..fe797c5262 100644 --- a/src/gui/graphics/drawables/juce_Drawable.h +++ b/src/gui/graphics/drawables/juce_Drawable.h @@ -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); }; //============================================================================== diff --git a/src/gui/graphics/drawables/juce_DrawablePath.cpp b/src/gui/graphics/drawables/juce_DrawablePath.cpp index 3af7b83693..98d9e88202 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.cpp +++ b/src/gui/graphics/drawables/juce_DrawablePath.cpp @@ -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 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 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) diff --git a/src/gui/graphics/drawables/juce_DrawablePath.h b/src/gui/graphics/drawables/juce_DrawablePath.h index 34343e28c4..ec8486c490 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.h +++ b/src/gui/graphics/drawables/juce_DrawablePath.h @@ -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); diff --git a/src/native/linux/juce_linux_Network.cpp b/src/native/linux/juce_linux_Network.cpp index 774f2fbafc..8182792b84 100644 --- a/src/native/linux/juce_linux_Network.cpp +++ b/src/native/linux/juce_linux_Network.cpp @@ -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 (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 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 (handle); +} + +int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) +{ + JUCE_HTTPSocketStream* const s = static_cast (handle); + + return s != 0 ? s->read (buffer, bytesToRead) : 0; +} + +int64 juce_getInternetFileContentLength (void* handle) +{ + JUCE_HTTPSocketStream* const s = static_cast (handle); + + if (s != 0) + { + //xxx todo + jassertfalse + } + + return -1; +} + +bool juce_getInternetFileHeaders (void* handle, StringPairArray& headers) +{ + JUCE_HTTPSocketStream* const s = static_cast (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 (handle); + + return s != 0 ? s->readPosition : 0; +} + +#endif