diff --git a/extras/Projucer/Builds/LinuxMakefile/Makefile b/extras/Projucer/Builds/LinuxMakefile/Makefile index d47f707caf..49693e7808 100644 --- a/extras/Projucer/Builds/LinuxMakefile/Makefile +++ b/extras/Projucer/Builds/LinuxMakefile/Makefile @@ -114,7 +114,6 @@ OBJECTS_APP := \ $(JUCE_OBJDIR)/jucer_FileHelpers_54f12f83.o \ $(JUCE_OBJDIR)/jucer_MiscUtilities_31fc8dd8.o \ $(JUCE_OBJDIR)/jucer_PIPGenerator_fd3402c7.o \ - $(JUCE_OBJDIR)/jucer_DependencyPathPropertyComponent_5d68c773.o \ $(JUCE_OBJDIR)/jucer_Icons_d02d18f1.o \ $(JUCE_OBJDIR)/jucer_JucerTreeViewBase_9b9f2ff0.o \ $(JUCE_OBJDIR)/jucer_ProjucerLookAndFeel_3b20291d.o \ @@ -372,11 +371,6 @@ $(JUCE_OBJDIR)/jucer_PIPGenerator_fd3402c7.o: ../../Source/Utility/PIPs/jucer_PI @echo "Compiling jucer_PIPGenerator.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" -$(JUCE_OBJDIR)/jucer_DependencyPathPropertyComponent_5d68c773.o: ../../Source/Utility/UI/PropertyComponents/jucer_DependencyPathPropertyComponent.cpp - -$(V_AT)mkdir -p $(JUCE_OBJDIR) - @echo "Compiling jucer_DependencyPathPropertyComponent.cpp" - $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" - $(JUCE_OBJDIR)/jucer_Icons_d02d18f1.o: ../../Source/Utility/UI/jucer_Icons.cpp -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling jucer_Icons.cpp" diff --git a/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj b/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj index af18c8c8f5..beded00060 100644 --- a/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj +++ b/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj @@ -261,10 +261,6 @@ isa = PBXBuildFile; fileRef = 191330B20DAC08B890656EA0; }; - 2BEC1197D981951D8A897F01 = { - isa = PBXBuildFile; - fileRef = D859E9EA11A71BD6E85DC649; - }; D0E26EB54B0087C8BE3D541E = { isa = PBXBuildFile; fileRef = 846B2A670C5A19DE0039E11A; @@ -2271,13 +2267,6 @@ path = "../../JuceLibraryCode/include_juce_cryptography.mm"; sourceTree = "SOURCE_ROOT"; }; - D859E9EA11A71BD6E85DC649 = { - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = "jucer_DependencyPathPropertyComponent.cpp"; - path = "../../Source/Utility/UI/PropertyComponents/jucer_DependencyPathPropertyComponent.cpp"; - sourceTree = "SOURCE_ROOT"; - }; D91E7F8FEF9290195D56782C = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; @@ -2551,13 +2540,6 @@ path = "../../Source/BinaryData/Icons/wizard_Highlight.svg"; sourceTree = "SOURCE_ROOT"; }; - F8F94093A0963D86BD27A95D = { - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = "jucer_DependencyPathPropertyComponent.h"; - path = "../../Source/Utility/UI/PropertyComponents/jucer_DependencyPathPropertyComponent.h"; - sourceTree = "SOURCE_ROOT"; - }; F9111E150CFF155329D44853 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; @@ -3039,8 +3021,6 @@ isa = PBXGroup; children = ( 9EB33734D0DBD0370AB1247B, - D859E9EA11A71BD6E85DC649, - F8F94093A0963D86BD27A95D, E367FC2BDAF5EBA48D767FBB, 59F8A47C0020D62C8836A1E7, ); @@ -3463,7 +3443,6 @@ BF913199032B4CE970E82AA3, 25EF9B3FECB4C9F0F522DCAA, 638C7247B6DBA67EFE46E124, - 2BEC1197D981951D8A897F01, D0E26EB54B0087C8BE3D541E, 468548FB21D264DC12321327, 6ECB2F11D2F593FACCCF99DB, diff --git a/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj index 61425b1716..6c3af6c3f2 100644 --- a/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj @@ -240,7 +240,6 @@ - @@ -1627,7 +1626,6 @@ - diff --git a/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj.filters index 5d368f4831..90be4e3a3e 100644 --- a/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj.filters @@ -496,9 +496,6 @@ Projucer\Utility\PIPs - - Projucer\Utility\UI\PropertyComponents - Projucer\Utility\UI @@ -2325,9 +2322,6 @@ Projucer\Utility\UI\PropertyComponents - - Projucer\Utility\UI\PropertyComponents - Projucer\Utility\UI\PropertyComponents diff --git a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj index b2e5ec6293..e7ff40d31f 100644 --- a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj @@ -240,7 +240,6 @@ - @@ -1627,7 +1626,6 @@ - diff --git a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters index b6c6ad13f0..5c48a9bdfc 100644 --- a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters @@ -496,9 +496,6 @@ Projucer\Utility\PIPs - - Projucer\Utility\UI\PropertyComponents - Projucer\Utility\UI @@ -2325,9 +2322,6 @@ Projucer\Utility\UI\PropertyComponents - - Projucer\Utility\UI\PropertyComponents - Projucer\Utility\UI\PropertyComponents diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj index 8fb4cea3e6..ef4a28a3e6 100644 --- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj @@ -242,7 +242,6 @@ - @@ -1629,7 +1628,6 @@ - diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters index 9913ba7dd8..465bbc299d 100644 --- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters @@ -496,9 +496,6 @@ Projucer\Utility\PIPs - - Projucer\Utility\UI\PropertyComponents - Projucer\Utility\UI @@ -2325,9 +2322,6 @@ Projucer\Utility\UI\PropertyComponents - - Projucer\Utility\UI\PropertyComponents - Projucer\Utility\UI\PropertyComponents diff --git a/extras/Projucer/Projucer.jucer b/extras/Projucer/Projucer.jucer index fc8483a550..55909bd625 100644 --- a/extras/Projucer/Projucer.jucer +++ b/extras/Projucer/Projucer.jucer @@ -655,10 +655,6 @@ - - (pathValue.getValueSource())) -{ - bool initialValueIsEmpty = ! pathValueSource.isUsingProjectSettings(); - - getValue().referTo (pathValue); - - // the following step is necessary because the above referTo() has internally called setValue(), - // which has set the project value to whatever is displayed in the label (this may be the - // global/fallback value). In this case we have to reset the project value to blank: - if (initialValueIsEmpty) - getValue().setValue (String()); - - getValue().addListener (this); - - if (auto* label = dynamic_cast (getChildComponent (0))) - label->onEditorShow = [this, label] { setEditorText (label); }; - else - jassertfalse; - - lookAndFeelChanged(); -} -catch (const std::bad_cast&) -{ - // a DependencyPathPropertyComponent must be initialised with a Value - // that is referring to a DependencyPathValueSource! - jassertfalse; - throw; -} - -void DependencyPathPropertyComponent::valueChanged (Value& value) -{ - // this callback handles the update of this setting in case - // the user changed the global preferences. - if (value.refersToSameSourceAs (pathValue) && pathValueSource.isUsingGlobalSettings()) - textWasEdited(); -} - -void DependencyPathPropertyComponent::textWasEdited() -{ - setColour (textColourId, getTextColourToDisplay()); - TextPropertyComponent::textWasEdited(); -} - -Colour DependencyPathPropertyComponent::getTextColourToDisplay() const -{ - if (! pathValueSource.isUsingProjectSettings()) - return pathValueSource.isValidPath (pathRelativeTo) ? findColour (widgetTextColourId).withMultipliedAlpha (0.5f) - : Colours::red.withMultipliedAlpha (0.5f); - - return pathValueSource.isValidPath (pathRelativeTo) ? findColour (widgetTextColourId) - : Colours::red; -} - -void DependencyPathPropertyComponent::setEditorText (Label* label) -{ - if (! pathValueSource.isUsingProjectSettings()) - if (auto editor = label->getCurrentTextEditor()) - editor->setText (String(), dontSendNotification); -} - -void DependencyPathPropertyComponent::lookAndFeelChanged() -{ - textWasEdited(); -} - -//============================================================================== -DependencyFilePathPropertyComponent::DependencyFilePathPropertyComponent (Value& value, - const String& propertyDescription, - bool isDir, - const String& wc, - const File& rootToUseForRelativePaths) -try : TextPropertyComponent (propertyDescription, 1024, false), - pathRelativeTo (rootToUseForRelativePaths), - pathValue (value), - pathValueSource (dynamic_cast (pathValue.getValueSource())), - browseButton ("..."), - isDirectory (isDir), - wildcards (wc) -{ - auto initialValueIsEmpty = ! pathValueSource.isUsingProjectSettings(); - - getValue().referTo (pathValue); - - if (initialValueIsEmpty) - getValue().setValue (String()); - - getValue().addListener (this); - - if (auto* label = dynamic_cast (getChildComponent (0))) - label->onEditorShow = [this, label] { setEditorText (label); }; - else - jassertfalse; - - setInterestedInFileDrag (false); - - addAndMakeVisible (browseButton); - browseButton.onClick = [this] { browse(); }; - - lookAndFeelChanged(); -} -catch (const std::bad_cast&) -{ - // a DependencyPathPropertyComponent must be initialised with a Value - // that is referring to a DependencyPathValueSource! - jassertfalse; - throw; -} - -void DependencyFilePathPropertyComponent::resized() -{ - auto bounds = getLookAndFeel().getPropertyComponentContentPosition (*this); - - browseButton.setBounds (bounds.removeFromRight (30)); - getChildComponent (0)->setBounds (bounds); -} - -void DependencyFilePathPropertyComponent::paintOverChildren (Graphics& g) -{ - if (highlightForDragAndDrop) - { - g.setColour (findColour (defaultHighlightColourId).withAlpha (0.5f)); - g.fillRect (getChildComponent (0)->getBounds()); - } -} - -void DependencyFilePathPropertyComponent::filesDropped (const StringArray& files, int, int) -{ - const File firstFile (files[0]); - - if (isDirectory) - setTo (firstFile.isDirectory() ? firstFile - : firstFile.getParentDirectory()); - else - setTo (firstFile); - - highlightForDragAndDrop = false; -} - -void DependencyFilePathPropertyComponent::setTo (const File& f) -{ - pathValue = (pathRelativeTo == File()) ? f.getFullPathName() - : f.getRelativePathFrom (pathRelativeTo); - - textWasEdited(); -} - -void DependencyFilePathPropertyComponent::enablementChanged() -{ - getValue().referTo (isEnabled() ? pathValue - : pathValueSource.appliesToThisOS() ? pathValueSource.getGlobalSettingsValue() - : pathValueSource.getFallbackSettingsValue()); - textWasEdited(); - repaint(); -} - -void DependencyFilePathPropertyComponent::textWasEdited() -{ - setColour (textColourId, getTextColourToDisplay()); - TextPropertyComponent::textWasEdited(); -} - -void DependencyFilePathPropertyComponent::valueChanged (Value& value) -{ - if ((value.refersToSameSourceAs (pathValue) && pathValueSource.isUsingGlobalSettings()) - || value.refersToSameSourceAs (pathValueSource.getGlobalSettingsValue())) - textWasEdited(); -} - -void DependencyFilePathPropertyComponent::setEditorText (Label* label) -{ - if (! pathValueSource.isUsingProjectSettings()) - if (auto editor = label->getCurrentTextEditor()) - editor->setText (String(), dontSendNotification); -} - -void DependencyFilePathPropertyComponent::browse() -{ - auto currentFile = pathRelativeTo.getChildFile (pathValue.toString()); - - if (isDirectory) - { - FileChooser chooser ("Select directory", currentFile); - - if (chooser.browseForDirectory()) - setTo (chooser.getResult()); - } - else - { - FileChooser chooser ("Select file", currentFile, wildcards); - - if (chooser.browseForFileToOpen()) - setTo (chooser.getResult()); - } -} - -Colour DependencyFilePathPropertyComponent::getTextColourToDisplay() const -{ - auto alpha = 1.0f; - auto key = pathValueSource.getKey(); - const auto& globalSettingsValue = pathValueSource.getGlobalSettingsValue(); - - if (! pathValueSource.isUsingProjectSettings() && isEnabled()) - alpha = 0.5f; - - if ((key == Ids::defaultUserModulePath && getValue().toString().contains (";")) || ! pathValueSource.appliesToThisOS()) - return findColour (widgetTextColourId).withMultipliedAlpha (alpha); - - auto usingGlobalPath = (getValue().refersToSameSourceAs (globalSettingsValue)); - - auto isValidPath = getAppSettings().isGlobalPathValid (pathRelativeTo, key, - (usingGlobalPath ? globalSettingsValue : pathValue).toString()); - - return isValidPath ? findColour (widgetTextColourId).withMultipliedAlpha (alpha) - : Colours::red.withMultipliedAlpha (alpha); -} diff --git a/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_DependencyPathPropertyComponent.h b/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_DependencyPathPropertyComponent.h deleted file mode 100644 index 9bcc340306..0000000000 --- a/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_DependencyPathPropertyComponent.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -#pragma once - - -//============================================================================== -/** This ValueSource type implements the fallback logic required for dependency - path settings: use the project exporter value; if this is empty, fall back to - the global preference value; if the exporter is supposed to run on another - OS and we don't know what the global preferences on that other machine are, - fall back to a generic OS-specific fallback value. -*/ -class DependencyPathValueSource : public Value::ValueSource, - private Value::Listener -{ -public: - DependencyPathValueSource (const Value& projectSettingsPath, - Identifier globalSettingsKey, - DependencyPathOS osThisSettingAppliesTo); - - /** This gets the currently used value, which may be either - the project setting, the global setting, or the fallback value. */ - var getValue() const override - { - if (isUsingProjectSettings()) - return projectSettingsValue.getValue(); - - if (isUsingGlobalSettings()) - return globalSettingsValue.getValue(); - - return fallbackValue.getValue(); - } - - void setValue (const var& newValue) override - { - projectSettingsValue = newValue; - - if (isUsingProjectSettings()) - sendChangeMessage (false); - } - - bool isUsingProjectSettings() const - { - return projectSettingsValueIsValid(); - } - - bool isUsingGlobalSettings() const - { - return ! projectSettingsValueIsValid() && globalSettingsValueIsValid(); - } - - bool isUsingFallbackValue() const - { - return ! projectSettingsValueIsValid() && ! globalSettingsValueIsValid(); - } - - bool appliesToThisOS() const - { - return os == TargetOS::getThisOS(); - } - - bool isValidPath (const File& relativeTo) const; - - bool isValidPath() const; - - Identifier getKey() { return globalKey; } - - Value getGlobalSettingsValue() { return globalSettingsValue; } - Value getFallbackSettingsValue() { return fallbackValue; } - -private: - void valueChanged (Value& value) override - { - if ((value.refersToSameSourceAs (globalSettingsValue) && isUsingGlobalSettings()) - || (value.refersToSameSourceAs (fallbackValue) && isUsingFallbackValue())) - { - sendChangeMessage (true); - setValue (String()); // make sure that the project-specific value is still blank - } - } - - /** This defines when to use the project setting, and when to - consider it invalid and to fall back to the global setting or - the fallback value. */ - bool projectSettingsValueIsValid() const - { - return ! projectSettingsValue.toString().isEmpty(); - } - - /** This defines when to use the global setting - given the project setting - is invalid, and when to fall back to the fallback value instead. */ - bool globalSettingsValueIsValid() const - { - // only use the global settings if they are set on the same OS - // that this setting is for! - DependencyPathOS thisOS = TargetOS::getThisOS(); - - return thisOS == TargetOS::unknown ? false : os == thisOS; - } - - /** the dependency path setting as set in this Projucer project. */ - Value projectSettingsValue; - - /** the global key used in the application settings for the global setting value. - needed for checking whether the path is valid. */ - Identifier globalKey; - - /** on what operating system should this dependency path be used? - note that this is *not* the os that is targeted by the project, - but rather the os on which the project will be compiled - (= on which the path settings need to be set correctly). */ - DependencyPathOS os; - - /** the dependency path global setting on this machine. - used when there value set for this project is invalid. */ - Value globalSettingsValue; - - /** the dependency path fallback setting. used instead of the global setting - whenever the latter doesn't apply, e.g. the setting is for another - OS than the ome this machine is running. */ - Value fallbackValue; -}; - - -//============================================================================== -class DependencyPathPropertyComponent : public TextPropertyComponent, - private Value::Listener -{ -public: - DependencyPathPropertyComponent (const File& pathRelativeToUse, - const Value& value, - const String& propertyName); - - -private: - /** This function defines what colour the label text should assume - depending on the current state of the value the component tracks. */ - Colour getTextColourToDisplay() const; - - /** This function handles path changes because of user input. */ - void textWasEdited() override; - - /** This function handles path changes because the global path changed. */ - void valueChanged (Value& value) override; - - /** If the dependency path is relative, relative to which directory should - we check if an object is available. */ - File pathRelativeTo; - - /** the value that represents this dependency path setting. */ - Value pathValue; - - /** a reference to the value source that this value refers to. */ - DependencyPathValueSource& pathValueSource; - - void setEditorText (Label* label); - - void lookAndFeelChanged() override; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DependencyPathPropertyComponent) -}; - -//============================================================================== -class DependencyFilePathPropertyComponent : public TextPropertyComponent, - public FileDragAndDropTarget, - private Value::Listener -{ -public: - DependencyFilePathPropertyComponent (Value& value, - const String& propertyDescription, - bool isDirectory, - const String& wildcards = "*", - const File& rootToUseForRelativePaths = File()); - - void resized() override; - void paintOverChildren (Graphics& g) override; - - bool isInterestedInFileDrag (const StringArray&) override { return isEnabled(); } - void fileDragEnter (const StringArray&, int, int) override { highlightForDragAndDrop = true; repaint(); } - void fileDragExit (const StringArray&) override { highlightForDragAndDrop = false; repaint(); } - void filesDropped (const StringArray&, int, int) override; - - void setTo (const File& f); - - void enablementChanged() override; - -private: - void textWasEdited() override; - - void valueChanged (Value&) override; - - void setEditorText (Label* label); - - void lookAndFeelChanged() override - { - browseButton.setColour (TextButton::buttonColourId, - findColour (secondaryButtonBackgroundColourId)); - textWasEdited(); - } - - void browse(); - Colour getTextColourToDisplay() const; - - //========================================================================== - File pathRelativeTo; - Value pathValue; - DependencyPathValueSource& pathValueSource; - - TextButton browseButton; - bool isDirectory, highlightForDragAndDrop = false; - String wildcards; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DependencyFilePathPropertyComponent) -}; diff --git a/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h b/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h index e78db6a88f..a50006cf39 100644 --- a/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h +++ b/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h @@ -28,193 +28,178 @@ //============================================================================== -class FilePathPropertyComponent : public PropertyComponent +/** A PropertyComponent for selecting files or folders. + + The user may drag files over the property box, enter the path manually and/or click + the '...' button to open a file selection dialog box. +*/ +class FilePathPropertyComponent : public PropertyComponent, + public FileDragAndDropTarget, + private Value::Listener { public: - /** A Property Component for selecting files or folders. - - The user may drag files over the property box, enter the path - manually and/or click the '...' button to open a file selection - dialog box - */ - FilePathPropertyComponent (Value valueToControl, - const String& propertyDescription, - bool isDirectory, - const String& wildcards = "*", - const File& rootToUseForRelativePaths = File(), - const bool supportsMultiplePaths = false) - : PropertyComponent (propertyDescription), - innerComp (valueToControl, isDirectory, wildcards, rootToUseForRelativePaths, supportsMultiplePaths) + FilePathPropertyComponent (Value valueToControl, const String& propertyName, bool isDir, bool thisOS = true, + const String& wildcardsToUse = "*", const File& relativeRoot = File(), bool multiPath = false) + : PropertyComponent (propertyName), + text (valueToControl, propertyName, 1024, false), + isDirectory (isDir), isThisOS (thisOS), supportsMultiplePaths (multiPath), wildcards (wildcardsToUse), root (relativeRoot) { - addAndMakeVisible (innerComp); + textValue.referTo (valueToControl); + + init(); } - void refresh() override {} // N/A + /** Displays a default value when no value is specified by the user. */ + FilePathPropertyComponent (ValueWithDefault& valueToControl, const String& propertyName, bool isDir, bool thisOS = true, + const String& wildcardsToUse = "*", const File& relativeRoot = File(), bool multiPath = false) + : PropertyComponent (propertyName), + text (valueToControl, propertyName, 1024, false), + isDirectory (isDir), isThisOS (thisOS), supportsMultiplePaths (multiPath), wildcards (wildcardsToUse), root (relativeRoot) + { + textValue = valueToControl.getPropertyAsValue(); + + init(); + } + + //============================================================================== + void refresh() override {} + + void resized() override + { + auto bounds = getLocalBounds(); + + browseButton.setBounds (bounds.removeFromRight (50).reduced (5, 0)); + text.setBounds (bounds); + } + + void paintOverChildren (Graphics& g) override + { + if (highlightForDragAndDrop) + { + g.setColour (findColour (defaultHighlightColourId).withAlpha (0.5f)); + g.fillRect (text.getBounds().withTrimmedRight (50)); + } + } + + //============================================================================== + bool isInterestedInFileDrag (const StringArray&) override { return true; } + void fileDragEnter (const StringArray&, int, int) override { highlightForDragAndDrop = true; repaint(); } + void fileDragExit (const StringArray&) override { highlightForDragAndDrop = false; repaint(); } + + void filesDropped (const StringArray& selectedFiles, int, int) override + { + if (supportsMultiplePaths) + { + for (auto& f : selectedFiles) + setTo (f); + } + else + { + setTo (selectedFiles[0]); + } + + highlightForDragAndDrop = false; + repaint(); + } private: - struct InnerComponent : public Component, - public FileDragAndDropTarget + //============================================================================== + void init() { - InnerComponent (Value v, bool isDir, const String& wc, const File& rt, const bool multiplePaths) - : value (v), - isDirectory (isDir), - highlightForDragAndDrop (false), - wildcards (wc), - root (rt), - button ("..."), - supportsMultiplePaths (multiplePaths) + textValue.addListener (this); + + text.setInterestedInFileDrag (false); + addAndMakeVisible (text); + + browseButton.onClick = [this] { browse(); }; + addAndMakeVisible (browseButton); + + lookAndFeelChanged(); + } + + void setTo (File f) + { + if (isDirectory && ! f.isDirectory()) + f = f.getParentDirectory(); + + auto pathName = (root == File()) ? f.getFullPathName() + : f.getRelativePathFrom (root); + + auto currentPath = text.getText(); + + if (supportsMultiplePaths && currentPath.isNotEmpty()) + pathName = currentPath.trimCharactersAtEnd (" ;") + "; " + pathName; + + text.setText (pathName); + updateEditorColour(); + } + + void browse() + { + auto currentFile = root.getChildFile (text.getText()); + + if (isDirectory) { - addAndMakeVisible (textbox); - textbox.getTextValue().referTo (value); - textbox.onReturnKey = [this] { updateEditorColour (textbox); }; - textbox.onFocusLost = [this] { updateEditorColour (textbox); }; + FileChooser chooser ("Select directory", currentFile); - addAndMakeVisible (button); - button.onClick = [this] { browse(); }; - - lookAndFeelChanged(); + if (chooser.browseForDirectory()) + setTo (chooser.getResult()); } - - void paintOverChildren (Graphics& g) override + else { - if (highlightForDragAndDrop) - { - g.setColour (findColour (defaultHighlightColourId).withAlpha (0.5f)); - g.fillRect (textbox.getBounds()); - } - } + FileChooser chooser ("Select file", currentFile, wildcards); - void resized() override + if (chooser.browseForFileToOpen()) + setTo (chooser.getResult()); + } + } + + void updateEditorColour() + { + if (supportsMultiplePaths || ! isThisOS) + return; + + text.setColour (TextPropertyComponent::textColourId, findColour (widgetTextColourId)); + + auto pathToCheck = text.getText(); + + if (pathToCheck.isNotEmpty()) { - juce::Rectangle r (getLocalBounds()); + pathToCheck.replace ("${user.home}", "~"); - button.setBounds (r.removeFromRight (30)); - textbox.setBounds (r); + #if JUCE_WINDOWS + if (pathToCheck.startsWith ("~")) + pathToCheck = pathToCheck.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); + #endif + + if (! root.getChildFile (pathToCheck).exists()) + text.setColour (TextPropertyComponent::textColourId, Colours::red); } + } - bool isInterestedInFileDrag (const StringArray&) override { return true; } - void fileDragEnter (const StringArray&, int, int) override { highlightForDragAndDrop = true; repaint(); } - void fileDragExit (const StringArray&) override { highlightForDragAndDrop = false; repaint(); } + void valueChanged (Value&) override + { + updateEditorColour(); + } - void filesDropped (const StringArray& files, int, int) override - { - const File firstFile (files[0]); + void lookAndFeelChanged() override + { + browseButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId)); + browseButton.setColour (TextButton::textColourOffId, Colours::white); - if (isDirectory) - setTo (firstFile.isDirectory() ? firstFile - : firstFile.getParentDirectory()); - else - setTo (firstFile); + updateEditorColour(); + } - highlightForDragAndDrop = false; - repaint(); - } + //============================================================================== + Value textValue; - void browse() - { - auto currentFile = root.getChildFile (value.toString()); + TextPropertyComponent text; + TextButton browseButton { "..." }; - if (isDirectory) - { - FileChooser chooser ("Select directory", currentFile); - - if (chooser.browseForDirectory()) - setTo (chooser.getResult()); - } - else - { - FileChooser chooser ("Select file", currentFile, wildcards); - - if (chooser.browseForFileToOpen()) - setTo (chooser.getResult()); - } - } - - void updateEditorColour (TextEditor& editor) - { - if (supportsMultiplePaths) - { - auto paths = StringArray::fromTokens (editor.getTextValue().toString(), ";", {}); - - editor.clear(); - - AttributedString str; - for (auto p : paths) - { - if (root.getChildFile (p.trim()).exists()) editor.setColour (TextEditor::textColourId, findColour (widgetTextColourId)); - else editor.setColour (TextEditor::textColourId, Colours::red); - - editor.insertTextAtCaret (p); - - if (paths.indexOf (p) < paths.size() - 1) - { - editor.setColour (TextEditor::textColourId, findColour (widgetTextColourId)); - editor.insertTextAtCaret (";"); - } - } - - editor.setColour (TextEditor::textColourId, findColour (widgetTextColourId)); - } - else - { - auto pathToCheck = editor.getTextValue().toString(); - - if (pathToCheck.isEmpty()) - return; - - //android SDK/NDK paths - if (pathToCheck.contains ("${user.home}")) - pathToCheck = pathToCheck.replace ("${user.home}", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); - - #if JUCE_WINDOWS - if (pathToCheck.startsWith ("~")) - pathToCheck = pathToCheck.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); - #endif - - const auto currentFile = root.getChildFile (pathToCheck); - - if (currentFile.exists()) - editor.applyColourToAllText (findColour (widgetTextColourId)); - else - editor.applyColourToAllText (Colours::red); - } - } - - void setTo (const File& f) - { - auto pathName = (root == File()) ? f.getFullPathName() - : f.getRelativePathFrom (root); - - if (supportsMultiplePaths && value.toString().isNotEmpty()) - value = value.toString().trimCharactersAtEnd (" ;") + "; " + pathName; - else - value = pathName; - - updateEditorColour (textbox); - } - - void lookAndFeelChanged() override - { - textbox.setColour (TextEditor::backgroundColourId, findColour (widgetBackgroundColourId)); - textbox.setColour (TextEditor::outlineColourId, Colours::transparentBlack); - updateEditorColour (textbox); - - button.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId)); - button.setColour (TextButton::textColourOffId, Colours::white); - } - - Value value; - bool isDirectory, highlightForDragAndDrop; - String wildcards; - File root; - TextEditor textbox; - TextButton button; - bool supportsMultiplePaths; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InnerComponent) - }; - - InnerComponent innerComp; // Used so that the PropertyComponent auto first-child positioning works + bool isDirectory, isThisOS, supportsMultiplePaths, highlightForDragAndDrop = false; + String wildcards; + File root; + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePathPropertyComponent) };