diff --git a/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj b/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj index 406084abb4..496b9e545c 100644 --- a/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj +++ b/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj @@ -105,6 +105,7 @@ F9EAFD5BC3E676BC59B326E0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_DrawableObjectComponent.h; path = "../../Source/ui/Drawable Editor/jucer_DrawableObjectComponent.h"; sourceTree = SOURCE_ROOT; }; 654C1B62A2BE1FBB28BEAE72 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_DrawableTreeviewItem.h; path = "../../Source/ui/Drawable Editor/jucer_DrawableTreeviewItem.h"; sourceTree = SOURCE_ROOT; }; E6BB0D9B515B50E418D98B9C = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_CommandIDs.h; path = ../../Source/ui/jucer_CommandIDs.h; sourceTree = SOURCE_ROOT; }; + 95A4E72FC64A7338CB259DF7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_CoordinatePropertyComponent.h; path = ../../Source/ui/jucer_CoordinatePropertyComponent.h; sourceTree = SOURCE_ROOT; }; 3B2C45064E85B3B631D4F921 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_DocumentEditorComponent.cpp; path = ../../Source/ui/jucer_DocumentEditorComponent.cpp; sourceTree = SOURCE_ROOT; }; 156F72179BBFCD0D9804C266 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_DocumentEditorComponent.h; path = ../../Source/ui/jucer_DocumentEditorComponent.h; sourceTree = SOURCE_ROOT; }; 8F731296532276CBF2B6190D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_GroupInformationComponent.cpp; path = ../../Source/ui/jucer_GroupInformationComponent.cpp; sourceTree = SOURCE_ROOT; }; @@ -209,6 +210,7 @@ 3A0BE83502CB509D623C2C07, E6053BD673F80E900DDA3593, E6BB0D9B515B50E418D98B9C, + 95A4E72FC64A7338CB259DF7, 3B2C45064E85B3B631D4F921, 156F72179BBFCD0D9804C266, 8F731296532276CBF2B6190D, diff --git a/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj b/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj index fef4486a97..e95c9cbdee 100644 --- a/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj +++ b/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj @@ -185,6 +185,7 @@ + diff --git a/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj b/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj index 1d1c6f7bfd..16e97f17c0 100644 --- a/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj +++ b/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj @@ -185,6 +185,7 @@ + diff --git a/extras/Jucer (experimental)/Jucer.jucer b/extras/Jucer (experimental)/Jucer.jucer index be9986fa81..bae1deadc4 100644 --- a/extras/Jucer (experimental)/Jucer.jucer +++ b/extras/Jucer (experimental)/Jucer.jucer @@ -123,6 +123,8 @@ + setEditable (true, true, false); - label->setColour (Label::backgroundColourId, Colours::white); - label->setColour (Label::outlineColourId, findColour (ComboBox::outlineColourId)); - label->getTextValue().referTo (textValue); - - addAndMakeVisible (proportionButton = new TextButton ("%")); - proportionButton->addButtonListener (this); - - addAndMakeVisible (anchorButton1 = new TextButton (String::empty)); - anchorButton1->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); - anchorButton1->setTriggeredOnMouseDown (true); - anchorButton1->addButtonListener (this); - - addAndMakeVisible (anchorButton2 = new TextButton (String::empty)); - anchorButton2->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); - anchorButton2->setTriggeredOnMouseDown (true); - anchorButton2->addButtonListener (this); - - coordValue.addListener (this); - valueChanged (coordValue); - } - - ~CoordinateEditor() - { - coordValue.removeListener (this); - deleteAllChildren(); - } - - void resized() - { - const Rectangle r (getLookAndFeel().getPropertyComponentContentPosition (*this)); - - label->setBounds (r.getX(), r.getY(), r.getWidth() / 2, r.getHeight() / 2); - proportionButton->setBounds (r.getX() + r.getWidth() / 2, r.getY(), - r.getWidth() / 2, r.getHeight() / 2); - - if (anchorButton2->isVisible()) - { - anchorButton1->setBounds (r.getX(), r.getY() + r.getHeight() / 2, r.getWidth() / 2, r.getHeight() / 2); - anchorButton2->setBounds (r.getX() + r.getWidth() / 2, r.getY() + r.getHeight() / 2, r.getWidth() / 2, r.getHeight() / 2); - } - else - { - anchorButton1->setBounds (r.getX(), r.getY() + r.getHeight() / 2, r.getWidth(), r.getHeight() / 2); - } - } - - void refresh() {} - - void buttonClicked (Button* button) - { - Coordinate coord (getCoordinate()); - - if (button == proportionButton) - { - coord.toggleProportionality (document); - coordValue = coord.toString(); - } - else if (button == anchorButton1) - { - const String marker (pickMarker (anchorButton1, coord.getAnchor1(), true)); - - if (marker.isNotEmpty()) - { - coord.changeAnchor1 (marker, document); - coordValue = coord.toString(); - } - } - else if (button == anchorButton2) - { - const String marker (pickMarker (anchorButton2, coord.getAnchor2(), false)); - - if (marker.isNotEmpty()) - { - coord.changeAnchor2 (marker, document); - coordValue = coord.toString(); - } - } - } - - void valueChanged (Value&) - { - Coordinate coord (getCoordinate()); - - anchorButton1->setButtonText (coord.getAnchor1()); - - anchorButton2->setVisible (coord.isProportional()); - anchorButton2->setButtonText (coord.getAnchor2()); - resized(); - } - - const Coordinate getCoordinate() const - { - return Coordinate (coordValue.toString(), isHorizontal); - } - - virtual const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) = 0; - -protected: - ComponentDocument& document; - Value coordValue, textValue; - Label* label; - TextButton* proportionButton; - TextButton* anchorButton1; - TextButton* anchorButton2; - bool isHorizontal; - - //============================================================================== - class CoordEditableValueSource : public Value::ValueSource, - public Value::Listener - { - public: - CoordEditableValueSource (const Value& sourceValue_, bool isHorizontal_) - : sourceValue (sourceValue_), isHorizontal (isHorizontal_) - { - sourceValue.addListener (this); - } - - ~CoordEditableValueSource() {} - - const var getValue() const - { - Coordinate coord (sourceValue.toString(), isHorizontal); - - if (coord.isProportional()) - return String (coord.getEditableValue()) + "%"; - - return coord.getEditableValue(); - } - - void setValue (const var& newValue) - { - Coordinate coord (sourceValue.toString(), isHorizontal); - coord.setEditableValue ((double) newValue); - - const String newVal (coord.toString()); - if (sourceValue != newVal) - sourceValue = newVal; - } - - void valueChanged (Value&) - { - sendChangeMessage (true); - } - - //============================================================================== - juce_UseDebuggingNewOperator - - protected: - Value sourceValue; - bool isHorizontal; - - CoordEditableValueSource (const CoordEditableValueSource&); - const CoordEditableValueSource& operator= (const CoordEditableValueSource&); - }; -}; - - -//============================================================================== -class ComponentBoundsEditor : public CoordinateEditor +class ComponentBoundsEditor : public CoordinatePropertyComponent { public: enum Type @@ -213,9 +40,9 @@ public: //============================================================================== ComponentBoundsEditor (ComponentDocument& document_, const String& name, Type type_, const ValueTree& compState_, const Value& coordValue_) - : CoordinateEditor (document_, name, - Value (new BoundsCoordValueSource (coordValue_, type_)), - type_ == left || type_ == right), + : CoordinatePropertyComponent (document_, name, + Value (new CoordExtractor (coordValue_, type_)), + type_ == left || type_ == right), type (type_), compState (compState_) { @@ -226,17 +53,17 @@ public: } //============================================================================== - class BoundsCoordValueSource : public Value::ValueSource, - public Value::Listener + class CoordExtractor : public Value::ValueSource, + public Value::Listener { public: - BoundsCoordValueSource (const Value& sourceValue_, Type type_) + CoordExtractor (const Value& sourceValue_, Type type_) : sourceValue (sourceValue_), type (type_) { sourceValue.addListener (this); } - ~BoundsCoordValueSource() {} + ~CoordExtractor() {} const var getValue() const { @@ -286,8 +113,8 @@ public: return getCoordForType (type, r); } - BoundsCoordValueSource (const BoundsCoordValueSource&); - const BoundsCoordValueSource& operator= (const BoundsCoordValueSource&); + CoordExtractor (const CoordExtractor&); + const CoordExtractor& operator= (const CoordExtractor&); }; const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) @@ -295,7 +122,7 @@ public: Coordinate coord (getCoordinate()); PopupMenu m; - document.getComponentMarkerMenuItems (compState, getTypeName(), coord, m, isAnchor1); + document.addComponentMarkerMenuItems (compState, getTypeName(), coord, m, isAnchor1); const int r = m.showAt (button); diff --git a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp index 560b95bf1f..377d500672 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp @@ -25,6 +25,7 @@ #include "jucer_ComponentDocument.h" #include "Component Types/jucer_ComponentTypeManager.h" +#include "../ui/jucer_CoordinatePropertyComponent.h" //============================================================================== @@ -355,38 +356,37 @@ bool ComponentDocument::setCoordsFor (ValueTree& state, const RectangleCoordinat return true; } -void ComponentDocument::addMarkerMenuItem (int i, Coordinate& coord, const String& name, PopupMenu& menu, bool isAnchor1, - const ValueTree& componentState, const String& coordName) +void ComponentDocument::addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu, + bool isAnchor1, const String& fullCoordName) { - const String componentName (componentState [memberNameProperty].toString()); Coordinate requestedCoord (findMarker (name, coord.isHorizontal())); - const String fullCoordName (componentName + "." + coordName); menu.addItem (i, name, ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)), name == (isAnchor1 ? coord.getAnchor1() : coord.getAnchor2())); } -void ComponentDocument::getComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName, +void ComponentDocument::addComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName, Coordinate& coord, PopupMenu& menu, bool isAnchor1) { const String componentName (componentState [memberNameProperty].toString()); + const String fullCoordName (componentName + "." + coordName); if (coord.isHorizontal()) { - addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, componentState, coordName); - addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, componentState, coordName); + addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName); menu.addSeparator(); - addMarkerMenuItem (3, coord, componentName + ".left", menu, isAnchor1, componentState, coordName); - addMarkerMenuItem (4, coord, componentName + ".right", menu, isAnchor1, componentState, coordName); + addMarkerMenuItem (3, coord, componentName + ".left", menu, isAnchor1, fullCoordName); + addMarkerMenuItem (4, coord, componentName + ".right", menu, isAnchor1, fullCoordName); } else { - addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, componentState, coordName); - addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, componentState, coordName); + addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName); menu.addSeparator(); - addMarkerMenuItem (3, coord, componentName + ".top", menu, isAnchor1, componentState, coordName); - addMarkerMenuItem (4, coord, componentName + ".bottom", menu, isAnchor1, componentState, coordName); + addMarkerMenuItem (3, coord, componentName + ".top", menu, isAnchor1, fullCoordName); + addMarkerMenuItem (4, coord, componentName + ".bottom", menu, isAnchor1, fullCoordName); } menu.addSeparator(); @@ -394,7 +394,7 @@ void ComponentDocument::getComponentMarkerMenuItems (const ValueTree& componentS int i; for (i = 0; i < markerList.size(); ++i) - addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, componentState, coordName); + addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName); menu.addSeparator(); for (i = 0; i < getNumComponents(); ++i) @@ -405,13 +405,13 @@ void ComponentDocument::getComponentMarkerMenuItems (const ValueTree& componentS { if (coord.isHorizontal()) { - addMarkerMenuItem (10000 + i * 4, coord, compName + ".left", menu, isAnchor1, componentState, coordName); - addMarkerMenuItem (10001 + i * 4, coord, compName + ".right", menu, isAnchor1, componentState, coordName); + addMarkerMenuItem (10000 + i * 4, coord, compName + ".left", menu, isAnchor1, fullCoordName); + addMarkerMenuItem (10001 + i * 4, coord, compName + ".right", menu, isAnchor1, fullCoordName); } else { - addMarkerMenuItem (10002 + i * 4, coord, compName + ".top", menu, isAnchor1, componentState, coordName); - addMarkerMenuItem (10003 + i * 4, coord, compName + ".bottom", menu, isAnchor1, componentState, coordName); + addMarkerMenuItem (10002 + i * 4, coord, compName + ".top", menu, isAnchor1, fullCoordName); + addMarkerMenuItem (10003 + i * 4, coord, compName + ".bottom", menu, isAnchor1, fullCoordName); } } } @@ -600,9 +600,84 @@ const Coordinate ComponentDocument::MarkerList::findMarker (const String& name, return Coordinate (isX); } +void ComponentDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1) +{ + const String fullCoordName (getName (markerState)); + + if (coord.isHorizontal()) + { + document.addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName); + } + else + { + document.addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName); + } + + menu.addSeparator(); + const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); + + for (int i = 0; i < markerList.size(); ++i) + document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName); +} + +const String ComponentDocument::MarkerList::getChosenMarkerMenuItem (const Coordinate& coord, int i) const +{ + if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName; + if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName; + + const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); + + if (i >= 100 && i < 10000) + return markerList.getName (markerList.getMarker (i - 100)); + + jassertfalse; + return String::empty; +} + +//============================================================================== +class MarkerPositionComponent : public CoordinatePropertyComponent +{ +public: + //============================================================================== + MarkerPositionComponent (ComponentDocument& document_, const String& name, const ValueTree& markerState_, + const Value& coordValue_, bool isHorizontal_) + : CoordinatePropertyComponent (document_, name, coordValue_, isHorizontal_), + markerState (markerState_) + { + } + + ~MarkerPositionComponent() + { + } + + const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) + { + Coordinate coord (getCoordinate()); + + PopupMenu m; + document.getMarkerList (coord.isHorizontal()) + .addMarkerMenuItems (markerState, coord, m, isAnchor1); + + const int r = m.showAt (button); + + if (r > 0) + return document.getMarkerList (coord.isHorizontal()).getChosenMarkerMenuItem (coord, r); + + return String::empty; + } + +private: + ValueTree markerState; +}; + void ComponentDocument::MarkerList::createMarkerProperties (Array & props, ValueTree& marker) { props.add (new TextPropertyComponent (getNameAsValue (marker), "Marker Name", 256, false)); + props.add (new MarkerPositionComponent (document, "Position", marker, + marker.getPropertyAsValue (markerPosProperty, document.getUndoManager()), + contains (marker))); } bool ComponentDocument::MarkerList::createProperties (Array & props, const String& itemId) diff --git a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h index 0077f98933..9af0d4213f 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h +++ b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h @@ -74,7 +74,7 @@ public: // for Coordinate::MarkerResolver: const Coordinate findMarker (const String& name, bool isHorizontal) const; - void getComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName, + void addComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName, Coordinate& coord, PopupMenu& menu, bool isAnchor1); const String getChosenMarkerMenuItem (const ValueTree& componentState, Coordinate& coord, int itemId) const; @@ -105,6 +105,9 @@ public: bool createProperties (Array & props, const String& itemId); void createMarkerProperties (Array & props, ValueTree& marker); + void addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1); + const String getChosenMarkerMenuItem (const Coordinate& coord, int itemId) const; + private: ComponentDocument& document; ValueTree group; @@ -159,8 +162,8 @@ private: void checkRootObject(); void createSubTreeIfNotThere (const String& name); ValueTree getComponentGroup() const; - void addMarkerMenuItem (int i, Coordinate& coord, const String& name, PopupMenu& menu, bool isAnchor1, - const ValueTree& componentState, const String& coordName); + void addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu, + bool isAnchor1, const String& fullCoordName); Value getRootValueUndoable (const var::identifier& name) const { return root.getPropertyAsValue (name, getUndoManager()); } Value getRootValueNonUndoable (const var::identifier& name) const { return root.getPropertyAsValue (name, 0); } diff --git a/extras/Jucer (experimental)/Source/ui/jucer_CoordinatePropertyComponent.h b/extras/Jucer (experimental)/Source/ui/jucer_CoordinatePropertyComponent.h new file mode 100644 index 0000000000..ddeab9da2b --- /dev/null +++ b/extras/Jucer (experimental)/Source/ui/jucer_CoordinatePropertyComponent.h @@ -0,0 +1,205 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-9 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. + + ============================================================================== +*/ + +#ifndef __JUCER_COORDINATEPROPERTYCOMPONENT_H_1128AA3D__ +#define __JUCER_COORDINATEPROPERTYCOMPONENT_H_1128AA3D__ + + +//============================================================================== +class CoordinatePropertyComponent : public PropertyComponent, + public ButtonListener, + public Value::Listener +{ +public: + //============================================================================== + CoordinatePropertyComponent (ComponentDocument& document_, const String& name, + const Value& coordValue_, bool isHorizontal_) + : PropertyComponent (name, 40), document (document_), + coordValue (coordValue_), + textValue (Value (new CoordEditableValueSource (coordValue_, isHorizontal_))), + isHorizontal (isHorizontal_) + { + addAndMakeVisible (label = new Label (String::empty, String::empty)); + + label->setEditable (true, true, false); + label->setColour (Label::backgroundColourId, Colours::white); + label->setColour (Label::outlineColourId, findColour (ComboBox::outlineColourId)); + label->getTextValue().referTo (textValue); + + addAndMakeVisible (proportionButton = new TextButton ("%")); + proportionButton->addButtonListener (this); + + addAndMakeVisible (anchorButton1 = new TextButton (String::empty)); + anchorButton1->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); + anchorButton1->setTriggeredOnMouseDown (true); + anchorButton1->addButtonListener (this); + + addAndMakeVisible (anchorButton2 = new TextButton (String::empty)); + anchorButton2->setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnTop | Button::ConnectedOnRight | Button::ConnectedOnBottom); + anchorButton2->setTriggeredOnMouseDown (true); + anchorButton2->addButtonListener (this); + + coordValue.addListener (this); + valueChanged (coordValue); + } + + ~CoordinatePropertyComponent() + { + coordValue.removeListener (this); + deleteAllChildren(); + } + + void resized() + { + const Rectangle r (getLookAndFeel().getPropertyComponentContentPosition (*this)); + + label->setBounds (r.getX(), r.getY(), r.getWidth() / 2, r.getHeight() / 2); + proportionButton->setBounds (r.getX() + r.getWidth() / 2, r.getY(), + r.getWidth() / 2, r.getHeight() / 2); + + if (anchorButton2->isVisible()) + { + anchorButton1->setBounds (r.getX(), r.getY() + r.getHeight() / 2, r.getWidth() / 2, r.getHeight() / 2); + anchorButton2->setBounds (r.getX() + r.getWidth() / 2, r.getY() + r.getHeight() / 2, r.getWidth() / 2, r.getHeight() / 2); + } + else + { + anchorButton1->setBounds (r.getX(), r.getY() + r.getHeight() / 2, r.getWidth(), r.getHeight() / 2); + } + } + + void refresh() {} + + void buttonClicked (Button* button) + { + Coordinate coord (getCoordinate()); + + if (button == proportionButton) + { + coord.toggleProportionality (document); + coordValue = coord.toString(); + } + else if (button == anchorButton1) + { + const String marker (pickMarker (anchorButton1, coord.getAnchor1(), true)); + + if (marker.isNotEmpty()) + { + coord.changeAnchor1 (marker, document); + coordValue = coord.toString(); + } + } + else if (button == anchorButton2) + { + const String marker (pickMarker (anchorButton2, coord.getAnchor2(), false)); + + if (marker.isNotEmpty()) + { + coord.changeAnchor2 (marker, document); + coordValue = coord.toString(); + } + } + } + + void valueChanged (Value&) + { + Coordinate coord (getCoordinate()); + + anchorButton1->setButtonText (coord.getAnchor1()); + + anchorButton2->setVisible (coord.isProportional()); + anchorButton2->setButtonText (coord.getAnchor2()); + resized(); + } + + const Coordinate getCoordinate() const + { + return Coordinate (coordValue.toString(), isHorizontal); + } + + virtual const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) = 0; + +protected: + ComponentDocument& document; + Value coordValue, textValue; + Label* label; + TextButton* proportionButton; + TextButton* anchorButton1; + TextButton* anchorButton2; + bool isHorizontal; + + //============================================================================== + class CoordEditableValueSource : public Value::ValueSource, + public Value::Listener + { + public: + CoordEditableValueSource (const Value& sourceValue_, bool isHorizontal_) + : sourceValue (sourceValue_), isHorizontal (isHorizontal_) + { + sourceValue.addListener (this); + } + + ~CoordEditableValueSource() {} + + const var getValue() const + { + Coordinate coord (sourceValue.toString(), isHorizontal); + + if (coord.isProportional()) + return String (coord.getEditableValue()) + "%"; + + return coord.getEditableValue(); + } + + void setValue (const var& newValue) + { + Coordinate coord (sourceValue.toString(), isHorizontal); + coord.setEditableValue ((double) newValue); + + const String newVal (coord.toString()); + if (sourceValue != newVal) + sourceValue = newVal; + } + + void valueChanged (Value&) + { + sendChangeMessage (true); + } + + //============================================================================== + juce_UseDebuggingNewOperator + + protected: + Value sourceValue; + bool isHorizontal; + + CoordEditableValueSource (const CoordEditableValueSource&); + const CoordEditableValueSource& operator= (const CoordEditableValueSource&); + }; +}; + + + +#endif // __JUCER_COORDINATEPROPERTYCOMPONENT_H_1128AA3D__ diff --git a/juce_Config.h b/juce_Config.h index a2f8ad1d1a..13794ca42a 100644 --- a/juce_Config.h +++ b/juce_Config.h @@ -45,7 +45,7 @@ it to be true or false. */ #ifndef JUCE_FORCE_DEBUG - #define JUCE_FORCE_DEBUG 0 + //#define JUCE_FORCE_DEBUG 0 #endif //============================================================================= diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 328c712ad6..38f574497e 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -168,7 +168,7 @@ #endif #ifndef JUCE_FORCE_DEBUG - #define JUCE_FORCE_DEBUG 0 + //#define JUCE_FORCE_DEBUG 0 #endif #ifndef JUCE_LOG_ASSERTIONS @@ -1892,7 +1892,7 @@ const String Time::toString (const bool includeDate, return result.trimEnd(); } -const String Time::formatted (const juce_wchar* const format) const throw() +const String Time::formatted (const String& format) const throw() { String buffer; int bufferSize = 128; @@ -5464,77 +5464,21 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_DirectoryIterator.cpp ***/ BEGIN_JUCE_NAMESPACE -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly); -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly); -void juce_findFileClose (void* handle); - DirectoryIterator::DirectoryIterator (const File& directory, - bool isRecursive, - const String& wc, + bool isRecursive_, + const String& wildCard_, const int whatToLookFor_) - : wildCard (wc), + : fileFinder (directory, isRecursive ? "*" : wildCard_), + wildCard (wildCard_), + path (File::addTrailingSeparator (directory.getFullPathName())), index (-1), - whatToLookFor (whatToLookFor_) + totalNumFiles (-1), + whatToLookFor (whatToLookFor_), + isRecursive (isRecursive_) { // you have to specify the type of files you're looking for! jassert ((whatToLookFor_ & (File::findFiles | File::findDirectories)) != 0); jassert (whatToLookFor_ > 0 && whatToLookFor_ <= 7); - - String path (directory.getFullPathName()); - if (! path.endsWithChar (File::separator)) - path += File::separator; - - String filename; - bool isDirectory, isHidden; - - void* const handle = juce_findFileStart (path, - isRecursive ? "*" : wc, - filename, &isDirectory, &isHidden, 0, 0, 0, 0); - - if (handle != 0) - { - do - { - if (! filename.containsOnly (".")) - { - bool addToList = false; - - if (isDirectory) - { - if (isRecursive - && ((whatToLookFor_ & File::ignoreHiddenFiles) == 0 - || ! isHidden)) - { - dirsFound.add (File (path + filename, 0)); - } - - addToList = (whatToLookFor_ & File::findDirectories) != 0; - } - else - { - addToList = (whatToLookFor_ & File::findFiles) != 0; - } - - // if it's recursive, we're not relying on the OS iterator - // to do the wildcard match, so do it now.. - if (isRecursive && addToList) - addToList = filename.matchesWildcard (wc, true); - - if (addToList && (whatToLookFor_ & File::ignoreHiddenFiles) != 0) - addToList = ! isHidden; - - if (addToList) - filesFound.add (File (path + filename, 0)); - } - - } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0)); - - juce_findFileClose (handle); - } } DirectoryIterator::~DirectoryIterator() @@ -5542,28 +5486,67 @@ DirectoryIterator::~DirectoryIterator() } bool DirectoryIterator::next() +{ + return next (0, 0, 0, 0, 0, 0); +} + +bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResult, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) { if (subIterator != 0) { - if (subIterator->next()) + if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly)) return true; subIterator = 0; } - if (index >= filesFound.size() + dirsFound.size() - 1) - return false; - - ++index; - - if (index >= filesFound.size()) + String filename; + bool isDirectory, isHidden; + while (fileFinder.next (filename, &isDirectory, &isHidden, fileSize, modTime, creationTime, isReadOnly)) { - subIterator = new DirectoryIterator (dirsFound.getReference (index - filesFound.size()), - true, wildCard, whatToLookFor); - return next(); + ++index; + + if (! filename.containsOnly (".")) + { + const File fileFound (path + filename, 0); + bool matches = false; + + if (isDirectory) + { + if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden)) + subIterator = new DirectoryIterator (fileFound, true, wildCard, whatToLookFor); + + matches = (whatToLookFor & File::findDirectories) != 0; + } + else + { + matches = (whatToLookFor & File::findFiles) != 0; + } + + // if recursive, we're not relying on the OS iterator to do the wildcard match, so do it now.. + if (matches && isRecursive) + matches = filename.matchesWildcard (wildCard, ! File::areFileNamesCaseSensitive()); + + if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0) + matches = ! isHidden; + + if (matches) + { + currentFile = fileFound; + if (isHiddenResult != 0) *isHiddenResult = isHidden; + if (isDirResult != 0) *isDirResult = isDirectory; + + return true; + } + else if (subIterator != 0) + { + return next(); + } + } } - return true; + return false; } const File DirectoryIterator::getFile() const @@ -5571,22 +5554,21 @@ const File DirectoryIterator::getFile() const if (subIterator != 0) return subIterator->getFile(); - return filesFound [index]; + return currentFile; } float DirectoryIterator::getEstimatedProgress() const { - if (filesFound.size() + dirsFound.size() == 0) - { - return 0.0f; - } - else - { - const float detailedIndex = (subIterator != 0) ? index + subIterator->getEstimatedProgress() - : (float) index; + if (totalNumFiles < 0) + totalNumFiles = File (path).getNumberOfChildFiles (File::findFilesAndDirectories); - return detailedIndex / (filesFound.size() + dirsFound.size()); - } + if (totalNumFiles <= 0) + return 0.0f; + + const float detailedIndex = (subIterator != 0) ? index + subIterator->getEstimatedProgress() + : (float) index; + + return detailedIndex / totalNumFiles; } END_JUCE_NAMESPACE @@ -5594,165 +5576,20 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_File.cpp ***/ -#ifdef _MSC_VER - #pragma warning (disable: 4514) - #pragma warning (push) -#endif - #if ! JUCE_WINDOWS #include #endif BEGIN_JUCE_NAMESPACE -#ifdef _MSC_VER - #pragma warning (pop) -#endif - -void* juce_fileOpen (const String& path, bool forWriting); -void juce_fileClose (void* handle); -int juce_fileWrite (void* handle, const void* buffer, int size); -int64 juce_fileGetPosition (void* handle); -int64 juce_fileSetPosition (void* handle, int64 pos); -void juce_fileFlush (void* handle); - -bool juce_fileExists (const String& fileName, const bool dontCountDirectories); -bool juce_isDirectory (const String& fileName); -int64 juce_getFileSize (const String& fileName); bool juce_canWriteToFile (const String& fileName); bool juce_setFileReadOnly (const String& fileName, bool isReadOnly); - void juce_getFileTimes (const String& fileName, int64& modificationTime, int64& accessTime, int64& creationTime); bool juce_setFileTimes (const String& fileName, int64 modificationTime, int64 accessTime, int64 creationTime); - -bool juce_deleteFile (const String& fileName); bool juce_copyFile (const String& source, const String& dest); bool juce_moveFile (const String& source, const String& dest); - -// this must also create all paths involved in the directory. -void juce_createDirectory (const String& fileName); - bool juce_launchFile (const String& fileName, const String& parameters); -const StringArray juce_getFileSystemRoots(); -const String juce_getVolumeLabel (const String& filenameOnVolume, int& volumeSerialNumber); - -// starts a directory search operation with a wildcard, returning a handle for -// use in calls to juce_findFileNext. -// juce_firstResultFile gets the name of the file (not the whole pathname) and -// the other pointers, if non-null, are set based on the properties of the file. -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, - Time* creationTime, bool* isReadOnly); - -// returns false when no more files are found -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly); - -void juce_findFileClose (void* handle); - -static const String juce_addTrailingSeparator (const String& path) -{ - return path.endsWithChar (File::separator) ? path - : path + File::separator; -} - -static const String parseAbsolutePath (String path) -{ - if (path.isEmpty()) - return String::empty; - -#if JUCE_WINDOWS - // Windows.. - path = path.replaceCharacter ('/', '\\'); - - if (path.startsWithChar (File::separator)) - { - if (path[1] != File::separator) - { - /* When you supply a raw string to the File object constructor, it must be an absolute path. - If you're trying to parse a string that may be either a relative path or an absolute path, - you MUST provide a context against which the partial path can be evaluated - you can do - this by simply using File::getChildFile() instead of the File constructor. E.g. saying - "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute - path if that's what was supplied, or would evaluate a partial path relative to the CWD. - */ - jassertfalse - - path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; - } - } - else if (path.indexOfChar (':') < 0) - { - if (path.isEmpty()) - return String::empty; - - /* When you supply a raw string to the File object constructor, it must be an absolute path. - If you're trying to parse a string that may be either a relative path or an absolute path, - you MUST provide a context against which the partial path can be evaluated - you can do - this by simply using File::getChildFile() instead of the File constructor. E.g. saying - "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute - path if that's what was supplied, or would evaluate a partial path relative to the CWD. - */ - jassertfalse - - return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); - } -#else - // Mac or Linux.. - path = path.replaceCharacter ('\\', '/'); - - if (path.startsWithChar ('~')) - { - const char* homeDir = 0; - - if (path[1] == File::separator || path[1] == 0) - { - // expand a name of the form "~/abc" - path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() - + path.substring (1); - } - else - { - // expand a name of type "~dave/abc" - const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); - - struct passwd* const pw = getpwnam (userName.toUTF8()); - if (pw != 0) - { - String home (homeDir); - - if (home.endsWithChar (File::separator)) - home [home.length() - 1] = 0; - - path = String (pw->pw_dir) - + path.substring (userName.length()); - } - } - } - else if (! path.startsWithChar (File::separator)) - { - /* When you supply a raw string to the File object constructor, it must be an absolute path. - If you're trying to parse a string that may be either a relative path or an absolute path, - you MUST provide a context against which the partial path can be evaluated - you can do - this by simply using File::getChildFile() instead of the File constructor. E.g. saying - "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute - path if that's what was supplied, or would evaluate a partial path relative to the CWD. - */ - jassert (path.startsWith ("./") || path.startsWith ("../")); // (assume that a path "./xyz" is deliberately intended to be relative to the CWD) - - return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); - } -#endif - - int len = path.length(); - while (--len > 0 && path [len] == File::separator) - path [len] = 0; - - return path; -} - const File File::nonexistent; File::File (const String& fullPathName) @@ -5787,6 +5624,90 @@ File& File::operator= (const File& other) return *this; } +const String File::parseAbsolutePath (const String& p) +{ + if (p.isEmpty()) + return String::empty; + +#if JUCE_WINDOWS + // Windows.. + String path (p.replaceCharacter ('/', '\\')); + + if (path.startsWithChar (File::separator)) + { + if (path[1] != File::separator) + { + /* When you supply a raw string to the File object constructor, it must be an absolute path. + If you're trying to parse a string that may be either a relative path or an absolute path, + you MUST provide a context against which the partial path can be evaluated - you can do + this by simply using File::getChildFile() instead of the File constructor. E.g. saying + "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute + path if that's what was supplied, or would evaluate a partial path relative to the CWD. + */ + jassertfalse + + path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; + } + } + else if (! path.containsChar (':')) + { + /* When you supply a raw string to the File object constructor, it must be an absolute path. + If you're trying to parse a string that may be either a relative path or an absolute path, + you MUST provide a context against which the partial path can be evaluated - you can do + this by simply using File::getChildFile() instead of the File constructor. E.g. saying + "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute + path if that's what was supplied, or would evaluate a partial path relative to the CWD. + */ + jassertfalse + + return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); + } +#else + // Mac or Linux.. + String path (p.replaceCharacter ('\\', '/')); + + if (path.startsWithChar ('~')) + { + if (path[1] == File::separator || path[1] == 0) + { + // expand a name of the form "~/abc" + path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() + + path.substring (1); + } + else + { + // expand a name of type "~dave/abc" + const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); + + struct passwd* const pw = getpwnam (userName.toUTF8()); + if (pw != 0) + path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false); + } + } + else if (! path.startsWithChar (File::separator)) + { + /* When you supply a raw string to the File object constructor, it must be an absolute path. + If you're trying to parse a string that may be either a relative path or an absolute path, + you MUST provide a context against which the partial path can be evaluated - you can do + this by simply using File::getChildFile() instead of the File constructor. E.g. saying + "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute + path if that's what was supplied, or would evaluate a partial path relative to the CWD. + */ + jassert (path.startsWith ("./") || path.startsWith ("../")); // (assume that a path "./xyz" is deliberately intended to be relative to the CWD) + + return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); + } +#endif + + return path.trimCharactersAtEnd (separatorString); +} + +const String File::addTrailingSeparator (const String& path) +{ + return path.endsWithChar (File::separator) ? path + : path + File::separator; +} + #if JUCE_LINUX #define NAMES_ARE_CASE_SENSITIVE 1 #endif @@ -5802,7 +5723,6 @@ bool File::areFileNamesCaseSensitive() bool File::operator== (const File& other) const { - // case-insensitive on Windows, but not on linux. #if NAMES_ARE_CASE_SENSITIVE return fullPath == other.fullPath; #else @@ -5815,19 +5735,22 @@ bool File::operator!= (const File& other) const return ! operator== (other); } -bool File::exists() const +bool File::operator< (const File& other) const { - return juce_fileExists (fullPath, false); +#if NAMES_ARE_CASE_SENSITIVE + return fullPath < other.fullPath; +#else + return fullPath.compareIgnoreCase (other.fullPath) < 0; +#endif } -bool File::existsAsFile() const +bool File::operator> (const File& other) const { - return juce_fileExists (fullPath, true); -} - -bool File::isDirectory() const -{ - return juce_isDirectory (fullPath); +#if NAMES_ARE_CASE_SENSITIVE + return fullPath > other.fullPath; +#else + return fullPath.compareIgnoreCase (other.fullPath) > 0; +#endif } bool File::hasWriteAccess() const @@ -5865,12 +5788,6 @@ bool File::setReadOnly (const bool shouldBeReadOnly, return juce_setFileReadOnly (fullPath, shouldBeReadOnly) && worked; } -bool File::deleteFile() const -{ - return (! exists()) - || juce_deleteFile (fullPath); -} - bool File::deleteRecursively() const { bool worked = true; @@ -6062,7 +5979,7 @@ const File File::getChildFile (String relativePath) const } } - return File (juce_addTrailingSeparator (path) + relativePath); + return File (addTrailingSeparator (path) + relativePath); } } @@ -6071,11 +5988,6 @@ const File File::getSiblingFile (const String& fileName) const return getParentDirectory().getChildFile (fileName); } -int64 File::getSize() const -{ - return juce_getFileSize (fullPath); -} - const String File::descriptionOfSizeInBytes (const int64 bytes) { if (bytes == 1) @@ -6102,22 +6014,19 @@ const String File::descriptionOfSizeInBytes (const int64 bytes) bool File::create() const { - if (! exists()) + if (exists()) + return true; + { const File parentDir (getParentDirectory()); if (parentDir == *this || ! parentDir.createDirectory()) return false; - void* const fh = juce_fileOpen (fullPath, true); - - if (fh == 0) - return false; - - juce_fileClose (fh); + FileOutputStream fo (*this, 8); } - return true; + return exists(); } bool File::createDirectory() const @@ -6129,13 +6038,7 @@ bool File::createDirectory() const if (parentDir == *this || ! parentDir.createDirectory()) return false; - String dir (fullPath); - - while (dir.endsWithChar (separator)) - dir [dir.length() - 1] = 0; - - juce_createDirectory (dir); - + createDirectoryInternal (fullPath.trimCharactersAtEnd (separatorString)); return isDirectory(); } @@ -6196,14 +6099,11 @@ const String File::loadFileAsString() const return in.readEntireStreamAsString(); } -static inline bool fileTypeMatches (const int whatToLookFor, - const bool isDir, - const bool isHidden) +bool File::fileTypeMatches (const int whatToLookFor, const bool isDir, const bool isHidden) { - return (whatToLookFor & (isDir ? File::findDirectories - : File::findFiles)) != 0 - && ((! isHidden) - || (whatToLookFor & File::ignoreHiddenFiles) == 0); + return (whatToLookFor & (isDir ? findDirectories + : findFiles)) != 0 + && ((! isHidden) || (whatToLookFor & File::ignoreHiddenFiles) == 0); } int File::findChildFiles (Array& results, @@ -6216,50 +6116,32 @@ int File::findChildFiles (Array& results, int total = 0; - // find child files or directories in this directory first.. if (isDirectory()) { - const String path (juce_addTrailingSeparator (fullPath)); - - String filename; + // find child files or directories in this directory first.. + String path (addTrailingSeparator (fullPath)), filename; bool itemIsDirectory, itemIsHidden; - void* const handle = juce_findFileStart (path, wildCardPattern, filename, - &itemIsDirectory, &itemIsHidden, - 0, 0, 0, 0); + DirectoryIterator::NativeIterator i (path, wildCardPattern); - if (handle != 0) + while (i.next (filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)) { - do + if (! filename.containsOnly (".")) { - if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden) - && ! filename.containsOnly (".")) + const File fileFound (path + filename, 0); + + if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden)) { - results.add (File (path + filename, 0)); + results.add (fileFound); ++total; } - } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)); - - juce_findFileClose (handle); - } - } - else - { - // trying to search for files inside a non-directory? - //jassertfalse - } - - // and recurse down if required. - if (searchRecursively) - { - Array subDirectories; - findChildFiles (subDirectories, File::findDirectories, false); - - for (int i = 0; i < subDirectories.size(); ++i) - { - total += subDirectories.getReference(i).findChildFiles (results, whatToLookFor, - true, wildCardPattern); + if (searchRecursively && itemIsDirectory + && fileTypeMatches (whatToLookFor | findDirectories, true, itemIsHidden)) + { + total += fileFound.findChildFiles (results, whatToLookFor, true, wildCardPattern); + } + } } } @@ -6279,24 +6161,12 @@ int File::getNumberOfChildFiles (const int whatToLookFor, String filename; bool itemIsDirectory, itemIsHidden; - void* const handle = juce_findFileStart (fullPath, wildCardPattern, filename, - &itemIsDirectory, &itemIsHidden, - 0, 0, 0, 0); + DirectoryIterator::NativeIterator i (*this, wildCardPattern); - if (handle != 0) - { - do - { - if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden) - && ! filename.containsOnly (".")) - { - ++count; - } - - } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)); - - juce_findFileClose (handle); - } + while (i.next (filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)) + if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden) + && ! filename.containsOnly (".")) + ++count; } else { @@ -6309,33 +6179,19 @@ int File::getNumberOfChildFiles (const int whatToLookFor, bool File::containsSubDirectories() const { - bool result = false; - if (isDirectory()) { String filename; - bool itemIsDirectory, itemIsHidden; - void* const handle = juce_findFileStart (juce_addTrailingSeparator (fullPath), - "*", filename, - &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0); + bool itemIsDirectory; - if (handle != 0) - { - do - { - if (itemIsDirectory) - { - result = true; - break; - } + DirectoryIterator::NativeIterator i (*this, "*"); - } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)); - - juce_findFileClose (handle); - } + while (i.next (filename, &itemIsDirectory, 0, 0, 0, 0, 0)) + if (itemIsDirectory) + return true; } - return result; + return false; } const File File::getNonexistentChildFile (const String& prefix_, @@ -6585,8 +6441,8 @@ const String File::getRelativePathFrom (const File& dir) const thisPath [len] = 0; } - String dirPath (juce_addTrailingSeparator ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName() - : dir.fullPath)); + String dirPath (addTrailingSeparator (dir.existsAsFile() ? dir.getParentDirectory().getFullPathName() + : dir.fullPath)); const int len = jmin (thisPath.length(), dirPath.length()); int commonBitLength = 0; @@ -6636,28 +6492,6 @@ const String File::getRelativePathFrom (const File& dir) const return thisPath; } -void File::findFileSystemRoots (Array& destArray) -{ - const StringArray roots (juce_getFileSystemRoots()); - - for (int i = 0; i < roots.size(); ++i) - destArray.add (File (roots[i])); -} - -const String File::getVolumeLabel() const -{ - int serialNum; - return juce_getVolumeLabel (fullPath, serialNum); -} - -int File::getVolumeSerialNumber() const -{ - int serialNum; - juce_getVolumeLabel (fullPath, serialNum); - - return serialNum; -} - const File File::createTempFile (const String& fileNameEnding) { const File tempFile (getSpecialLocation (tempDirectory) @@ -6677,7 +6511,7 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_FileInputStream.cpp ***/ BEGIN_JUCE_NAMESPACE -void* juce_fileOpen (const String& path, bool forWriting); +void* juce_fileOpen (const File& file, bool forWriting); void juce_fileClose (void* handle); int juce_fileRead (void* handle, void* buffer, int size); int64 juce_fileSetPosition (void* handle, int64 pos); @@ -6689,7 +6523,7 @@ FileInputStream::FileInputStream (const File& f) { totalSize = f.getSize(); - fileHandle = juce_fileOpen (f.getFullPathName(), false); + fileHandle = juce_fileOpen (f, false); } FileInputStream::~FileInputStream() @@ -6747,11 +6581,9 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_FileOutputStream.cpp ***/ BEGIN_JUCE_NAMESPACE -void* juce_fileOpen (const String& path, bool forWriting); +void* juce_fileOpen (const File& file, bool forWriting); void juce_fileClose (void* handle); int juce_fileWrite (void* handle, const void* buffer, int size); -void juce_fileFlush (void* handle); -int64 juce_fileGetPosition (void* handle); int64 juce_fileSetPosition (void* handle, int64 pos); FileOutputStream::FileOutputStream (const File& f, @@ -6760,11 +6592,11 @@ FileOutputStream::FileOutputStream (const File& f, bufferSize (bufferSize_), bytesInBuffer (0) { - fileHandle = juce_fileOpen (f.getFullPathName(), true); + fileHandle = juce_fileOpen (f, true); if (fileHandle != 0) { - currentPosition = juce_fileGetPosition (fileHandle); + currentPosition = getPositionInternal(); if (currentPosition < 0) { @@ -6808,7 +6640,7 @@ void FileOutputStream::flush() bytesInBuffer = 0; } - juce_fileFlush (fileHandle); + flushInternal(); } bool FileOutputStream::write (const void* const src, const int numBytes) @@ -11634,10 +11466,7 @@ const String String::trimCharactersAtStart (const String& charactersToTrim) cons while (charactersToTrim.containsChar (*t)) ++t; - if (t == text) - return *this; - - return String (t); + return t == text ? *this : String (t); } const String String::trimCharactersAtEnd (const String& charactersToTrim) const @@ -11645,12 +11474,17 @@ const String String::trimCharactersAtEnd (const String& charactersToTrim) const if (isEmpty()) return empty; - const juce_wchar* endT = text + (length() - 1); + const int len = length(); + const juce_wchar* endT = text + (len - 1); + int numToRemove = 0; - while (endT >= text && charactersToTrim.containsChar (*endT)) + while (numToRemove < len && charactersToTrim.containsChar (*endT)) + { + ++numToRemove; --endT; + } - return String (text, (int) (++endT - text)); + return numToRemove > 0 ? String (text, len - numToRemove) : *this; } const String String::retainCharacters (const String& charactersToRetain) const @@ -12699,10 +12533,10 @@ void StringArray::appendNumbersToDuplicates (const bool ignoreCase, const juce_wchar* postNumberString) { if (preNumberString == 0) - preNumberString = T(" ("); + preNumberString = L" ("; if (postNumberString == 0) - postNumberString = T(")"); + postNumberString = L")"; for (int i = 0; i < size() - 1; ++i) { @@ -13591,9 +13425,7 @@ const String XmlDocument::expandExternalEntity (const String& entity) { if (dtdText.isNotEmpty()) { - while (dtdText.endsWithChar ('>')) - dtdText = dtdText.dropLastCharacters (1); - + dtdText = dtdText.trimCharactersAtEnd (">"); tokenisedDTD.addTokens (dtdText, true); if (tokenisedDTD [tokenisedDTD.size() - 2].equalsIgnoreCase ("system") @@ -13645,12 +13477,7 @@ const String XmlDocument::expandExternalEntity (const String& entity) { if (tokenisedDTD[i - 1].equalsIgnoreCase ("')) - ent = ent.dropLastCharacters (1); - - ent = ent.trim().unquoted(); + String ent (tokenisedDTD [i + 1].trimCharactersAtEnd (">").trim().unquoted()); // check for sub-entities.. int ampersand = ent.indexOfChar ('&'); @@ -13693,24 +13520,12 @@ const String XmlDocument::getParameterEntity (const String& entity) if (tokenisedDTD [i - 1] == "%" && tokenisedDTD [i - 2].equalsIgnoreCase ("')) - ent = ent.dropLastCharacters (1); + const String ent (tokenisedDTD [i + 1].trimCharactersAtEnd (">")); if (ent.equalsIgnoreCase ("system")) - { - String filename (tokenisedDTD [i + 2]); - - while (filename.endsWithChar ('>')) - filename = filename.dropLastCharacters (1); - - return getFileContents (filename); - } + return getFileContents (tokenisedDTD [i + 2].trimCharactersAtEnd (">")); else - { return ent.trim().unquoted(); - } } } } @@ -14726,7 +14541,7 @@ bool XmlElement::isTextElement() const throw() return tagName.isEmpty(); } -static const juce_wchar* const juce_xmltextContentAttributeName = T("text"); +static const juce_wchar* const juce_xmltextContentAttributeName = L"text"; const String XmlElement::getText() const throw() { @@ -21400,8 +21215,8 @@ const StringPairArray WavAudioFormat::createBWAVMetadata (const String& descript m.set (bwavDescription, description); m.set (bwavOriginator, originator); m.set (bwavOriginatorRef, originatorRef); - m.set (bwavOriginationDate, date.formatted (T("%Y-%m-%d"))); - m.set (bwavOriginationTime, date.formatted (T("%H:%M:%S"))); + m.set (bwavOriginationDate, date.formatted ("%Y-%m-%d")); + m.set (bwavOriginationTime, date.formatted ("%H:%M:%S")); m.set (bwavTimeReference, String (timeReferenceSamples)); m.set (bwavCodingHistory, codingHistory); @@ -56342,21 +56157,11 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_DirectoryContentsList.cpp ***/ BEGIN_JUCE_NAMESPACE -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, - Time* creationTime, bool* isReadOnly); -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly); -void juce_findFileClose (void* handle); - DirectoryContentsList::DirectoryContentsList (const FileFilter* const fileFilter_, TimeSliceThread& thread_) : fileFilter (fileFilter_), thread (thread_), - includeDirectories (false), - includeFiles (false), - ignoreHiddenFiles (true), + fileTypeFlags (File::ignoreHiddenFiles | File::findFiles), fileFindHandle (0), shouldStop (true) { @@ -56369,7 +56174,13 @@ DirectoryContentsList::~DirectoryContentsList() void DirectoryContentsList::setIgnoresHiddenFiles (const bool shouldIgnoreHiddenFiles) { - ignoreHiddenFiles = shouldIgnoreHiddenFiles; + setTypeFlags (shouldIgnoreHiddenFiles ? (fileTypeFlags | File::ignoreHiddenFiles) + : (fileTypeFlags & ~File::ignoreHiddenFiles)); +} + +bool DirectoryContentsList::ignoresHiddenFiles() const +{ + return (fileTypeFlags & File::ignoreHiddenFiles) != 0; } const File& DirectoryContentsList::getDirectory() const @@ -56378,19 +56189,32 @@ const File& DirectoryContentsList::getDirectory() const } void DirectoryContentsList::setDirectory (const File& directory, - const bool includeDirectories_, - const bool includeFiles_) + const bool includeDirectories, + const bool includeFiles) { - if (directory != root - || includeDirectories != includeDirectories_ - || includeFiles != includeFiles_) + jassert (includeDirectories || includeFiles); // you have to speciify at least one of these! + + if (directory != root) { clear(); - root = directory; - includeDirectories = includeDirectories_; - includeFiles = includeFiles_; + // (this forces a refresh when setTypeFlags() is called, rather than triggering two refreshes) + fileTypeFlags &= ~(File::findDirectories | File::findFiles); + } + + int newFlags = fileTypeFlags; + if (includeDirectories) newFlags |= File::findDirectories; else newFlags &= ~File::findDirectories; + if (includeFiles) newFlags |= File::findFiles; else newFlags &= ~File::findFiles; + + setTypeFlags (newFlags); +} + +void DirectoryContentsList::setTypeFlags (const int newFlags) +{ + if (fileTypeFlags != newFlags) + { + fileTypeFlags = newFlags; refresh(); } } @@ -56400,11 +56224,7 @@ void DirectoryContentsList::clear() shouldStop = true; thread.removeTimeSliceClient (this); - if (fileFindHandle != 0) - { - juce_findFileClose (fileFindHandle); - fileFindHandle = 0; - } + fileFindHandle = 0; if (files.size() > 0) { @@ -56419,36 +56239,8 @@ void DirectoryContentsList::refresh() if (root.isDirectory()) { - String fileFound; - bool fileFoundIsDir, isHidden, isReadOnly; - int64 fileSize; - Time modTime, creationTime; - - String path (root.getFullPathName()); - if (! path.endsWithChar (File::separator)) - path += File::separator; - - jassert (fileFindHandle == 0); - - fileFindHandle = juce_findFileStart (path, "*", fileFound, - &fileFoundIsDir, - &isHidden, - &fileSize, - &modTime, - &creationTime, - &isReadOnly); - - if (fileFindHandle != 0 && fileFound.isNotEmpty()) - { - if (addFile (fileFound, fileFoundIsDir, isHidden, - fileSize, modTime, creationTime, isReadOnly)) - { - changed(); - } - } - + fileFindHandle = new DirectoryIterator (root, false, "*", fileTypeFlags); shouldStop = false; - thread.addTimeSliceClient (this); } } @@ -56523,20 +56315,15 @@ bool DirectoryContentsList::checkNextFile (bool& hasChanged) { if (fileFindHandle != 0) { - String fileFound; bool fileFoundIsDir, isHidden, isReadOnly; int64 fileSize; Time modTime, creationTime; - if (juce_findFileNext (fileFindHandle, fileFound, - &fileFoundIsDir, &isHidden, - &fileSize, - &modTime, - &creationTime, - &isReadOnly)) + if (fileFindHandle->next (&fileFoundIsDir, &isHidden, &fileSize, + &modTime, &creationTime, &isReadOnly)) { - if (addFile (fileFound, fileFoundIsDir, isHidden, fileSize, - modTime, creationTime, isReadOnly)) + if (addFile (fileFindHandle->getFile(), fileFoundIsDir, + fileSize, modTime, creationTime, isReadOnly)) { hasChanged = true; } @@ -56545,7 +56332,6 @@ bool DirectoryContentsList::checkNextFile (bool& hasChanged) } else { - juce_findFileClose (fileFindHandle); fileFindHandle = 0; } } @@ -56564,29 +56350,20 @@ int DirectoryContentsList::compareElements (const DirectoryContentsList::FileInf return first->filename.compareIgnoreCase (second->filename); } -bool DirectoryContentsList::addFile (const String& filename, +bool DirectoryContentsList::addFile (const File& file, const bool isDir, - const bool isHidden, const int64 fileSize, const Time& modTime, const Time& creationTime, const bool isReadOnly) { - if (filename == ".." - || filename == "." - || (ignoreHiddenFiles && isHidden)) - return false; - - const File file (root.getChildFile (filename)); - - if (((isDir && includeDirectories) || ((! isDir) && includeFiles)) - && (fileFilter == 0 - || ((! isDir) && fileFilter->isFileSuitable (file)) - || (isDir && fileFilter->isDirectorySuitable (file)))) + if (fileFilter == 0 + || ((! isDir) && fileFilter->isFileSuitable (file)) + || (isDir && fileFilter->isDirectorySuitable (file))) { ScopedPointer info (new FileInfo()); - info->filename = filename; + info->filename = file.getFileName(); info->fileSize = fileSize; info->modificationTime = modTime; info->creationTime = creationTime; @@ -56803,7 +56580,7 @@ void FileBrowserComponent::setRoot (const File& newRootDirectory) String path (newRootDirectory.getFullPathName()); if (path.isEmpty()) - path += File::separator; + path = File::separatorString; StringArray rootNames, rootPaths; getRoots (rootNames, rootPaths); @@ -56831,7 +56608,7 @@ void FileBrowserComponent::setRoot (const File& newRootDirectory) String currentRootName (currentRoot.getFullPathName()); if (currentRootName.isEmpty()) - currentRootName += File::separator; + currentRootName = File::separatorString; currentPathBox->setText (currentRootName, true); @@ -57527,7 +57304,7 @@ public: { newFile = root.getChildFile (fileInfo->filename); newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize); - newModTime = fileInfo->modificationTime.formatted (T("%d %b '%y %H:%M")); + newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M"); } if (newFile != file @@ -58147,7 +57924,7 @@ public: && parentContentsList_->getFileInfo (indexInContentsList_, fileInfo)) { fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize); - modTime = fileInfo.modificationTime.formatted (T("%d %b '%y %H:%M")); + modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M"); isDirectory = fileInfo.isDirectory; } else @@ -212038,22 +211815,24 @@ void InterProcessLock::exit() #define INVALID_FILE_ATTRIBUTES ((DWORD) -1) #endif -const juce_wchar File::separator = '\\'; -const juce_wchar* File::separatorString = T("\\"); +const juce_wchar File::separator = '\\'; +const String File::separatorString ("\\"); -bool juce_fileExists (const String& fileName, const bool dontCountDirectories) +bool File::exists() const { - if (fileName.isEmpty()) - return false; - - const DWORD attr = GetFileAttributes (fileName); - return dontCountDirectories ? ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) - : (attr != INVALID_FILE_ATTRIBUTES); + return fullPath.isNotEmpty() + && GetFileAttributes (fullPath) != INVALID_FILE_ATTRIBUTES; } -bool juce_isDirectory (const String& fileName) +bool File::existsAsFile() const { - const DWORD attr = GetFileAttributes (fileName); + return fullPath.isNotEmpty() + && (GetFileAttributes (fullPath) & FILE_ATTRIBUTE_DIRECTORY) == 0; +} + +bool File::isDirectory() const +{ + const DWORD attr = GetFileAttributes (fullPath); return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) && (attr != INVALID_FILE_ATTRIBUTES); } @@ -212086,12 +211865,14 @@ bool File::isHidden() const return (GetFileAttributes (getFullPathName()) & FILE_ATTRIBUTE_HIDDEN) != 0; } -bool juce_deleteFile (const String& fileName) +bool File::deleteFile() const { - if (juce_isDirectory (fileName)) - return RemoveDirectory (fileName) != 0; - - return DeleteFile (fileName) != 0; + if (! exists()) + return true; + else if (isDirectory()) + return RemoveDirectory (fullPath) != 0; + else + return DeleteFile (fullPath) != 0; } bool File::moveToTrash() const @@ -212125,20 +211906,19 @@ bool juce_copyFile (const String& source, const String& dest) return CopyFile (source, dest, false) != 0; } -void juce_createDirectory (const String& fileName) +void File::createDirectoryInternal (const String& fileName) const { - if (! juce_fileExists (fileName, true)) - CreateDirectory (fileName, 0); + CreateDirectory (fileName, 0); } // return 0 if not possible -void* juce_fileOpen (const String& fileName, bool forWriting) +void* juce_fileOpen (const File& file, bool forWriting) { HANDLE h; if (forWriting) { - h = CreateFile (fileName, GENERIC_WRITE, FILE_SHARE_READ, 0, + h = CreateFile (file.getFullPathName(), GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (h != INVALID_HANDLE_VALUE) @@ -212148,7 +211928,7 @@ void* juce_fileOpen (const String& fileName, bool forWriting) } else { - h = CreateFile (fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + h = CreateFile (file.getFullPathName(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); if (h == INVALID_HANDLE_VALUE) @@ -212185,24 +211965,28 @@ int64 juce_fileSetPosition (void* handle, int64 pos) return li.QuadPart; } -int64 juce_fileGetPosition (void* handle) +int64 FileOutputStream::getPositionInternal() const { + if (fileHandle == 0) + return -1; + LARGE_INTEGER li; li.QuadPart = 0; - li.LowPart = SetFilePointer ((HANDLE) handle, 0, &li.HighPart, FILE_CURRENT); // (returns -1 if it fails) + li.LowPart = SetFilePointer ((HANDLE) fileHandle, 0, &li.HighPart, FILE_CURRENT); // (returns -1 if it fails) return jmax ((int64) 0, li.QuadPart); } -void juce_fileFlush (void* handle) +void FileOutputStream::flushInternal() { - FlushFileBuffers ((HANDLE) handle); + if (fileHandle != 0) + FlushFileBuffers ((HANDLE) fileHandle); } -int64 juce_getFileSize (const String& fileName) +int64 File::getSize() const { WIN32_FILE_ATTRIBUTE_DATA attributes; - if (GetFileAttributesEx (fileName, GetFileExInfoStandard, &attributes)) + if (GetFileAttributesEx (fullPath, GetFileExInfoStandard, &attributes)) return (((int64) attributes.nFileSizeHigh) << 32) | attributes.nFileSizeLow; return 0; @@ -212265,8 +212049,7 @@ bool juce_setFileTimes (const String& fileName, return ok; } -// return '\0' separated list of strings -const StringArray juce_getFileSystemRoots() +void File::findFileSystemRoots (Array& destArray) { TCHAR buffer [2048]; buffer[0] = 0; @@ -212285,7 +212068,9 @@ const StringArray juce_getFileSystemRoots() } roots.sort (true); - return roots; + + for (int i = 0; i < roots.size(); ++i) + destArray.add (roots [i]); } static const String getDriveFromPath (const String& path) @@ -212296,21 +212081,26 @@ static const String getDriveFromPath (const String& path) return path; } -const String juce_getVolumeLabel (const String& filenameOnVolume, - int& volumeSerialNumber) +const String File::getVolumeLabel() const +{ + TCHAR dest[64]; + if (! GetVolumeInformation (getDriveFromPath (getFullPathName()), dest, + numElementsInArray (dest), 0, 0, 0, 0, 0)) + dest[0] = 0; + + return dest; +} + +int File::getVolumeSerialNumber() const { TCHAR dest[64]; DWORD serialNum; - if (! GetVolumeInformation (getDriveFromPath (filenameOnVolume), dest, + if (! GetVolumeInformation (getDriveFromPath (getFullPathName()), dest, numElementsInArray (dest), &serialNum, 0, 0, 0, 0)) - { - dest[0] = 0; - serialNum = 0; - } + return 0; - volumeSerialNumber = serialNum; - return dest; + return (int) serialNum; } static int64 getDiskSpaceInfo (const String& path, const bool total) @@ -212494,78 +212284,76 @@ const File File::getLinkedTarget() const return result; } -template -static void getFindFileInfo (FindDataType& findData, - String& filename, bool* const isDir, bool* const isHidden, - int64* const fileSize, Time* const modTime, Time* const creationTime, - bool* const isReadOnly) +class DirectoryIterator::NativeIterator::Pimpl { - filename = findData.cFileName; - - if (isDir != 0) - *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); - - if (isHidden != 0) - *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0); - - if (fileSize != 0) - *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32); - - if (modTime != 0) - *modTime = fileTimeToTime (&findData.ftLastWriteTime); - - if (creationTime != 0) - *creationTime = fileTimeToTime (&findData.ftCreationTime); - - if (isReadOnly != 0) - *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); -} - -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResult, - bool* isDir, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly) -{ - String wc (directory); - - if (! wc.endsWithChar (File::separator)) - wc += File::separator; - - wc += wildCard; - - WIN32_FIND_DATA findData; - HANDLE h = FindFirstFile (wc, &findData); - - if (h != INVALID_HANDLE_VALUE) +public: + Pimpl (const File& directory, const String& wildCard) + : directoryWithWildCard (File::addTrailingSeparator (directory.getFullPathName()) + wildCard), + handle (INVALID_HANDLE_VALUE) { - getFindFileInfo (findData, firstResult, isDir, isHidden, fileSize, - modTime, creationTime, isReadOnly); - return h; } - firstResult = String::empty; - return 0; -} - -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDir, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly) -{ - WIN32_FIND_DATA findData; - - if (handle != 0 && FindNextFile ((HANDLE) handle, &findData) != 0) + ~Pimpl() { - getFindFileInfo (findData, resultFile, isDir, isHidden, fileSize, - modTime, creationTime, isReadOnly); + if (handle != INVALID_HANDLE_VALUE) + FindClose (handle); + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + WIN32_FIND_DATA findData; + + if (handle == INVALID_HANDLE_VALUE) + { + handle = FindFirstFile (directoryWithWildCard, &findData); + + if (handle == INVALID_HANDLE_VALUE) + return false; + } + else + { + if (FindNextFile (handle, &findData) == 0) + return false; + } + + filenameFound = findData.cFileName; + + if (isDir != 0) *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); + if (isHidden != 0) *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0); + if (fileSize != 0) *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32); + if (modTime != 0) *modTime = fileTimeToTime (&findData.ftLastWriteTime); + if (creationTime != 0) *creationTime = fileTimeToTime (&findData.ftCreationTime); + if (isReadOnly != 0) *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); + return true; } - resultFile = String::empty; - return false; + juce_UseDebuggingNewOperator + +private: + const String directoryWithWildCard; + HANDLE handle; + + Pimpl (const Pimpl&); + Pimpl& operator= (const Pimpl&); +}; + +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) +{ } -void juce_findFileClose (void* handle) +DirectoryIterator::NativeIterator::~NativeIterator() { - FindClose (handle); +} + +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) +{ + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); } bool juce_launchFile (const String& fileName, const String& parameters) @@ -227843,7 +227631,7 @@ void JUCE_CALLTYPE Thread::sleep (int millisecs) } const juce_wchar File::separator = '/'; -const juce_wchar* File::separatorString = L"/"; +const String File::separatorString ("/"); const File File::getCurrentWorkingDirectory() { @@ -227868,47 +227656,35 @@ bool File::setAsCurrentWorkingDirectory() const return chdir (getFullPathName().toUTF8()) == 0; } -bool juce_copyFile (const String& s, const String& d); - static bool juce_stat (const String& fileName, struct stat& info) { return fileName.isNotEmpty() && (stat (fileName.toUTF8(), &info) == 0); } -bool juce_isDirectory (const String& fileName) +bool File::isDirectory() const { struct stat info; - return fileName.isEmpty() - || (juce_stat (fileName, info) - && ((info.st_mode & S_IFDIR) != 0)); + return fullPath.isEmpty() + || (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0)); } -bool juce_fileExists (const String& fileName, const bool dontCountDirectories) +bool File::exists() const { - if (fileName.isEmpty()) - return false; - - const char* const fileNameUTF8 = fileName.toUTF8(); - bool exists = access (fileNameUTF8, F_OK) == 0; - - if (exists && dontCountDirectories) - { - struct stat info; - const int res = stat (fileNameUTF8, &info); - - if (res == 0 && (info.st_mode & S_IFDIR) != 0) - exists = false; - } - - return exists; + return fullPath.isNotEmpty() + && access (fullPath.toUTF8(), F_OK) == 0; } -int64 juce_getFileSize (const String& fileName) +bool File::existsAsFile() const +{ + return exists() && ! isDirectory(); +} + +int64 File::getSize() const { struct stat info; - return juce_stat (fileName, info) ? info.st_size : 0; + return juce_stat (fullPath, info) ? info.st_size : 0; } bool juce_canWriteToFile (const String& fileName) @@ -227916,14 +227692,36 @@ bool juce_canWriteToFile (const String& fileName) return access (fileName.toUTF8(), W_OK) == 0; } -bool juce_deleteFile (const String& fileName) +bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) { - if (juce_isDirectory (fileName)) - return rmdir (fileName.toUTF8()) == 0; + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res != 0) + return false; + + info.st_mode &= 0777; // Just permissions + + if (isReadOnly) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); else - return remove (fileName.toUTF8()) == 0; + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + return chmod (fileName.toUTF8(), info.st_mode) == 0; } +bool File::deleteFile() const +{ + if (! exists()) + return true; + else if (isDirectory()) + return rmdir (fullPath.toUTF8()) == 0; + else + return remove (fullPath.toUTF8()) == 0; +} + +bool juce_copyFile (const String& s, const String& d); + bool juce_moveFile (const String& source, const String& dest) { if (rename (source.toUTF8(), dest.toUTF8()) == 0) @@ -227932,29 +227730,29 @@ bool juce_moveFile (const String& source, const String& dest) if (juce_canWriteToFile (source) && juce_copyFile (source, dest)) { - if (juce_deleteFile (source)) + if (File (source).deleteFile()) return true; - juce_deleteFile (dest); + File (dest).deleteFile(); } return false; } -void juce_createDirectory (const String& fileName) +void File::createDirectoryInternal (const String& fileName) const { mkdir (fileName.toUTF8(), 0777); } -void* juce_fileOpen (const String& fileName, bool forWriting) +void* juce_fileOpen (const File& file, bool forWriting) { int flags = O_RDONLY; if (forWriting) { - if (juce_fileExists (fileName, false)) + if (file.exists()) { - const int f = open (fileName.toUTF8(), O_RDWR, 00644); + const int f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644); if (f != -1) lseek (f, 0, SEEK_END); @@ -227967,7 +227765,7 @@ void* juce_fileOpen (const String& fileName, bool forWriting) } } - return (void*) open (fileName.toUTF8(), flags, 00644); + return (void*) open (file.getFullPathName().toUTF8(), flags, 00644); } void juce_fileClose (void* handle) @@ -228000,18 +227798,18 @@ int64 juce_fileSetPosition (void* handle, int64 pos) return -1; } -int64 juce_fileGetPosition (void* handle) +int64 FileOutputStream::getPositionInternal() const { - if (handle != 0) - return lseek ((int) (pointer_sized_int) handle, 0, SEEK_CUR); + if (fileHandle != 0) + return lseek ((int) (pointer_sized_int) fileHandle, 0, SEEK_CUR); return -1; } -void juce_fileFlush (void* handle) +void FileOutputStream::flushInternal() { - if (handle != 0) - fsync ((int) (pointer_sized_int) handle); + if (fileHandle != 0) + fsync ((int) (pointer_sized_int) fileHandle); } const File juce_getExecutableFile() @@ -228022,10 +227820,8 @@ const File juce_getExecutableFile() } // if this file doesn't exist, find a parent of it that does.. -static bool doStatFS (const File* file, struct statfs& result) +static bool juce_doStatFS (File f, struct statfs& result) { - File f (*file); - for (int i = 5; --i >= 0;) { if (f.exists()) @@ -228040,7 +227836,7 @@ static bool doStatFS (const File* file, struct statfs& result) int64 File::getBytesFreeOnVolume() const { struct statfs buf; - if (doStatFS (this, buf)) + if (juce_doStatFS (*this, buf)) return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user return 0; @@ -228049,17 +227845,14 @@ int64 File::getBytesFreeOnVolume() const int64 File::getVolumeTotalSize() const { struct statfs buf; - if (doStatFS (this, buf)) + if (juce_doStatFS (*this, buf)) return (int64) buf.f_bsize * (int64) buf.f_blocks; return 0; } -const String juce_getVolumeLabel (const String& filenameOnVolume, - int& volumeSerialNumber) +const String File::getVolumeLabel() const { - volumeSerialNumber = 0; - #if JUCE_MAC struct VolAttrBuf { @@ -228073,16 +227866,13 @@ const String juce_getVolumeLabel (const String& filenameOnVolume, attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; - File f (filenameOnVolume); + File f (*this); for (;;) { - if (getattrlist (f.getFullPathName().toUTF8(), - &attrList, &attrBuf, sizeof(attrBuf), 0) == 0) - { + if (getattrlist (f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof (attrBuf), 0) == 0) return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset, (int) attrBuf.mountPointRef.attr_length); - } const File parent (f.getParentDirectory()); @@ -228096,6 +227886,11 @@ const String juce_getVolumeLabel (const String& filenameOnVolume, return String::empty; } +int File::getVolumeSerialNumber() const +{ + return 0; // xxx +} + void juce_runSystemCommand (const String& command) { int result = system (command.toUTF8()); @@ -228267,24 +228062,6 @@ bool juce_setFileTimes (const String& fileName, return utime (fileName.toUTF8(), ×) == 0; } -bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) -{ - struct stat info; - const int res = stat (fileName.toUTF8(), &info); - if (res != 0) - return false; - - info.st_mode &= 0777; // Just permissions - - if( isReadOnly ) - info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - else - // Give everybody write permission? - info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - - return chmod (fileName.toUTF8(), info.st_mode) == 0; -} - bool juce_copyFile (const String& s, const String& d) { const File source (s), dest (d); @@ -228316,11 +228093,9 @@ bool juce_copyFile (const String& s, const String& d) return ok; } -const StringArray juce_getFileSystemRoots() +void File::findFileSystemRoots (Array& destArray) { - StringArray s; - s.add ("/"); - return s; + destArray.add (File ("/")); } bool File::isOnCDRomDrive() const @@ -228472,42 +228247,54 @@ bool File::moveToTrash() const getFileExtension())); } -struct FindFileStruct +class DirectoryIterator::NativeIterator::Pimpl { - String parentDir, wildCard; - DIR* dir; - - bool getNextMatch (String& result, bool* const isDir, bool* const isHidden, int64* const fileSize, - Time* const modTime, Time* const creationTime, bool* const isReadOnly) +public: + Pimpl (const File& directory, const String& wildCard_) + : parentDir (File::addTrailingSeparator (directory.getFullPathName())), + wildCard (wildCard_), + dir (opendir (directory.getFullPathName().toUTF8())) { - const char* const wildcardUTF8 = wildCard.toUTF8(); + if (wildCard == "*.*") + wildCard = "*"; + + wildcardUTF8 = wildCard.toUTF8(); + } + + ~Pimpl() + { + if (dir != 0) + closedir (dir); + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + if (dir == 0) + return false; for (;;) { struct dirent* const de = readdir (dir); if (de == 0) - break; + return false; if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) { - result = String::fromUTF8 (de->d_name); + filenameFound = String::fromUTF8 (de->d_name); - const String path (parentDir + result); + const String path (parentDir + filenameFound); if (isDir != 0 || fileSize != 0) { struct stat info; const bool statOk = (stat (path.toUTF8(), &info) == 0); - if (isDir != 0) - *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0)); - - if (isHidden != 0) - *isHidden = (de->d_name[0] == '.'); - - if (fileSize != 0) - *fileSize = statOk ? info.st_size : 0; + if (isDir != 0) *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0)); + if (isHidden != 0) *isHidden = (de->d_name[0] == '.'); + if (fileSize != 0) *fileSize = statOk ? info.st_size : 0; } if (modTime != 0 || creationTime != 0) @@ -228515,11 +228302,8 @@ struct FindFileStruct int64 m, a, c; juce_getFileTimes (path, m, a, c); - if (modTime != 0) - *modTime = m; - - if (creationTime != 0) - *creationTime = c; + if (modTime != 0) *modTime = m; + if (creationTime != 0) *creationTime = c; } if (isReadOnly != 0) @@ -228528,69 +228312,31 @@ struct FindFileStruct return true; } } - - return false; } + +private: + String parentDir, wildCard; + const char* wildcardUTF8; + DIR* dir; + + Pimpl (const Pimpl&); + Pimpl& operator= (const Pimpl&); }; -// returns 0 on failure -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, - Time* creationTime, bool* isReadOnly) +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) { - DIR* d = opendir (directory.toUTF8()); - - if (d != 0) - { - FindFileStruct* ff = new FindFileStruct(); - ff->parentDir = directory; - - if (!ff->parentDir.endsWithChar (File::separator)) - ff->parentDir += File::separator; - - ff->wildCard = wildCard; - if (wildCard == "*.*") - ff->wildCard = "*"; - - ff->dir = d; - - if (ff->getNextMatch (firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) - { - return ff; - } - else - { - firstResultFile = String::empty; - isDir = false; - isHidden = false; - closedir (d); - delete ff; - } - } - - return 0; } -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) +DirectoryIterator::NativeIterator::~NativeIterator() { - FindFileStruct* const ff = (FindFileStruct*) handle; - - if (ff != 0) - return ff->getNextMatch (resultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); - - return false; } -void juce_findFileClose (void* handle) +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) { - FindFileStruct* const ff = (FindFileStruct*) handle; - - if (ff != 0) - { - closedir (ff->dir); - delete ff; - } + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); } bool juce_launchFile (const String& fileName, @@ -237902,7 +237648,7 @@ void JUCE_CALLTYPE Thread::sleep (int millisecs) } const juce_wchar File::separator = '/'; -const juce_wchar* File::separatorString = L"/"; +const String File::separatorString ("/"); const File File::getCurrentWorkingDirectory() { @@ -237927,47 +237673,35 @@ bool File::setAsCurrentWorkingDirectory() const return chdir (getFullPathName().toUTF8()) == 0; } -bool juce_copyFile (const String& s, const String& d); - static bool juce_stat (const String& fileName, struct stat& info) { return fileName.isNotEmpty() && (stat (fileName.toUTF8(), &info) == 0); } -bool juce_isDirectory (const String& fileName) +bool File::isDirectory() const { struct stat info; - return fileName.isEmpty() - || (juce_stat (fileName, info) - && ((info.st_mode & S_IFDIR) != 0)); + return fullPath.isEmpty() + || (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0)); } -bool juce_fileExists (const String& fileName, const bool dontCountDirectories) +bool File::exists() const { - if (fileName.isEmpty()) - return false; - - const char* const fileNameUTF8 = fileName.toUTF8(); - bool exists = access (fileNameUTF8, F_OK) == 0; - - if (exists && dontCountDirectories) - { - struct stat info; - const int res = stat (fileNameUTF8, &info); - - if (res == 0 && (info.st_mode & S_IFDIR) != 0) - exists = false; - } - - return exists; + return fullPath.isNotEmpty() + && access (fullPath.toUTF8(), F_OK) == 0; } -int64 juce_getFileSize (const String& fileName) +bool File::existsAsFile() const +{ + return exists() && ! isDirectory(); +} + +int64 File::getSize() const { struct stat info; - return juce_stat (fileName, info) ? info.st_size : 0; + return juce_stat (fullPath, info) ? info.st_size : 0; } bool juce_canWriteToFile (const String& fileName) @@ -237975,14 +237709,36 @@ bool juce_canWriteToFile (const String& fileName) return access (fileName.toUTF8(), W_OK) == 0; } -bool juce_deleteFile (const String& fileName) +bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) { - if (juce_isDirectory (fileName)) - return rmdir (fileName.toUTF8()) == 0; + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res != 0) + return false; + + info.st_mode &= 0777; // Just permissions + + if (isReadOnly) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); else - return remove (fileName.toUTF8()) == 0; + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + return chmod (fileName.toUTF8(), info.st_mode) == 0; } +bool File::deleteFile() const +{ + if (! exists()) + return true; + else if (isDirectory()) + return rmdir (fullPath.toUTF8()) == 0; + else + return remove (fullPath.toUTF8()) == 0; +} + +bool juce_copyFile (const String& s, const String& d); + bool juce_moveFile (const String& source, const String& dest) { if (rename (source.toUTF8(), dest.toUTF8()) == 0) @@ -237991,29 +237747,29 @@ bool juce_moveFile (const String& source, const String& dest) if (juce_canWriteToFile (source) && juce_copyFile (source, dest)) { - if (juce_deleteFile (source)) + if (File (source).deleteFile()) return true; - juce_deleteFile (dest); + File (dest).deleteFile(); } return false; } -void juce_createDirectory (const String& fileName) +void File::createDirectoryInternal (const String& fileName) const { mkdir (fileName.toUTF8(), 0777); } -void* juce_fileOpen (const String& fileName, bool forWriting) +void* juce_fileOpen (const File& file, bool forWriting) { int flags = O_RDONLY; if (forWriting) { - if (juce_fileExists (fileName, false)) + if (file.exists()) { - const int f = open (fileName.toUTF8(), O_RDWR, 00644); + const int f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644); if (f != -1) lseek (f, 0, SEEK_END); @@ -238026,7 +237782,7 @@ void* juce_fileOpen (const String& fileName, bool forWriting) } } - return (void*) open (fileName.toUTF8(), flags, 00644); + return (void*) open (file.getFullPathName().toUTF8(), flags, 00644); } void juce_fileClose (void* handle) @@ -238059,18 +237815,18 @@ int64 juce_fileSetPosition (void* handle, int64 pos) return -1; } -int64 juce_fileGetPosition (void* handle) +int64 FileOutputStream::getPositionInternal() const { - if (handle != 0) - return lseek ((int) (pointer_sized_int) handle, 0, SEEK_CUR); + if (fileHandle != 0) + return lseek ((int) (pointer_sized_int) fileHandle, 0, SEEK_CUR); return -1; } -void juce_fileFlush (void* handle) +void FileOutputStream::flushInternal() { - if (handle != 0) - fsync ((int) (pointer_sized_int) handle); + if (fileHandle != 0) + fsync ((int) (pointer_sized_int) fileHandle); } const File juce_getExecutableFile() @@ -238081,10 +237837,8 @@ const File juce_getExecutableFile() } // if this file doesn't exist, find a parent of it that does.. -static bool doStatFS (const File* file, struct statfs& result) +static bool juce_doStatFS (File f, struct statfs& result) { - File f (*file); - for (int i = 5; --i >= 0;) { if (f.exists()) @@ -238099,7 +237853,7 @@ static bool doStatFS (const File* file, struct statfs& result) int64 File::getBytesFreeOnVolume() const { struct statfs buf; - if (doStatFS (this, buf)) + if (juce_doStatFS (*this, buf)) return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user return 0; @@ -238108,17 +237862,14 @@ int64 File::getBytesFreeOnVolume() const int64 File::getVolumeTotalSize() const { struct statfs buf; - if (doStatFS (this, buf)) + if (juce_doStatFS (*this, buf)) return (int64) buf.f_bsize * (int64) buf.f_blocks; return 0; } -const String juce_getVolumeLabel (const String& filenameOnVolume, - int& volumeSerialNumber) +const String File::getVolumeLabel() const { - volumeSerialNumber = 0; - #if JUCE_MAC struct VolAttrBuf { @@ -238132,16 +237883,13 @@ const String juce_getVolumeLabel (const String& filenameOnVolume, attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; - File f (filenameOnVolume); + File f (*this); for (;;) { - if (getattrlist (f.getFullPathName().toUTF8(), - &attrList, &attrBuf, sizeof(attrBuf), 0) == 0) - { + if (getattrlist (f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof (attrBuf), 0) == 0) return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset, (int) attrBuf.mountPointRef.attr_length); - } const File parent (f.getParentDirectory()); @@ -238155,6 +237903,11 @@ const String juce_getVolumeLabel (const String& filenameOnVolume, return String::empty; } +int File::getVolumeSerialNumber() const +{ + return 0; // xxx +} + void juce_runSystemCommand (const String& command) { int result = system (command.toUTF8()); @@ -238321,24 +238074,6 @@ bool juce_setFileTimes (const String& fileName, return utime (fileName.toUTF8(), ×) == 0; } -bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) -{ - struct stat info; - const int res = stat (fileName.toUTF8(), &info); - if (res != 0) - return false; - - info.st_mode &= 0777; // Just permissions - - if (isReadOnly) - info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - else - // Give everybody write permission? - info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - - return chmod (fileName.toUTF8(), info.st_mode) == 0; -} - bool juce_copyFile (const String& src, const String& dst) { const ScopedAutoReleasePool pool; @@ -238356,18 +238091,16 @@ bool juce_copyFile (const String& src, const String& dst) #endif } -const StringArray juce_getFileSystemRoots() +void File::findFileSystemRoots (Array& destArray) { - StringArray s; - s.add ("/"); - return s; + destArray.add (File ("/")); } -static bool isFileOnDriveType (const File* const f, const char** types) +static bool isFileOnDriveType (const File& f, const char** types) { struct statfs buf; - if (doStatFS (f, buf)) + if (juce_doStatFS (f, buf)) { const String type (buf.f_fstypename); @@ -238383,14 +238116,14 @@ bool File::isOnCDRomDrive() const { static const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", 0 }; - return isFileOnDriveType (this, (const char**) cdTypes); + return isFileOnDriveType (*this, (const char**) cdTypes); } bool File::isOnHardDisk() const { static const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", 0 }; - return ! (isOnCDRomDrive() || isFileOnDriveType (this, (const char**) nonHDTypes)); + return ! (isOnCDRomDrive() || isFileOnDriveType (*this, (const char**) nonHDTypes)); } bool File::isOnRemovableDrive() const @@ -238575,98 +238308,95 @@ bool File::moveToTrash() const #endif } -struct FindFileStruct +class DirectoryIterator::NativeIterator::Pimpl { - NSDirectoryEnumerator* enumerator; +public: + Pimpl (const File& directory, const String& wildCard_) + : parentDir (File::addTrailingSeparator (directory.getFullPathName())), + wildCard (wildCard_), + enumerator (0) + { + ScopedAutoReleasePool pool; + + enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory.getFullPathName())] retain]; + + wildcardUTF8 = wildCard.toUTF8(); + } + + ~Pimpl() + { + [enumerator release]; + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + ScopedAutoReleasePool pool; + + for (;;) + { + NSString* file; + if (enumerator == 0 || (file = [enumerator nextObject]) == 0) + return false; + + [enumerator skipDescendents]; + filenameFound = nsStringToJuce (file); + + if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0) + continue; + + const String path (parentDir + filenameFound); + + if (isDir != 0 || fileSize != 0) + { + struct stat info; + const bool statOk = juce_stat (path, info); + + if (isDir != 0) *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); + if (isHidden != 0) *isHidden = juce_isHiddenFile (path); + if (fileSize != 0) *fileSize = statOk ? info.st_size : 0; + } + + if (modTime != 0 || creationTime != 0) + { + int64 m, a, c; + juce_getFileTimes (path, m, a, c); + + if (modTime != 0) *modTime = m; + if (creationTime != 0) *creationTime = c; + } + + if (isReadOnly != 0) + *isReadOnly = ! juce_canWriteToFile (path); + + return true; + } + } + +private: String parentDir, wildCard; + const char* wildcardUTF8; + NSDirectoryEnumerator* enumerator; + + Pimpl (const Pimpl&); + Pimpl& operator= (const Pimpl&); }; -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) { - ScopedAutoReleasePool pool; - FindFileStruct* ff = (FindFileStruct*) handle; - NSString* file; - const char* const wildcardUTF8 = ff->wildCard.toUTF8(); - - for (;;) - { - if (ff == 0 || (file = [ff->enumerator nextObject]) == 0) - return false; - - [ff->enumerator skipDescendents]; - resultFile = nsStringToJuce (file); - - if (fnmatch (wildcardUTF8, resultFile.toUTF8(), FNM_CASEFOLD) != 0) - continue; - - const String path (ff->parentDir + resultFile); - - if (isDir != 0 || fileSize != 0) - { - struct stat info; - const bool statOk = juce_stat (path, info); - - if (isDir != 0) - *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); - - if (isHidden != 0) - *isHidden = juce_isHiddenFile (path); - - if (fileSize != 0) - *fileSize = statOk ? info.st_size : 0; - } - - if (modTime != 0 || creationTime != 0) - { - int64 m, a, c; - juce_getFileTimes (path, m, a, c); - - if (modTime != 0) - *modTime = m; - - if (creationTime != 0) - *creationTime = c; - } - - if (isReadOnly != 0) - *isReadOnly = ! juce_canWriteToFile (path); - - return true; - } } -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, - Time* creationTime, bool* isReadOnly) +DirectoryIterator::NativeIterator::~NativeIterator() { - ScopedAutoReleasePool pool; - NSDirectoryEnumerator* e = [[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory)]; - - if (e != 0) - { - ScopedPointer ff (new FindFileStruct()); - ff->enumerator = [e retain]; - ff->parentDir = directory; - ff->wildCard = wildCard; - - if (! ff->parentDir.endsWithChar (File::separator)) - ff->parentDir += File::separator; - - if (juce_findFileNext (ff, firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) - return ff.release(); - - [e release]; - } - - return 0; } -void juce_findFileClose (void* handle) +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) { - ScopedAutoReleasePool pool; - ScopedPointer ff ((FindFileStruct*) handle); - [ff->enumerator release]; + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); } bool juce_launchExecutable (const String& pathAndArguments) diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 2dd3f1ba30..d8d86f59cf 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -42,8 +42,8 @@ #define __JUCE_STANDARDHEADER_JUCEHEADER__ #define JUCE_MAJOR_VERSION 1 -#define JUCE_MINOR_VERSION 51 -#define JUCE_BUILDNUMBER 16 +#define JUCE_MINOR_VERSION 52 +#define JUCE_BUILDNUMBER 0 #define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) @@ -189,7 +189,7 @@ #endif #ifndef JUCE_FORCE_DEBUG - #define JUCE_FORCE_DEBUG 0 + //#define JUCE_FORCE_DEBUG 0 #endif #ifndef JUCE_LOG_ASSERTIONS @@ -4256,7 +4256,7 @@ public: bool includeSeconds = true, bool use24HourClock = false) const throw(); - const String formatted (const juce_wchar* format) const throw(); + const String formatted (const String& format) const throw(); const Time operator+ (const RelativeTime& delta) const throw() { return Time (millisSinceEpoch + delta.inMilliseconds()); } @@ -4379,6 +4379,8 @@ public: bool operator== (const File& otherFile) const; bool operator!= (const File& otherFile) const; + bool operator< (const File& otherFile) const; + bool operator> (const File& otherFile) const; bool hasWriteAccess() const; @@ -4516,7 +4518,7 @@ public: static const juce_wchar separator; - static const juce_wchar* separatorString; + static const String separatorString; static const String createLegalFileName (const String& fileNameToFix); @@ -4528,6 +4530,8 @@ public: static const File createFileWithoutCheckingPath (const String& path); + static const String addTrailingSeparator (const String& path); + juce_UseDebuggingNewOperator private: @@ -4538,6 +4542,10 @@ private: friend class DirectoryIterator; File (const String&, int); const String getPathUpToLastSlash() const; + + void createDirectoryInternal (const String& fileName) const; + static const String parseAbsolutePath (const String& path); + static bool fileTypeMatches (int whatToLookFor, bool isDir, bool isHidden); }; #endif // __JUCE_FILE_JUCEHEADER__ @@ -7744,6 +7752,9 @@ public: bool next(); + bool next (bool* isDirectory, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly); + const File getFile() const; float getEstimatedProgress() const; @@ -7751,12 +7762,39 @@ public: juce_UseDebuggingNewOperator private: - Array filesFound; - Array dirsFound; - String wildCard; + friend class File; + + class NativeIterator + { + public: + NativeIterator (const File& directory, const String& wildCard); + ~NativeIterator(); + + bool next (String& filenameFound, + bool* isDirectory, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly); + + juce_UseDebuggingNewOperator + + private: + class Pimpl; + friend class DirectoryIterator; + friend class ScopedPointer; + ScopedPointer pimpl; + + NativeIterator (const NativeIterator&); + NativeIterator& operator= (const NativeIterator&); + }; + + friend class ScopedPointer; + NativeIterator fileFinder; + String wildCard, path; int index; + mutable int totalNumFiles; const int whatToLookFor; + const bool isRecursive; ScopedPointer subIterator; + File currentFile; DirectoryIterator (const DirectoryIterator&); DirectoryIterator& operator= (const DirectoryIterator&); @@ -7842,6 +7880,9 @@ private: int bufferSize, bytesInBuffer; HeapBlock buffer; + void flushInternal(); + int64 getPositionInternal() const; + FileOutputStream (const FileOutputStream&); FileOutputStream& operator= (const FileOutputStream&); }; @@ -22342,7 +22383,7 @@ public: void setIgnoresHiddenFiles (bool shouldIgnoreHiddenFiles); - bool ignoresHiddenFiles() const { return ignoreHiddenFiles; } + bool ignoresHiddenFiles() const; struct FileInfo { @@ -22379,19 +22420,20 @@ private: File root; const FileFilter* fileFilter; TimeSliceThread& thread; - bool includeDirectories, includeFiles, ignoreHiddenFiles; + int fileTypeFlags; CriticalSection fileListLock; OwnedArray files; - void* volatile fileFindHandle; + ScopedPointer fileFindHandle; bool volatile shouldStop; void changed(); bool checkNextFile (bool& hasChanged); - bool addFile (const String& filename, bool isDir, bool isHidden, + bool addFile (const File& file, bool isDir, const int64 fileSize, const Time& modTime, const Time& creationTime, bool isReadOnly); + void setTypeFlags (int newFlags); DirectoryContentsList (const DirectoryContentsList&); DirectoryContentsList& operator= (const DirectoryContentsList&); diff --git a/src/audio/audio_file_formats/juce_WavAudioFormat.cpp b/src/audio/audio_file_formats/juce_WavAudioFormat.cpp index c8c7e07cd8..0573f6a008 100644 --- a/src/audio/audio_file_formats/juce_WavAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_WavAudioFormat.cpp @@ -60,8 +60,8 @@ const StringPairArray WavAudioFormat::createBWAVMetadata (const String& descript m.set (bwavDescription, description); m.set (bwavOriginator, originator); m.set (bwavOriginatorRef, originatorRef); - m.set (bwavOriginationDate, date.formatted (T("%Y-%m-%d"))); - m.set (bwavOriginationTime, date.formatted (T("%H:%M:%S"))); + m.set (bwavOriginationDate, date.formatted ("%Y-%m-%d")); + m.set (bwavOriginationTime, date.formatted ("%H:%M:%S")); m.set (bwavTimeReference, String (timeReferenceSamples)); m.set (bwavCodingHistory, codingHistory); diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 3d6c95c90e..7108010022 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -32,8 +32,8 @@ See also SystemStats::getJUCEVersion() for a string version. */ #define JUCE_MAJOR_VERSION 1 -#define JUCE_MINOR_VERSION 51 -#define JUCE_BUILDNUMBER 16 +#define JUCE_MINOR_VERSION 52 +#define JUCE_BUILDNUMBER 0 /** Current Juce version number. diff --git a/src/core/juce_Time.cpp b/src/core/juce_Time.cpp index e636d4de7a..e0145c9c7b 100644 --- a/src/core/juce_Time.cpp +++ b/src/core/juce_Time.cpp @@ -341,7 +341,7 @@ const String Time::toString (const bool includeDate, return result.trimEnd(); } -const String Time::formatted (const juce_wchar* const format) const throw() +const String Time::formatted (const String& format) const throw() { String buffer; int bufferSize = 128; diff --git a/src/core/juce_Time.h b/src/core/juce_Time.h index 0d7f951bea..98b95bfd28 100644 --- a/src/core/juce_Time.h +++ b/src/core/juce_Time.h @@ -245,7 +245,7 @@ public: @see toString */ - const String formatted (const juce_wchar* format) const throw(); + const String formatted (const String& format) const throw(); //============================================================================== /** Adds a RelativeTime to this time and returns the result. */ diff --git a/src/gui/components/filebrowser/juce_DirectoryContentsList.cpp b/src/gui/components/filebrowser/juce_DirectoryContentsList.cpp index a9587510cc..83c467b66e 100644 --- a/src/gui/components/filebrowser/juce_DirectoryContentsList.cpp +++ b/src/gui/components/filebrowser/juce_DirectoryContentsList.cpp @@ -30,23 +30,13 @@ BEGIN_JUCE_NAMESPACE #include "juce_DirectoryContentsList.h" #include "../../graphics/imaging/juce_ImageCache.h" -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, - Time* creationTime, bool* isReadOnly); -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly); -void juce_findFileClose (void* handle); - //============================================================================== DirectoryContentsList::DirectoryContentsList (const FileFilter* const fileFilter_, TimeSliceThread& thread_) : fileFilter (fileFilter_), thread (thread_), - includeDirectories (false), - includeFiles (false), - ignoreHiddenFiles (true), + fileTypeFlags (File::ignoreHiddenFiles | File::findFiles), fileFindHandle (0), shouldStop (true) { @@ -59,7 +49,13 @@ DirectoryContentsList::~DirectoryContentsList() void DirectoryContentsList::setIgnoresHiddenFiles (const bool shouldIgnoreHiddenFiles) { - ignoreHiddenFiles = shouldIgnoreHiddenFiles; + setTypeFlags (shouldIgnoreHiddenFiles ? (fileTypeFlags | File::ignoreHiddenFiles) + : (fileTypeFlags & ~File::ignoreHiddenFiles)); +} + +bool DirectoryContentsList::ignoresHiddenFiles() const +{ + return (fileTypeFlags & File::ignoreHiddenFiles) != 0; } //============================================================================== @@ -69,19 +65,32 @@ const File& DirectoryContentsList::getDirectory() const } void DirectoryContentsList::setDirectory (const File& directory, - const bool includeDirectories_, - const bool includeFiles_) + const bool includeDirectories, + const bool includeFiles) { - if (directory != root - || includeDirectories != includeDirectories_ - || includeFiles != includeFiles_) + jassert (includeDirectories || includeFiles); // you have to speciify at least one of these! + + if (directory != root) { clear(); - root = directory; - includeDirectories = includeDirectories_; - includeFiles = includeFiles_; + // (this forces a refresh when setTypeFlags() is called, rather than triggering two refreshes) + fileTypeFlags &= ~(File::findDirectories | File::findFiles); + } + + int newFlags = fileTypeFlags; + if (includeDirectories) newFlags |= File::findDirectories; else newFlags &= ~File::findDirectories; + if (includeFiles) newFlags |= File::findFiles; else newFlags &= ~File::findFiles; + + setTypeFlags (newFlags); +} + +void DirectoryContentsList::setTypeFlags (const int newFlags) +{ + if (fileTypeFlags != newFlags) + { + fileTypeFlags = newFlags; refresh(); } } @@ -91,11 +100,7 @@ void DirectoryContentsList::clear() shouldStop = true; thread.removeTimeSliceClient (this); - if (fileFindHandle != 0) - { - juce_findFileClose (fileFindHandle); - fileFindHandle = 0; - } + fileFindHandle = 0; if (files.size() > 0) { @@ -110,36 +115,8 @@ void DirectoryContentsList::refresh() if (root.isDirectory()) { - String fileFound; - bool fileFoundIsDir, isHidden, isReadOnly; - int64 fileSize; - Time modTime, creationTime; - - String path (root.getFullPathName()); - if (! path.endsWithChar (File::separator)) - path += File::separator; - - jassert (fileFindHandle == 0); - - fileFindHandle = juce_findFileStart (path, "*", fileFound, - &fileFoundIsDir, - &isHidden, - &fileSize, - &modTime, - &creationTime, - &isReadOnly); - - if (fileFindHandle != 0 && fileFound.isNotEmpty()) - { - if (addFile (fileFound, fileFoundIsDir, isHidden, - fileSize, modTime, creationTime, isReadOnly)) - { - changed(); - } - } - + fileFindHandle = new DirectoryIterator (root, false, "*", fileTypeFlags); shouldStop = false; - thread.addTimeSliceClient (this); } } @@ -216,20 +193,15 @@ bool DirectoryContentsList::checkNextFile (bool& hasChanged) { if (fileFindHandle != 0) { - String fileFound; bool fileFoundIsDir, isHidden, isReadOnly; int64 fileSize; Time modTime, creationTime; - if (juce_findFileNext (fileFindHandle, fileFound, - &fileFoundIsDir, &isHidden, - &fileSize, - &modTime, - &creationTime, - &isReadOnly)) + if (fileFindHandle->next (&fileFoundIsDir, &isHidden, &fileSize, + &modTime, &creationTime, &isReadOnly)) { - if (addFile (fileFound, fileFoundIsDir, isHidden, fileSize, - modTime, creationTime, isReadOnly)) + if (addFile (fileFindHandle->getFile(), fileFoundIsDir, + fileSize, modTime, creationTime, isReadOnly)) { hasChanged = true; } @@ -238,7 +210,6 @@ bool DirectoryContentsList::checkNextFile (bool& hasChanged) } else { - juce_findFileClose (fileFindHandle); fileFindHandle = 0; } } @@ -257,29 +228,20 @@ int DirectoryContentsList::compareElements (const DirectoryContentsList::FileInf return first->filename.compareIgnoreCase (second->filename); } -bool DirectoryContentsList::addFile (const String& filename, +bool DirectoryContentsList::addFile (const File& file, const bool isDir, - const bool isHidden, const int64 fileSize, const Time& modTime, const Time& creationTime, const bool isReadOnly) { - if (filename == ".." - || filename == "." - || (ignoreHiddenFiles && isHidden)) - return false; - - const File file (root.getChildFile (filename)); - - if (((isDir && includeDirectories) || ((! isDir) && includeFiles)) - && (fileFilter == 0 - || ((! isDir) && fileFilter->isFileSuitable (file)) - || (isDir && fileFilter->isDirectorySuitable (file)))) + if (fileFilter == 0 + || ((! isDir) && fileFilter->isFileSuitable (file)) + || (isDir && fileFilter->isDirectorySuitable (file))) { ScopedPointer info (new FileInfo()); - info->filename = filename; + info->filename = file.getFileName(); info->fileSize = fileSize; info->modificationTime = modTime; info->creationTime = creationTime; diff --git a/src/gui/components/filebrowser/juce_DirectoryContentsList.h b/src/gui/components/filebrowser/juce_DirectoryContentsList.h index b31903c547..0dd4f56b44 100644 --- a/src/gui/components/filebrowser/juce_DirectoryContentsList.h +++ b/src/gui/components/filebrowser/juce_DirectoryContentsList.h @@ -30,6 +30,7 @@ #include "../../../events/juce_ChangeBroadcaster.h" #include "../../../threads/juce_TimeSliceThread.h" #include "../../graphics/imaging/juce_Image.h" +#include "../../../io/files/juce_DirectoryIterator.h" //============================================================================== @@ -104,7 +105,7 @@ public: /** Returns true if hidden files are ignored. @see setIgnoresHiddenFiles */ - bool ignoresHiddenFiles() const { return ignoreHiddenFiles; } + bool ignoresHiddenFiles() const; //============================================================================== /** Contains cached information about one of the files in a DirectoryContentsList. @@ -197,19 +198,20 @@ private: File root; const FileFilter* fileFilter; TimeSliceThread& thread; - bool includeDirectories, includeFiles, ignoreHiddenFiles; + int fileTypeFlags; CriticalSection fileListLock; OwnedArray files; - void* volatile fileFindHandle; + ScopedPointer fileFindHandle; bool volatile shouldStop; void changed(); bool checkNextFile (bool& hasChanged); - bool addFile (const String& filename, bool isDir, bool isHidden, + bool addFile (const File& file, bool isDir, const int64 fileSize, const Time& modTime, const Time& creationTime, bool isReadOnly); + void setTypeFlags (int newFlags); DirectoryContentsList (const DirectoryContentsList&); DirectoryContentsList& operator= (const DirectoryContentsList&); diff --git a/src/gui/components/filebrowser/juce_FileBrowserComponent.cpp b/src/gui/components/filebrowser/juce_FileBrowserComponent.cpp index 4e31eed64a..b264ebc209 100644 --- a/src/gui/components/filebrowser/juce_FileBrowserComponent.cpp +++ b/src/gui/components/filebrowser/juce_FileBrowserComponent.cpp @@ -231,7 +231,7 @@ void FileBrowserComponent::setRoot (const File& newRootDirectory) String path (newRootDirectory.getFullPathName()); if (path.isEmpty()) - path += File::separator; + path = File::separatorString; StringArray rootNames, rootPaths; getRoots (rootNames, rootPaths); @@ -259,7 +259,7 @@ void FileBrowserComponent::setRoot (const File& newRootDirectory) String currentRootName (currentRoot.getFullPathName()); if (currentRootName.isEmpty()) - currentRootName += File::separator; + currentRootName = File::separatorString; currentPathBox->setText (currentRootName, true); diff --git a/src/gui/components/filebrowser/juce_FileListComponent.cpp b/src/gui/components/filebrowser/juce_FileListComponent.cpp index da9641c8c1..84c91d7eb4 100644 --- a/src/gui/components/filebrowser/juce_FileListComponent.cpp +++ b/src/gui/components/filebrowser/juce_FileListComponent.cpp @@ -142,7 +142,7 @@ public: { newFile = root.getChildFile (fileInfo->filename); newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize); - newModTime = fileInfo->modificationTime.formatted (T("%d %b '%y %H:%M")); + newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M"); } if (newFile != file diff --git a/src/gui/components/filebrowser/juce_FileTreeComponent.cpp b/src/gui/components/filebrowser/juce_FileTreeComponent.cpp index b0949d789a..8a2ba0ea09 100644 --- a/src/gui/components/filebrowser/juce_FileTreeComponent.cpp +++ b/src/gui/components/filebrowser/juce_FileTreeComponent.cpp @@ -63,7 +63,7 @@ public: && parentContentsList_->getFileInfo (indexInContentsList_, fileInfo)) { fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize); - modTime = fileInfo.modificationTime.formatted (T("%d %b '%y %H:%M")); + modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M"); isDirectory = fileInfo.isDirectory; } else diff --git a/src/io/files/juce_DirectoryIterator.cpp b/src/io/files/juce_DirectoryIterator.cpp index f8ca3e11b4..1e97850ce1 100644 --- a/src/io/files/juce_DirectoryIterator.cpp +++ b/src/io/files/juce_DirectoryIterator.cpp @@ -30,79 +30,23 @@ BEGIN_JUCE_NAMESPACE #include "juce_DirectoryIterator.h" -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly); -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly); -void juce_findFileClose (void* handle); - //============================================================================== DirectoryIterator::DirectoryIterator (const File& directory, - bool isRecursive, - const String& wc, + bool isRecursive_, + const String& wildCard_, const int whatToLookFor_) - : wildCard (wc), + : fileFinder (directory, isRecursive ? "*" : wildCard_), + wildCard (wildCard_), + path (File::addTrailingSeparator (directory.getFullPathName())), index (-1), - whatToLookFor (whatToLookFor_) + totalNumFiles (-1), + whatToLookFor (whatToLookFor_), + isRecursive (isRecursive_) { // you have to specify the type of files you're looking for! jassert ((whatToLookFor_ & (File::findFiles | File::findDirectories)) != 0); jassert (whatToLookFor_ > 0 && whatToLookFor_ <= 7); - - String path (directory.getFullPathName()); - if (! path.endsWithChar (File::separator)) - path += File::separator; - - String filename; - bool isDirectory, isHidden; - - void* const handle = juce_findFileStart (path, - isRecursive ? "*" : wc, - filename, &isDirectory, &isHidden, 0, 0, 0, 0); - - if (handle != 0) - { - do - { - if (! filename.containsOnly (".")) - { - bool addToList = false; - - if (isDirectory) - { - if (isRecursive - && ((whatToLookFor_ & File::ignoreHiddenFiles) == 0 - || ! isHidden)) - { - dirsFound.add (File (path + filename, 0)); - } - - addToList = (whatToLookFor_ & File::findDirectories) != 0; - } - else - { - addToList = (whatToLookFor_ & File::findFiles) != 0; - } - - // if it's recursive, we're not relying on the OS iterator - // to do the wildcard match, so do it now.. - if (isRecursive && addToList) - addToList = filename.matchesWildcard (wc, true); - - if (addToList && (whatToLookFor_ & File::ignoreHiddenFiles) != 0) - addToList = ! isHidden; - - if (addToList) - filesFound.add (File (path + filename, 0)); - } - - } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0)); - - juce_findFileClose (handle); - } } DirectoryIterator::~DirectoryIterator() @@ -110,28 +54,67 @@ DirectoryIterator::~DirectoryIterator() } bool DirectoryIterator::next() +{ + return next (0, 0, 0, 0, 0, 0); +} + +bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResult, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) { if (subIterator != 0) { - if (subIterator->next()) + if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly)) return true; subIterator = 0; } - if (index >= filesFound.size() + dirsFound.size() - 1) - return false; - - ++index; - - if (index >= filesFound.size()) + String filename; + bool isDirectory, isHidden; + while (fileFinder.next (filename, &isDirectory, &isHidden, fileSize, modTime, creationTime, isReadOnly)) { - subIterator = new DirectoryIterator (dirsFound.getReference (index - filesFound.size()), - true, wildCard, whatToLookFor); - return next(); + ++index; + + if (! filename.containsOnly (".")) + { + const File fileFound (path + filename, 0); + bool matches = false; + + if (isDirectory) + { + if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden)) + subIterator = new DirectoryIterator (fileFound, true, wildCard, whatToLookFor); + + matches = (whatToLookFor & File::findDirectories) != 0; + } + else + { + matches = (whatToLookFor & File::findFiles) != 0; + } + + // if recursive, we're not relying on the OS iterator to do the wildcard match, so do it now.. + if (matches && isRecursive) + matches = filename.matchesWildcard (wildCard, ! File::areFileNamesCaseSensitive()); + + if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0) + matches = ! isHidden; + + if (matches) + { + currentFile = fileFound; + if (isHiddenResult != 0) *isHiddenResult = isHidden; + if (isDirResult != 0) *isDirResult = isDirectory; + + return true; + } + else if (subIterator != 0) + { + return next(); + } + } } - return true; + return false; } const File DirectoryIterator::getFile() const @@ -139,22 +122,21 @@ const File DirectoryIterator::getFile() const if (subIterator != 0) return subIterator->getFile(); - return filesFound [index]; + return currentFile; } float DirectoryIterator::getEstimatedProgress() const { - if (filesFound.size() + dirsFound.size() == 0) - { - return 0.0f; - } - else - { - const float detailedIndex = (subIterator != 0) ? index + subIterator->getEstimatedProgress() - : (float) index; + if (totalNumFiles < 0) + totalNumFiles = File (path).getNumberOfChildFiles (File::findFilesAndDirectories); - return detailedIndex / (filesFound.size() + dirsFound.size()); - } + if (totalNumFiles <= 0) + return 0.0f; + + const float detailedIndex = (subIterator != 0) ? index + subIterator->getEstimatedProgress() + : (float) index; + + return detailedIndex / totalNumFiles; } END_JUCE_NAMESPACE diff --git a/src/io/files/juce_DirectoryIterator.h b/src/io/files/juce_DirectoryIterator.h index 354f3947d1..fcea05271e 100644 --- a/src/io/files/juce_DirectoryIterator.h +++ b/src/io/files/juce_DirectoryIterator.h @@ -76,13 +76,28 @@ public: /** Destructor. */ ~DirectoryIterator(); - /** Call this to move the iterator along to the next file. + /** Moves the iterator along to the next file. @returns true if a file was found (you can then use getFile() to see what it was) - or false if there are no more matching files. */ bool next(); + /** Moves the iterator along to the next file, and returns various properties of that file. + + If you need to find out details about the file, it's more efficient to call this method than + to call the normal next() method and then find out the details afterwards. + + All the parameters are optional, so pass null pointers for any items that you're not + interested in. + + @returns true if a file was found (you can then use getFile() to see what it was) - or + false if there are no more matching files. If it returns false, then none of the + parameters will be filled-in. + */ + bool next (bool* isDirectory, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly); + /** Returns the file that the iterator is currently pointing at. The result of this call is only valid after a call to next() has returned true. @@ -96,17 +111,44 @@ public: */ float getEstimatedProgress() const; - //============================================================================== juce_UseDebuggingNewOperator private: - Array filesFound; - Array dirsFound; - String wildCard; + friend class File; + + //============================================================================== + class NativeIterator + { + public: + NativeIterator (const File& directory, const String& wildCard); + ~NativeIterator(); + + bool next (String& filenameFound, + bool* isDirectory, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly); + + juce_UseDebuggingNewOperator + + private: + class Pimpl; + friend class DirectoryIterator; + friend class ScopedPointer; + ScopedPointer pimpl; + + NativeIterator (const NativeIterator&); + NativeIterator& operator= (const NativeIterator&); + }; + + friend class ScopedPointer; + NativeIterator fileFinder; + String wildCard, path; int index; + mutable int totalNumFiles; const int whatToLookFor; + const bool isRecursive; ScopedPointer subIterator; + File currentFile; DirectoryIterator (const DirectoryIterator&); DirectoryIterator& operator= (const DirectoryIterator&); diff --git a/src/io/files/juce_File.cpp b/src/io/files/juce_File.cpp index 1e4cab13e5..46b27beb73 100644 --- a/src/io/files/juce_File.cpp +++ b/src/io/files/juce_File.cpp @@ -23,11 +23,6 @@ ============================================================================== */ -#ifdef _MSC_VER - #pragma warning (disable: 4514) - #pragma warning (push) -#endif - #include "../../core/juce_StandardHeader.h" #if ! JUCE_WINDOWS @@ -36,170 +31,28 @@ BEGIN_JUCE_NAMESPACE - #include "juce_File.h" #include "juce_FileInputStream.h" +#include "juce_DirectoryIterator.h" #include "juce_TemporaryFile.h" #include "../../core/juce_SystemStats.h" #include "../../core/juce_Random.h" #include "../../containers/juce_ScopedPointer.h" -#ifdef _MSC_VER - #pragma warning (pop) -#endif //============================================================================== -void* juce_fileOpen (const String& path, bool forWriting); -void juce_fileClose (void* handle); -int juce_fileWrite (void* handle, const void* buffer, int size); -int64 juce_fileGetPosition (void* handle); -int64 juce_fileSetPosition (void* handle, int64 pos); -void juce_fileFlush (void* handle); - -bool juce_fileExists (const String& fileName, const bool dontCountDirectories); -bool juce_isDirectory (const String& fileName); -int64 juce_getFileSize (const String& fileName); bool juce_canWriteToFile (const String& fileName); bool juce_setFileReadOnly (const String& fileName, bool isReadOnly); - void juce_getFileTimes (const String& fileName, int64& modificationTime, int64& accessTime, int64& creationTime); bool juce_setFileTimes (const String& fileName, int64 modificationTime, int64 accessTime, int64 creationTime); - -bool juce_deleteFile (const String& fileName); bool juce_copyFile (const String& source, const String& dest); bool juce_moveFile (const String& source, const String& dest); - -// this must also create all paths involved in the directory. -void juce_createDirectory (const String& fileName); - bool juce_launchFile (const String& fileName, const String& parameters); -const StringArray juce_getFileSystemRoots(); -const String juce_getVolumeLabel (const String& filenameOnVolume, int& volumeSerialNumber); - -// starts a directory search operation with a wildcard, returning a handle for -// use in calls to juce_findFileNext. -// juce_firstResultFile gets the name of the file (not the whole pathname) and -// the other pointers, if non-null, are set based on the properties of the file. -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, - Time* creationTime, bool* isReadOnly); - -// returns false when no more files are found -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDirectory, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly); - -void juce_findFileClose (void* handle); - -//============================================================================== -static const String juce_addTrailingSeparator (const String& path) -{ - return path.endsWithChar (File::separator) ? path - : path + File::separator; -} - -//============================================================================== -static const String parseAbsolutePath (String path) -{ - if (path.isEmpty()) - return String::empty; - -#if JUCE_WINDOWS - // Windows.. - path = path.replaceCharacter ('/', '\\'); - - if (path.startsWithChar (File::separator)) - { - if (path[1] != File::separator) - { - /* When you supply a raw string to the File object constructor, it must be an absolute path. - If you're trying to parse a string that may be either a relative path or an absolute path, - you MUST provide a context against which the partial path can be evaluated - you can do - this by simply using File::getChildFile() instead of the File constructor. E.g. saying - "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute - path if that's what was supplied, or would evaluate a partial path relative to the CWD. - */ - jassertfalse - - path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; - } - } - else if (path.indexOfChar (':') < 0) - { - if (path.isEmpty()) - return String::empty; - - /* When you supply a raw string to the File object constructor, it must be an absolute path. - If you're trying to parse a string that may be either a relative path or an absolute path, - you MUST provide a context against which the partial path can be evaluated - you can do - this by simply using File::getChildFile() instead of the File constructor. E.g. saying - "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute - path if that's what was supplied, or would evaluate a partial path relative to the CWD. - */ - jassertfalse - - return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); - } -#else - // Mac or Linux.. - path = path.replaceCharacter ('\\', '/'); - - if (path.startsWithChar ('~')) - { - const char* homeDir = 0; - - if (path[1] == File::separator || path[1] == 0) - { - // expand a name of the form "~/abc" - path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() - + path.substring (1); - } - else - { - // expand a name of type "~dave/abc" - const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); - - struct passwd* const pw = getpwnam (userName.toUTF8()); - if (pw != 0) - { - String home (homeDir); - - if (home.endsWithChar (File::separator)) - home [home.length() - 1] = 0; - - path = String (pw->pw_dir) - + path.substring (userName.length()); - } - } - } - else if (! path.startsWithChar (File::separator)) - { - /* When you supply a raw string to the File object constructor, it must be an absolute path. - If you're trying to parse a string that may be either a relative path or an absolute path, - you MUST provide a context against which the partial path can be evaluated - you can do - this by simply using File::getChildFile() instead of the File constructor. E.g. saying - "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute - path if that's what was supplied, or would evaluate a partial path relative to the CWD. - */ - jassert (path.startsWith ("./") || path.startsWith ("../")); // (assume that a path "./xyz" is deliberately intended to be relative to the CWD) - - return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); - } -#endif - - int len = path.length(); - while (--len > 0 && path [len] == File::separator) - path [len] = 0; - - return path; -} - //============================================================================== const File File::nonexistent; - //============================================================================== File::File (const String& fullPathName) : fullPath (parseAbsolutePath (fullPathName)) @@ -233,6 +86,91 @@ File& File::operator= (const File& other) return *this; } +//============================================================================== +const String File::parseAbsolutePath (const String& p) +{ + if (p.isEmpty()) + return String::empty; + +#if JUCE_WINDOWS + // Windows.. + String path (p.replaceCharacter ('/', '\\')); + + if (path.startsWithChar (File::separator)) + { + if (path[1] != File::separator) + { + /* When you supply a raw string to the File object constructor, it must be an absolute path. + If you're trying to parse a string that may be either a relative path or an absolute path, + you MUST provide a context against which the partial path can be evaluated - you can do + this by simply using File::getChildFile() instead of the File constructor. E.g. saying + "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute + path if that's what was supplied, or would evaluate a partial path relative to the CWD. + */ + jassertfalse + + path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; + } + } + else if (! path.containsChar (':')) + { + /* When you supply a raw string to the File object constructor, it must be an absolute path. + If you're trying to parse a string that may be either a relative path or an absolute path, + you MUST provide a context against which the partial path can be evaluated - you can do + this by simply using File::getChildFile() instead of the File constructor. E.g. saying + "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute + path if that's what was supplied, or would evaluate a partial path relative to the CWD. + */ + jassertfalse + + return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); + } +#else + // Mac or Linux.. + String path (p.replaceCharacter ('\\', '/')); + + if (path.startsWithChar ('~')) + { + if (path[1] == File::separator || path[1] == 0) + { + // expand a name of the form "~/abc" + path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() + + path.substring (1); + } + else + { + // expand a name of type "~dave/abc" + const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); + + struct passwd* const pw = getpwnam (userName.toUTF8()); + if (pw != 0) + path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false); + } + } + else if (! path.startsWithChar (File::separator)) + { + /* When you supply a raw string to the File object constructor, it must be an absolute path. + If you're trying to parse a string that may be either a relative path or an absolute path, + you MUST provide a context against which the partial path can be evaluated - you can do + this by simply using File::getChildFile() instead of the File constructor. E.g. saying + "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute + path if that's what was supplied, or would evaluate a partial path relative to the CWD. + */ + jassert (path.startsWith ("./") || path.startsWith ("../")); // (assume that a path "./xyz" is deliberately intended to be relative to the CWD) + + return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); + } +#endif + + return path.trimCharactersAtEnd (separatorString); +} + +const String File::addTrailingSeparator (const String& path) +{ + return path.endsWithChar (File::separator) ? path + : path + File::separator; +} + //============================================================================== #if JUCE_LINUX #define NAMES_ARE_CASE_SENSITIVE 1 @@ -249,7 +187,6 @@ bool File::areFileNamesCaseSensitive() bool File::operator== (const File& other) const { - // case-insensitive on Windows, but not on linux. #if NAMES_ARE_CASE_SENSITIVE return fullPath == other.fullPath; #else @@ -262,22 +199,25 @@ bool File::operator!= (const File& other) const return ! operator== (other); } +bool File::operator< (const File& other) const +{ +#if NAMES_ARE_CASE_SENSITIVE + return fullPath < other.fullPath; +#else + return fullPath.compareIgnoreCase (other.fullPath) < 0; +#endif +} + +bool File::operator> (const File& other) const +{ +#if NAMES_ARE_CASE_SENSITIVE + return fullPath > other.fullPath; +#else + return fullPath.compareIgnoreCase (other.fullPath) > 0; +#endif +} + //============================================================================== -bool File::exists() const -{ - return juce_fileExists (fullPath, false); -} - -bool File::existsAsFile() const -{ - return juce_fileExists (fullPath, true); -} - -bool File::isDirectory() const -{ - return juce_isDirectory (fullPath); -} - bool File::hasWriteAccess() const { if (exists()) @@ -313,12 +253,6 @@ bool File::setReadOnly (const bool shouldBeReadOnly, return juce_setFileReadOnly (fullPath, shouldBeReadOnly) && worked; } -bool File::deleteFile() const -{ - return (! exists()) - || juce_deleteFile (fullPath); -} - bool File::deleteRecursively() const { bool worked = true; @@ -513,7 +447,7 @@ const File File::getChildFile (String relativePath) const } } - return File (juce_addTrailingSeparator (path) + relativePath); + return File (addTrailingSeparator (path) + relativePath); } } @@ -523,11 +457,6 @@ const File File::getSiblingFile (const String& fileName) const } //============================================================================== -int64 File::getSize() const -{ - return juce_getFileSize (fullPath); -} - const String File::descriptionOfSizeInBytes (const int64 bytes) { if (bytes == 1) @@ -555,22 +484,19 @@ const String File::descriptionOfSizeInBytes (const int64 bytes) //============================================================================== bool File::create() const { - if (! exists()) + if (exists()) + return true; + { const File parentDir (getParentDirectory()); if (parentDir == *this || ! parentDir.createDirectory()) return false; - void* const fh = juce_fileOpen (fullPath, true); - - if (fh == 0) - return false; - - juce_fileClose (fh); + FileOutputStream fo (*this, 8); } - return true; + return exists(); } bool File::createDirectory() const @@ -582,13 +508,7 @@ bool File::createDirectory() const if (parentDir == *this || ! parentDir.createDirectory()) return false; - String dir (fullPath); - - while (dir.endsWithChar (separator)) - dir [dir.length() - 1] = 0; - - juce_createDirectory (dir); - + createDirectoryInternal (fullPath.trimCharactersAtEnd (separatorString)); return isDirectory(); } @@ -652,14 +572,11 @@ const String File::loadFileAsString() const } //============================================================================== -static inline bool fileTypeMatches (const int whatToLookFor, - const bool isDir, - const bool isHidden) +bool File::fileTypeMatches (const int whatToLookFor, const bool isDir, const bool isHidden) { - return (whatToLookFor & (isDir ? File::findDirectories - : File::findFiles)) != 0 - && ((! isHidden) - || (whatToLookFor & File::ignoreHiddenFiles) == 0); + return (whatToLookFor & (isDir ? findDirectories + : findFiles)) != 0 + && ((! isHidden) || (whatToLookFor & File::ignoreHiddenFiles) == 0); } int File::findChildFiles (Array& results, @@ -672,50 +589,32 @@ int File::findChildFiles (Array& results, int total = 0; - // find child files or directories in this directory first.. if (isDirectory()) { - const String path (juce_addTrailingSeparator (fullPath)); - - String filename; + // find child files or directories in this directory first.. + String path (addTrailingSeparator (fullPath)), filename; bool itemIsDirectory, itemIsHidden; - void* const handle = juce_findFileStart (path, wildCardPattern, filename, - &itemIsDirectory, &itemIsHidden, - 0, 0, 0, 0); + DirectoryIterator::NativeIterator i (path, wildCardPattern); - if (handle != 0) + while (i.next (filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)) { - do + if (! filename.containsOnly (".")) { - if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden) - && ! filename.containsOnly (".")) + const File fileFound (path + filename, 0); + + if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden)) { - results.add (File (path + filename, 0)); + results.add (fileFound); ++total; } - } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)); - - juce_findFileClose (handle); - } - } - else - { - // trying to search for files inside a non-directory? - //jassertfalse - } - - // and recurse down if required. - if (searchRecursively) - { - Array subDirectories; - findChildFiles (subDirectories, File::findDirectories, false); - - for (int i = 0; i < subDirectories.size(); ++i) - { - total += subDirectories.getReference(i).findChildFiles (results, whatToLookFor, - true, wildCardPattern); + if (searchRecursively && itemIsDirectory + && fileTypeMatches (whatToLookFor | findDirectories, true, itemIsHidden)) + { + total += fileFound.findChildFiles (results, whatToLookFor, true, wildCardPattern); + } + } } } @@ -735,24 +634,12 @@ int File::getNumberOfChildFiles (const int whatToLookFor, String filename; bool itemIsDirectory, itemIsHidden; - void* const handle = juce_findFileStart (fullPath, wildCardPattern, filename, - &itemIsDirectory, &itemIsHidden, - 0, 0, 0, 0); + DirectoryIterator::NativeIterator i (*this, wildCardPattern); - if (handle != 0) - { - do - { - if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden) - && ! filename.containsOnly (".")) - { - ++count; - } - - } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)); - - juce_findFileClose (handle); - } + while (i.next (filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)) + if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden) + && ! filename.containsOnly (".")) + ++count; } else { @@ -765,33 +652,19 @@ int File::getNumberOfChildFiles (const int whatToLookFor, bool File::containsSubDirectories() const { - bool result = false; - if (isDirectory()) { String filename; - bool itemIsDirectory, itemIsHidden; - void* const handle = juce_findFileStart (juce_addTrailingSeparator (fullPath), - "*", filename, - &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0); + bool itemIsDirectory; - if (handle != 0) - { - do - { - if (itemIsDirectory) - { - result = true; - break; - } + DirectoryIterator::NativeIterator i (*this, "*"); - } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0)); - - juce_findFileClose (handle); - } + while (i.next (filename, &itemIsDirectory, 0, 0, 0, 0, 0)) + if (itemIsDirectory) + return true; } - return result; + return false; } //============================================================================== @@ -1048,8 +921,8 @@ const String File::getRelativePathFrom (const File& dir) const thisPath [len] = 0; } - String dirPath (juce_addTrailingSeparator ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName() - : dir.fullPath)); + String dirPath (addTrailingSeparator (dir.existsAsFile() ? dir.getParentDirectory().getFullPathName() + : dir.fullPath)); const int len = jmin (thisPath.length(), dirPath.length()); int commonBitLength = 0; @@ -1099,29 +972,6 @@ const String File::getRelativePathFrom (const File& dir) const return thisPath; } -//============================================================================== -void File::findFileSystemRoots (Array& destArray) -{ - const StringArray roots (juce_getFileSystemRoots()); - - for (int i = 0; i < roots.size(); ++i) - destArray.add (File (roots[i])); -} - -const String File::getVolumeLabel() const -{ - int serialNum; - return juce_getVolumeLabel (fullPath, serialNum); -} - -int File::getVolumeSerialNumber() const -{ - int serialNum; - juce_getVolumeLabel (fullPath, serialNum); - - return serialNum; -} - //============================================================================== const File File::createTempFile (const String& fileNameEnding) { diff --git a/src/io/files/juce_File.h b/src/io/files/juce_File.h index 15cf7d0b8e..4b55bf9822 100644 --- a/src/io/files/juce_File.h +++ b/src/io/files/juce_File.h @@ -30,6 +30,7 @@ #include "../../core/juce_Time.h" #include "../../text/juce_StringArray.h" #include "../../containers/juce_MemoryBlock.h" +#include "../../containers/juce_ScopedPointer.h" class FileInputStream; class FileOutputStream; @@ -320,6 +321,10 @@ public: bool operator== (const File& otherFile) const; /** Compares the pathnames for two files. */ bool operator!= (const File& otherFile) const; + /** Compares the pathnames for two files. */ + bool operator< (const File& otherFile) const; + /** Compares the pathnames for two files. */ + bool operator> (const File& otherFile) const; //============================================================================== /** Checks whether a file can be created or written to. @@ -864,7 +869,7 @@ public: On Windows, this will be '\', on Mac/Linux, it'll be '/' */ - static const juce_wchar* separatorString; + static const String separatorString; //============================================================================== /** Removes illegal characters from a filename. @@ -903,6 +908,9 @@ public: */ static const File createFileWithoutCheckingPath (const String& path); + /** Adds a separator character to the end of a path if it doesn't already have one. */ + static const String addTrailingSeparator (const String& path); + //============================================================================== juce_UseDebuggingNewOperator @@ -914,6 +922,10 @@ private: friend class DirectoryIterator; File (const String&, int); const String getPathUpToLastSlash() const; + + void createDirectoryInternal (const String& fileName) const; + static const String parseAbsolutePath (const String& path); + static bool fileTypeMatches (int whatToLookFor, bool isDir, bool isHidden); }; #endif // __JUCE_FILE_JUCEHEADER__ diff --git a/src/io/files/juce_FileInputStream.cpp b/src/io/files/juce_FileInputStream.cpp index 8996280026..4556f6ea14 100644 --- a/src/io/files/juce_FileInputStream.cpp +++ b/src/io/files/juce_FileInputStream.cpp @@ -31,7 +31,7 @@ BEGIN_JUCE_NAMESPACE //============================================================================== -void* juce_fileOpen (const String& path, bool forWriting); +void* juce_fileOpen (const File& file, bool forWriting); void juce_fileClose (void* handle); int juce_fileRead (void* handle, void* buffer, int size); int64 juce_fileSetPosition (void* handle, int64 pos); @@ -45,7 +45,7 @@ FileInputStream::FileInputStream (const File& f) { totalSize = f.getSize(); - fileHandle = juce_fileOpen (f.getFullPathName(), false); + fileHandle = juce_fileOpen (f, false); } FileInputStream::~FileInputStream() diff --git a/src/io/files/juce_FileOutputStream.cpp b/src/io/files/juce_FileOutputStream.cpp index 11fe08ce14..39e1931837 100644 --- a/src/io/files/juce_FileOutputStream.cpp +++ b/src/io/files/juce_FileOutputStream.cpp @@ -29,11 +29,9 @@ BEGIN_JUCE_NAMESPACE #include "juce_FileOutputStream.h" -void* juce_fileOpen (const String& path, bool forWriting); +void* juce_fileOpen (const File& file, bool forWriting); void juce_fileClose (void* handle); int juce_fileWrite (void* handle, const void* buffer, int size); -void juce_fileFlush (void* handle); -int64 juce_fileGetPosition (void* handle); int64 juce_fileSetPosition (void* handle, int64 pos); @@ -44,11 +42,11 @@ FileOutputStream::FileOutputStream (const File& f, bufferSize (bufferSize_), bytesInBuffer (0) { - fileHandle = juce_fileOpen (f.getFullPathName(), true); + fileHandle = juce_fileOpen (f, true); if (fileHandle != 0) { - currentPosition = juce_fileGetPosition (fileHandle); + currentPosition = getPositionInternal(); if (currentPosition < 0) { @@ -92,7 +90,7 @@ void FileOutputStream::flush() bytesInBuffer = 0; } - juce_fileFlush (fileHandle); + flushInternal(); } bool FileOutputStream::write (const void* const src, const int numBytes) diff --git a/src/io/files/juce_FileOutputStream.h b/src/io/files/juce_FileOutputStream.h index 67ac2d1b7b..e15dd4bf5b 100644 --- a/src/io/files/juce_FileOutputStream.h +++ b/src/io/files/juce_FileOutputStream.h @@ -88,6 +88,9 @@ private: int bufferSize, bytesInBuffer; HeapBlock buffer; + void flushInternal(); + int64 getPositionInternal() const; + FileOutputStream (const FileOutputStream&); FileOutputStream& operator= (const FileOutputStream&); }; diff --git a/src/native/common/juce_posix_SharedCode.h b/src/native/common/juce_posix_SharedCode.h index 7d39c754dc..a641439309 100644 --- a/src/native/common/juce_posix_SharedCode.h +++ b/src/native/common/juce_posix_SharedCode.h @@ -188,7 +188,7 @@ void JUCE_CALLTYPE Thread::sleep (int millisecs) //============================================================================== const juce_wchar File::separator = '/'; -const juce_wchar* File::separatorString = L"/"; +const String File::separatorString ("/"); //============================================================================== const File File::getCurrentWorkingDirectory() @@ -215,47 +215,35 @@ bool File::setAsCurrentWorkingDirectory() const } //============================================================================== -bool juce_copyFile (const String& s, const String& d); - static bool juce_stat (const String& fileName, struct stat& info) { return fileName.isNotEmpty() && (stat (fileName.toUTF8(), &info) == 0); } -bool juce_isDirectory (const String& fileName) +bool File::isDirectory() const { struct stat info; - return fileName.isEmpty() - || (juce_stat (fileName, info) - && ((info.st_mode & S_IFDIR) != 0)); + return fullPath.isEmpty() + || (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0)); } -bool juce_fileExists (const String& fileName, const bool dontCountDirectories) +bool File::exists() const { - if (fileName.isEmpty()) - return false; - - const char* const fileNameUTF8 = fileName.toUTF8(); - bool exists = access (fileNameUTF8, F_OK) == 0; - - if (exists && dontCountDirectories) - { - struct stat info; - const int res = stat (fileNameUTF8, &info); - - if (res == 0 && (info.st_mode & S_IFDIR) != 0) - exists = false; - } - - return exists; + return fullPath.isNotEmpty() + && access (fullPath.toUTF8(), F_OK) == 0; } -int64 juce_getFileSize (const String& fileName) +bool File::existsAsFile() const +{ + return exists() && ! isDirectory(); +} + +int64 File::getSize() const { struct stat info; - return juce_stat (fileName, info) ? info.st_size : 0; + return juce_stat (fullPath, info) ? info.st_size : 0; } //============================================================================== @@ -264,14 +252,36 @@ bool juce_canWriteToFile (const String& fileName) return access (fileName.toUTF8(), W_OK) == 0; } -bool juce_deleteFile (const String& fileName) +bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) { - if (juce_isDirectory (fileName)) - return rmdir (fileName.toUTF8()) == 0; + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res != 0) + return false; + + info.st_mode &= 0777; // Just permissions + + if (isReadOnly) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); else - return remove (fileName.toUTF8()) == 0; + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + return chmod (fileName.toUTF8(), info.st_mode) == 0; } +bool File::deleteFile() const +{ + if (! exists()) + return true; + else if (isDirectory()) + return rmdir (fullPath.toUTF8()) == 0; + else + return remove (fullPath.toUTF8()) == 0; +} + +bool juce_copyFile (const String& s, const String& d); + bool juce_moveFile (const String& source, const String& dest) { if (rename (source.toUTF8(), dest.toUTF8()) == 0) @@ -280,29 +290,29 @@ bool juce_moveFile (const String& source, const String& dest) if (juce_canWriteToFile (source) && juce_copyFile (source, dest)) { - if (juce_deleteFile (source)) + if (File (source).deleteFile()) return true; - juce_deleteFile (dest); + File (dest).deleteFile(); } return false; } -void juce_createDirectory (const String& fileName) +void File::createDirectoryInternal (const String& fileName) const { mkdir (fileName.toUTF8(), 0777); } -void* juce_fileOpen (const String& fileName, bool forWriting) +void* juce_fileOpen (const File& file, bool forWriting) { int flags = O_RDONLY; if (forWriting) { - if (juce_fileExists (fileName, false)) + if (file.exists()) { - const int f = open (fileName.toUTF8(), O_RDWR, 00644); + const int f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644); if (f != -1) lseek (f, 0, SEEK_END); @@ -315,7 +325,7 @@ void* juce_fileOpen (const String& fileName, bool forWriting) } } - return (void*) open (fileName.toUTF8(), flags, 00644); + return (void*) open (file.getFullPathName().toUTF8(), flags, 00644); } void juce_fileClose (void* handle) @@ -348,18 +358,18 @@ int64 juce_fileSetPosition (void* handle, int64 pos) return -1; } -int64 juce_fileGetPosition (void* handle) +int64 FileOutputStream::getPositionInternal() const { - if (handle != 0) - return lseek ((int) (pointer_sized_int) handle, 0, SEEK_CUR); + if (fileHandle != 0) + return lseek ((int) (pointer_sized_int) fileHandle, 0, SEEK_CUR); return -1; } -void juce_fileFlush (void* handle) +void FileOutputStream::flushInternal() { - if (handle != 0) - fsync ((int) (pointer_sized_int) handle); + if (fileHandle != 0) + fsync ((int) (pointer_sized_int) fileHandle); } const File juce_getExecutableFile() @@ -371,10 +381,8 @@ const File juce_getExecutableFile() //============================================================================== // if this file doesn't exist, find a parent of it that does.. -static bool doStatFS (const File* file, struct statfs& result) +static bool juce_doStatFS (File f, struct statfs& result) { - File f (*file); - for (int i = 5; --i >= 0;) { if (f.exists()) @@ -389,7 +397,7 @@ static bool doStatFS (const File* file, struct statfs& result) int64 File::getBytesFreeOnVolume() const { struct statfs buf; - if (doStatFS (this, buf)) + if (juce_doStatFS (*this, buf)) return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user return 0; @@ -398,17 +406,14 @@ int64 File::getBytesFreeOnVolume() const int64 File::getVolumeTotalSize() const { struct statfs buf; - if (doStatFS (this, buf)) + if (juce_doStatFS (*this, buf)) return (int64) buf.f_bsize * (int64) buf.f_blocks; return 0; } -const String juce_getVolumeLabel (const String& filenameOnVolume, - int& volumeSerialNumber) +const String File::getVolumeLabel() const { - volumeSerialNumber = 0; - #if JUCE_MAC struct VolAttrBuf { @@ -422,16 +427,13 @@ const String juce_getVolumeLabel (const String& filenameOnVolume, attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; - File f (filenameOnVolume); + File f (*this); for (;;) { - if (getattrlist (f.getFullPathName().toUTF8(), - &attrList, &attrBuf, sizeof(attrBuf), 0) == 0) - { + if (getattrlist (f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof (attrBuf), 0) == 0) return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset, (int) attrBuf.mountPointRef.attr_length); - } const File parent (f.getParentDirectory()); @@ -445,6 +447,10 @@ const String juce_getVolumeLabel (const String& filenameOnVolume, return String::empty; } +int File::getVolumeSerialNumber() const +{ + return 0; // xxx +} //============================================================================== void juce_runSystemCommand (const String& command) diff --git a/src/native/juce_mac_NativeCode.mm b/src/native/juce_mac_NativeCode.mm index 65faebf679..4c072a0896 100644 --- a/src/native/juce_mac_NativeCode.mm +++ b/src/native/juce_mac_NativeCode.mm @@ -46,7 +46,9 @@ BEGIN_JUCE_NAMESPACE #include "../threads/juce_Thread.h" #include "../threads/juce_InterProcessLock.h" #include "../io/files/juce_FileInputStream.h" +#include "../io/files/juce_FileOutputStream.h" #include "../io/files/juce_NamedPipe.h" +#include "../io/files/juce_DirectoryIterator.h" #include "../io/network/juce_URL.h" #include "../io/streams/juce_MemoryInputStream.h" #include "../core/juce_PlatformUtilities.h" diff --git a/src/native/juce_win32_NativeCode.cpp b/src/native/juce_win32_NativeCode.cpp index 58358c033f..474ce1bd37 100644 --- a/src/native/juce_win32_NativeCode.cpp +++ b/src/native/juce_win32_NativeCode.cpp @@ -47,7 +47,9 @@ BEGIN_JUCE_NAMESPACE #include "../threads/juce_Thread.h" #include "../threads/juce_InterProcessLock.h" #include "../io/files/juce_FileInputStream.h" +#include "../io/files/juce_FileOutputStream.h" #include "../io/files/juce_NamedPipe.h" +#include "../io/files/juce_DirectoryIterator.h" #include "../io/network/juce_URL.h" #include "../core/juce_PlatformUtilities.h" #include "../text/juce_LocalisedStrings.h" diff --git a/src/native/linux/juce_linux_Files.cpp b/src/native/linux/juce_linux_Files.cpp index 1d56528e4f..1a15b74ebe 100644 --- a/src/native/linux/juce_linux_Files.cpp +++ b/src/native/linux/juce_linux_Files.cpp @@ -64,24 +64,6 @@ bool juce_setFileTimes (const String& fileName, return utime (fileName.toUTF8(), ×) == 0; } -bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) -{ - struct stat info; - const int res = stat (fileName.toUTF8(), &info); - if (res != 0) - return false; - - info.st_mode &= 0777; // Just permissions - - if( isReadOnly ) - info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - else - // Give everybody write permission? - info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - - return chmod (fileName.toUTF8(), info.st_mode) == 0; -} - bool juce_copyFile (const String& s, const String& d) { const File source (s), dest (d); @@ -113,11 +95,9 @@ bool juce_copyFile (const String& s, const String& d) return ok; } -const StringArray juce_getFileSystemRoots() +void File::findFileSystemRoots (Array& destArray) { - StringArray s; - s.add ("/"); - return s; + destArray.add (File ("/")); } //============================================================================== @@ -275,42 +255,54 @@ bool File::moveToTrash() const } //============================================================================== -struct FindFileStruct +class DirectoryIterator::NativeIterator::Pimpl { - String parentDir, wildCard; - DIR* dir; - - bool getNextMatch (String& result, bool* const isDir, bool* const isHidden, int64* const fileSize, - Time* const modTime, Time* const creationTime, bool* const isReadOnly) +public: + Pimpl (const File& directory, const String& wildCard_) + : parentDir (File::addTrailingSeparator (directory.getFullPathName())), + wildCard (wildCard_), + dir (opendir (directory.getFullPathName().toUTF8())) { - const char* const wildcardUTF8 = wildCard.toUTF8(); + if (wildCard == "*.*") + wildCard = "*"; + + wildcardUTF8 = wildCard.toUTF8(); + } + + ~Pimpl() + { + if (dir != 0) + closedir (dir); + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + if (dir == 0) + return false; for (;;) { struct dirent* const de = readdir (dir); if (de == 0) - break; + return false; if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) { - result = String::fromUTF8 (de->d_name); + filenameFound = String::fromUTF8 (de->d_name); - const String path (parentDir + result); + const String path (parentDir + filenameFound); if (isDir != 0 || fileSize != 0) { struct stat info; const bool statOk = (stat (path.toUTF8(), &info) == 0); - if (isDir != 0) - *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0)); - - if (isHidden != 0) - *isHidden = (de->d_name[0] == '.'); - - if (fileSize != 0) - *fileSize = statOk ? info.st_size : 0; + if (isDir != 0) *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0)); + if (isHidden != 0) *isHidden = (de->d_name[0] == '.'); + if (fileSize != 0) *fileSize = statOk ? info.st_size : 0; } if (modTime != 0 || creationTime != 0) @@ -318,11 +310,8 @@ struct FindFileStruct int64 m, a, c; juce_getFileTimes (path, m, a, c); - if (modTime != 0) - *modTime = m; - - if (creationTime != 0) - *creationTime = c; + if (modTime != 0) *modTime = m; + if (creationTime != 0) *creationTime = c; } if (isReadOnly != 0) @@ -331,71 +320,35 @@ struct FindFileStruct return true; } } - - return false; } + +private: + String parentDir, wildCard; + const char* wildcardUTF8; + DIR* dir; + + Pimpl (const Pimpl&); + Pimpl& operator= (const Pimpl&); }; -// returns 0 on failure -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, - Time* creationTime, bool* isReadOnly) +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) { - DIR* d = opendir (directory.toUTF8()); - - if (d != 0) - { - FindFileStruct* ff = new FindFileStruct(); - ff->parentDir = directory; - - if (!ff->parentDir.endsWithChar (File::separator)) - ff->parentDir += File::separator; - - ff->wildCard = wildCard; - if (wildCard == "*.*") - ff->wildCard = "*"; - - ff->dir = d; - - if (ff->getNextMatch (firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) - { - return ff; - } - else - { - firstResultFile = String::empty; - isDir = false; - isHidden = false; - closedir (d); - delete ff; - } - } - - return 0; } -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) +DirectoryIterator::NativeIterator::~NativeIterator() { - FindFileStruct* const ff = (FindFileStruct*) handle; - - if (ff != 0) - return ff->getNextMatch (resultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); - - return false; } -void juce_findFileClose (void* handle) +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) { - FindFileStruct* const ff = (FindFileStruct*) handle; - - if (ff != 0) - { - closedir (ff->dir); - delete ff; - } + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); } + +//============================================================================== bool juce_launchFile (const String& fileName, const String& parameters) { diff --git a/src/native/mac/juce_mac_Files.mm b/src/native/mac/juce_mac_Files.mm index 9772bb1ff0..50010f1104 100644 --- a/src/native/mac/juce_mac_Files.mm +++ b/src/native/mac/juce_mac_Files.mm @@ -64,24 +64,6 @@ bool juce_setFileTimes (const String& fileName, return utime (fileName.toUTF8(), ×) == 0; } -bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) -{ - struct stat info; - const int res = stat (fileName.toUTF8(), &info); - if (res != 0) - return false; - - info.st_mode &= 0777; // Just permissions - - if (isReadOnly) - info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - else - // Give everybody write permission? - info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - - return chmod (fileName.toUTF8(), info.st_mode) == 0; -} - bool juce_copyFile (const String& src, const String& dst) { const ScopedAutoReleasePool pool; @@ -99,19 +81,18 @@ bool juce_copyFile (const String& src, const String& dst) #endif } -const StringArray juce_getFileSystemRoots() +void File::findFileSystemRoots (Array& destArray) { - StringArray s; - s.add ("/"); - return s; + destArray.add (File ("/")); } + //============================================================================== -static bool isFileOnDriveType (const File* const f, const char** types) +static bool isFileOnDriveType (const File& f, const char** types) { struct statfs buf; - if (doStatFS (f, buf)) + if (juce_doStatFS (f, buf)) { const String type (buf.f_fstypename); @@ -127,14 +108,14 @@ bool File::isOnCDRomDrive() const { static const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", 0 }; - return isFileOnDriveType (this, (const char**) cdTypes); + return isFileOnDriveType (*this, (const char**) cdTypes); } bool File::isOnHardDisk() const { static const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", 0 }; - return ! (isOnCDRomDrive() || isFileOnDriveType (this, (const char**) nonHDTypes)); + return ! (isOnCDRomDrive() || isFileOnDriveType (*this, (const char**) nonHDTypes)); } bool File::isOnRemovableDrive() const @@ -324,100 +305,98 @@ bool File::moveToTrash() const } //============================================================================== -struct FindFileStruct +class DirectoryIterator::NativeIterator::Pimpl { - NSDirectoryEnumerator* enumerator; +public: + Pimpl (const File& directory, const String& wildCard_) + : parentDir (File::addTrailingSeparator (directory.getFullPathName())), + wildCard (wildCard_), + enumerator (0) + { + ScopedAutoReleasePool pool; + + enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory.getFullPathName())] retain]; + + wildcardUTF8 = wildCard.toUTF8(); + } + + ~Pimpl() + { + [enumerator release]; + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + ScopedAutoReleasePool pool; + + for (;;) + { + NSString* file; + if (enumerator == 0 || (file = [enumerator nextObject]) == 0) + return false; + + [enumerator skipDescendents]; + filenameFound = nsStringToJuce (file); + + if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0) + continue; + + const String path (parentDir + filenameFound); + + if (isDir != 0 || fileSize != 0) + { + struct stat info; + const bool statOk = juce_stat (path, info); + + if (isDir != 0) *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); + if (isHidden != 0) *isHidden = juce_isHiddenFile (path); + if (fileSize != 0) *fileSize = statOk ? info.st_size : 0; + } + + if (modTime != 0 || creationTime != 0) + { + int64 m, a, c; + juce_getFileTimes (path, m, a, c); + + if (modTime != 0) *modTime = m; + if (creationTime != 0) *creationTime = c; + } + + if (isReadOnly != 0) + *isReadOnly = ! juce_canWriteToFile (path); + + return true; + } + } + +private: String parentDir, wildCard; + const char* wildcardUTF8; + NSDirectoryEnumerator* enumerator; + + Pimpl (const Pimpl&); + Pimpl& operator= (const Pimpl&); }; -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) { - ScopedAutoReleasePool pool; - FindFileStruct* ff = (FindFileStruct*) handle; - NSString* file; - const char* const wildcardUTF8 = ff->wildCard.toUTF8(); - - for (;;) - { - if (ff == 0 || (file = [ff->enumerator nextObject]) == 0) - return false; - - [ff->enumerator skipDescendents]; - resultFile = nsStringToJuce (file); - - if (fnmatch (wildcardUTF8, resultFile.toUTF8(), FNM_CASEFOLD) != 0) - continue; - - const String path (ff->parentDir + resultFile); - - if (isDir != 0 || fileSize != 0) - { - struct stat info; - const bool statOk = juce_stat (path, info); - - if (isDir != 0) - *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); - - if (isHidden != 0) - *isHidden = juce_isHiddenFile (path); - - if (fileSize != 0) - *fileSize = statOk ? info.st_size : 0; - } - - if (modTime != 0 || creationTime != 0) - { - int64 m, a, c; - juce_getFileTimes (path, m, a, c); - - if (modTime != 0) - *modTime = m; - - if (creationTime != 0) - *creationTime = c; - } - - if (isReadOnly != 0) - *isReadOnly = ! juce_canWriteToFile (path); - - return true; - } } -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, - bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, - Time* creationTime, bool* isReadOnly) +DirectoryIterator::NativeIterator::~NativeIterator() { - ScopedAutoReleasePool pool; - NSDirectoryEnumerator* e = [[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory)]; - - if (e != 0) - { - ScopedPointer ff (new FindFileStruct()); - ff->enumerator = [e retain]; - ff->parentDir = directory; - ff->wildCard = wildCard; - - if (! ff->parentDir.endsWithChar (File::separator)) - ff->parentDir += File::separator; - - if (juce_findFileNext (ff, firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) - return ff.release(); - - [e release]; - } - - return 0; } -void juce_findFileClose (void* handle) +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) { - ScopedAutoReleasePool pool; - ScopedPointer ff ((FindFileStruct*) handle); - [ff->enumerator release]; + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); } + //============================================================================== bool juce_launchExecutable (const String& pathAndArguments) { diff --git a/src/native/windows/juce_win32_Files.cpp b/src/native/windows/juce_win32_Files.cpp index 98af5d5378..02fa7bbc2b 100644 --- a/src/native/windows/juce_win32_Files.cpp +++ b/src/native/windows/juce_win32_Files.cpp @@ -42,24 +42,26 @@ #endif //============================================================================== -const juce_wchar File::separator = '\\'; -const juce_wchar* File::separatorString = T("\\"); +const juce_wchar File::separator = '\\'; +const String File::separatorString ("\\"); //============================================================================== -bool juce_fileExists (const String& fileName, const bool dontCountDirectories) +bool File::exists() const { - if (fileName.isEmpty()) - return false; - - const DWORD attr = GetFileAttributes (fileName); - return dontCountDirectories ? ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) - : (attr != INVALID_FILE_ATTRIBUTES); + return fullPath.isNotEmpty() + && GetFileAttributes (fullPath) != INVALID_FILE_ATTRIBUTES; } -bool juce_isDirectory (const String& fileName) +bool File::existsAsFile() const { - const DWORD attr = GetFileAttributes (fileName); + return fullPath.isNotEmpty() + && (GetFileAttributes (fullPath) & FILE_ATTRIBUTE_DIRECTORY) == 0; +} + +bool File::isDirectory() const +{ + const DWORD attr = GetFileAttributes (fullPath); return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) && (attr != INVALID_FILE_ATTRIBUTES); } @@ -93,12 +95,14 @@ bool File::isHidden() const } //============================================================================== -bool juce_deleteFile (const String& fileName) +bool File::deleteFile() const { - if (juce_isDirectory (fileName)) - return RemoveDirectory (fileName) != 0; - - return DeleteFile (fileName) != 0; + if (! exists()) + return true; + else if (isDirectory()) + return RemoveDirectory (fullPath) != 0; + else + return DeleteFile (fullPath) != 0; } bool File::moveToTrash() const @@ -132,21 +136,20 @@ bool juce_copyFile (const String& source, const String& dest) return CopyFile (source, dest, false) != 0; } -void juce_createDirectory (const String& fileName) +void File::createDirectoryInternal (const String& fileName) const { - if (! juce_fileExists (fileName, true)) - CreateDirectory (fileName, 0); + CreateDirectory (fileName, 0); } //============================================================================== // return 0 if not possible -void* juce_fileOpen (const String& fileName, bool forWriting) +void* juce_fileOpen (const File& file, bool forWriting) { HANDLE h; if (forWriting) { - h = CreateFile (fileName, GENERIC_WRITE, FILE_SHARE_READ, 0, + h = CreateFile (file.getFullPathName(), GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (h != INVALID_HANDLE_VALUE) @@ -156,7 +159,7 @@ void* juce_fileOpen (const String& fileName, bool forWriting) } else { - h = CreateFile (fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + h = CreateFile (file.getFullPathName(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); if (h == INVALID_HANDLE_VALUE) @@ -194,24 +197,28 @@ int64 juce_fileSetPosition (void* handle, int64 pos) return li.QuadPart; } -int64 juce_fileGetPosition (void* handle) +int64 FileOutputStream::getPositionInternal() const { + if (fileHandle == 0) + return -1; + LARGE_INTEGER li; li.QuadPart = 0; - li.LowPart = SetFilePointer ((HANDLE) handle, 0, &li.HighPart, FILE_CURRENT); // (returns -1 if it fails) + li.LowPart = SetFilePointer ((HANDLE) fileHandle, 0, &li.HighPart, FILE_CURRENT); // (returns -1 if it fails) return jmax ((int64) 0, li.QuadPart); } -void juce_fileFlush (void* handle) +void FileOutputStream::flushInternal() { - FlushFileBuffers ((HANDLE) handle); + if (fileHandle != 0) + FlushFileBuffers ((HANDLE) fileHandle); } -int64 juce_getFileSize (const String& fileName) +int64 File::getSize() const { WIN32_FILE_ATTRIBUTE_DATA attributes; - if (GetFileAttributesEx (fileName, GetFileExInfoStandard, &attributes)) + if (GetFileAttributesEx (fullPath, GetFileExInfoStandard, &attributes)) return (((int64) attributes.nFileSizeHigh) << 32) | attributes.nFileSizeLow; return 0; @@ -276,8 +283,7 @@ bool juce_setFileTimes (const String& fileName, } //============================================================================== -// return '\0' separated list of strings -const StringArray juce_getFileSystemRoots() +void File::findFileSystemRoots (Array& destArray) { TCHAR buffer [2048]; buffer[0] = 0; @@ -296,7 +302,9 @@ const StringArray juce_getFileSystemRoots() } roots.sort (true); - return roots; + + for (int i = 0; i < roots.size(); ++i) + destArray.add (roots [i]); } //============================================================================== @@ -308,21 +316,26 @@ static const String getDriveFromPath (const String& path) return path; } -const String juce_getVolumeLabel (const String& filenameOnVolume, - int& volumeSerialNumber) +const String File::getVolumeLabel() const +{ + TCHAR dest[64]; + if (! GetVolumeInformation (getDriveFromPath (getFullPathName()), dest, + numElementsInArray (dest), 0, 0, 0, 0, 0)) + dest[0] = 0; + + return dest; +} + +int File::getVolumeSerialNumber() const { TCHAR dest[64]; DWORD serialNum; - if (! GetVolumeInformation (getDriveFromPath (filenameOnVolume), dest, + if (! GetVolumeInformation (getDriveFromPath (getFullPathName()), dest, numElementsInArray (dest), &serialNum, 0, 0, 0, 0)) - { - dest[0] = 0; - serialNum = 0; - } + return 0; - volumeSerialNumber = serialNum; - return dest; + return (int) serialNum; } static int64 getDiskSpaceInfo (const String& path, const bool total) @@ -513,80 +526,79 @@ const File File::getLinkedTarget() const //============================================================================== -template -static void getFindFileInfo (FindDataType& findData, - String& filename, bool* const isDir, bool* const isHidden, - int64* const fileSize, Time* const modTime, Time* const creationTime, - bool* const isReadOnly) +class DirectoryIterator::NativeIterator::Pimpl { - filename = findData.cFileName; - - if (isDir != 0) - *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); - - if (isHidden != 0) - *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0); - - if (fileSize != 0) - *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32); - - if (modTime != 0) - *modTime = fileTimeToTime (&findData.ftLastWriteTime); - - if (creationTime != 0) - *creationTime = fileTimeToTime (&findData.ftCreationTime); - - if (isReadOnly != 0) - *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); -} - -void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResult, - bool* isDir, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly) -{ - String wc (directory); - - if (! wc.endsWithChar (File::separator)) - wc += File::separator; - - wc += wildCard; - - WIN32_FIND_DATA findData; - HANDLE h = FindFirstFile (wc, &findData); - - if (h != INVALID_HANDLE_VALUE) +public: + Pimpl (const File& directory, const String& wildCard) + : directoryWithWildCard (File::addTrailingSeparator (directory.getFullPathName()) + wildCard), + handle (INVALID_HANDLE_VALUE) { - getFindFileInfo (findData, firstResult, isDir, isHidden, fileSize, - modTime, creationTime, isReadOnly); - return h; } - firstResult = String::empty; - return 0; -} - -bool juce_findFileNext (void* handle, String& resultFile, - bool* isDir, bool* isHidden, int64* fileSize, - Time* modTime, Time* creationTime, bool* isReadOnly) -{ - WIN32_FIND_DATA findData; - - if (handle != 0 && FindNextFile ((HANDLE) handle, &findData) != 0) + ~Pimpl() { - getFindFileInfo (findData, resultFile, isDir, isHidden, fileSize, - modTime, creationTime, isReadOnly); + if (handle != INVALID_HANDLE_VALUE) + FindClose (handle); + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + WIN32_FIND_DATA findData; + + if (handle == INVALID_HANDLE_VALUE) + { + handle = FindFirstFile (directoryWithWildCard, &findData); + + if (handle == INVALID_HANDLE_VALUE) + return false; + } + else + { + if (FindNextFile (handle, &findData) == 0) + return false; + } + + filenameFound = findData.cFileName; + + if (isDir != 0) *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); + if (isHidden != 0) *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0); + if (fileSize != 0) *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32); + if (modTime != 0) *modTime = fileTimeToTime (&findData.ftLastWriteTime); + if (creationTime != 0) *creationTime = fileTimeToTime (&findData.ftCreationTime); + if (isReadOnly != 0) *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); + return true; } - resultFile = String::empty; - return false; + juce_UseDebuggingNewOperator + +private: + const String directoryWithWildCard; + HANDLE handle; + + Pimpl (const Pimpl&); + Pimpl& operator= (const Pimpl&); +}; + +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) +{ } -void juce_findFileClose (void* handle) +DirectoryIterator::NativeIterator::~NativeIterator() { - FindClose (handle); } +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) +{ + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); +} + + //============================================================================== bool juce_launchFile (const String& fileName, const String& parameters) { diff --git a/src/text/juce_String.cpp b/src/text/juce_String.cpp index 088c314109..3e20c49e76 100644 --- a/src/text/juce_String.cpp +++ b/src/text/juce_String.cpp @@ -1542,10 +1542,7 @@ const String String::trimCharactersAtStart (const String& charactersToTrim) cons while (charactersToTrim.containsChar (*t)) ++t; - if (t == text) - return *this; - - return String (t); + return t == text ? *this : String (t); } const String String::trimCharactersAtEnd (const String& charactersToTrim) const @@ -1553,12 +1550,17 @@ const String String::trimCharactersAtEnd (const String& charactersToTrim) const if (isEmpty()) return empty; - const juce_wchar* endT = text + (length() - 1); + const int len = length(); + const juce_wchar* endT = text + (len - 1); + int numToRemove = 0; - while (endT >= text && charactersToTrim.containsChar (*endT)) + while (numToRemove < len && charactersToTrim.containsChar (*endT)) + { + ++numToRemove; --endT; + } - return String (text, (int) (++endT - text)); + return numToRemove > 0 ? String (text, len - numToRemove) : *this; } //============================================================================== diff --git a/src/text/juce_StringArray.cpp b/src/text/juce_StringArray.cpp index 2b88bba2d6..c27a6f5995 100644 --- a/src/text/juce_StringArray.cpp +++ b/src/text/juce_StringArray.cpp @@ -461,10 +461,10 @@ void StringArray::appendNumbersToDuplicates (const bool ignoreCase, const juce_wchar* postNumberString) { if (preNumberString == 0) - preNumberString = T(" ("); + preNumberString = L" ("; if (postNumberString == 0) - postNumberString = T(")"); + postNumberString = L")"; for (int i = 0; i < size() - 1; ++i) { diff --git a/src/text/juce_XmlDocument.cpp b/src/text/juce_XmlDocument.cpp index 40f4ef4a03..f2f71604b9 100644 --- a/src/text/juce_XmlDocument.cpp +++ b/src/text/juce_XmlDocument.cpp @@ -759,9 +759,7 @@ const String XmlDocument::expandExternalEntity (const String& entity) { if (dtdText.isNotEmpty()) { - while (dtdText.endsWithChar ('>')) - dtdText = dtdText.dropLastCharacters (1); - + dtdText = dtdText.trimCharactersAtEnd (">"); tokenisedDTD.addTokens (dtdText, true); if (tokenisedDTD [tokenisedDTD.size() - 2].equalsIgnoreCase ("system") @@ -813,12 +811,7 @@ const String XmlDocument::expandExternalEntity (const String& entity) { if (tokenisedDTD[i - 1].equalsIgnoreCase ("')) - ent = ent.dropLastCharacters (1); - - ent = ent.trim().unquoted(); + String ent (tokenisedDTD [i + 1].trimCharactersAtEnd (">").trim().unquoted()); // check for sub-entities.. int ampersand = ent.indexOfChar ('&'); @@ -861,24 +854,12 @@ const String XmlDocument::getParameterEntity (const String& entity) if (tokenisedDTD [i - 1] == "%" && tokenisedDTD [i - 2].equalsIgnoreCase ("')) - ent = ent.dropLastCharacters (1); + const String ent (tokenisedDTD [i + 1].trimCharactersAtEnd (">")); if (ent.equalsIgnoreCase ("system")) - { - String filename (tokenisedDTD [i + 2]); - - while (filename.endsWithChar ('>')) - filename = filename.dropLastCharacters (1); - - return getFileContents (filename); - } + return getFileContents (tokenisedDTD [i + 2].trimCharactersAtEnd (">")); else - { return ent.trim().unquoted(); - } } } } diff --git a/src/text/juce_XmlElement.cpp b/src/text/juce_XmlElement.cpp index 6b36cb3dd9..87bed1d850 100644 --- a/src/text/juce_XmlElement.cpp +++ b/src/text/juce_XmlElement.cpp @@ -1078,7 +1078,7 @@ bool XmlElement::isTextElement() const throw() return tagName.isEmpty(); } -static const juce_wchar* const juce_xmltextContentAttributeName = T("text"); +static const juce_wchar* const juce_xmltextContentAttributeName = L"text"; const String XmlElement::getText() const throw() {