The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

210 lines
7.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #pragma once
  20. //==============================================================================
  21. /** A PropertyComponent for selecting files or folders.
  22. The user may drag files over the property box, enter the path manually and/or click
  23. the '...' button to open a file selection dialog box.
  24. */
  25. class FilePathPropertyComponent : public PropertyComponent,
  26. public FileDragAndDropTarget,
  27. private Value::Listener
  28. {
  29. public:
  30. FilePathPropertyComponent (Value valueToControl, const String& propertyName, bool isDir, bool thisOS = true,
  31. const String& wildcardsToUse = "*", const File& relativeRoot = File(), bool multiPath = false)
  32. : PropertyComponent (propertyName),
  33. text (valueToControl, propertyName, 1024, false),
  34. isDirectory (isDir), isThisOS (thisOS), supportsMultiplePaths (multiPath), wildcards (wildcardsToUse), root (relativeRoot)
  35. {
  36. textValue.referTo (valueToControl);
  37. init();
  38. }
  39. /** Displays a default value when no value is specified by the user. */
  40. FilePathPropertyComponent (ValueWithDefault& valueToControl, const String& propertyName, bool isDir, bool thisOS = true,
  41. const String& wildcardsToUse = "*", const File& relativeRoot = File(), bool multiPath = false)
  42. : PropertyComponent (propertyName),
  43. text (valueToControl, propertyName, 1024, false),
  44. isDirectory (isDir), isThisOS (thisOS), supportsMultiplePaths (multiPath), wildcards (wildcardsToUse), root (relativeRoot)
  45. {
  46. textValue = valueToControl.getPropertyAsValue();
  47. init();
  48. }
  49. //==============================================================================
  50. void refresh() override {}
  51. void resized() override
  52. {
  53. auto bounds = getLocalBounds();
  54. text.setBounds (bounds.removeFromLeft (jmax (400, bounds.getWidth() - 55)));
  55. bounds.removeFromLeft (5);
  56. browseButton.setBounds (bounds);
  57. }
  58. void paintOverChildren (Graphics& g) override
  59. {
  60. if (highlightForDragAndDrop)
  61. {
  62. g.setColour (findColour (defaultHighlightColourId).withAlpha (0.5f));
  63. g.fillRect (getLookAndFeel().getPropertyComponentContentPosition (text));
  64. }
  65. }
  66. //==============================================================================
  67. bool isInterestedInFileDrag (const StringArray&) override { return true; }
  68. void fileDragEnter (const StringArray&, int, int) override { highlightForDragAndDrop = true; repaint(); }
  69. void fileDragExit (const StringArray&) override { highlightForDragAndDrop = false; repaint(); }
  70. void filesDropped (const StringArray& selectedFiles, int, int) override
  71. {
  72. if (supportsMultiplePaths)
  73. {
  74. for (auto& f : selectedFiles)
  75. setTo (f);
  76. }
  77. else
  78. {
  79. setTo (selectedFiles[0]);
  80. }
  81. highlightForDragAndDrop = false;
  82. repaint();
  83. }
  84. private:
  85. //==============================================================================
  86. void init()
  87. {
  88. textValue.addListener (this);
  89. text.setInterestedInFileDrag (false);
  90. addAndMakeVisible (text);
  91. browseButton.onClick = [this] { browse(); };
  92. addAndMakeVisible (browseButton);
  93. lookAndFeelChanged();
  94. }
  95. void setTo (File f)
  96. {
  97. if (isDirectory && ! f.isDirectory())
  98. f = f.getParentDirectory();
  99. auto pathName = (root == File()) ? f.getFullPathName()
  100. : f.getRelativePathFrom (root);
  101. auto currentPath = text.getText();
  102. if (supportsMultiplePaths && currentPath.isNotEmpty())
  103. pathName = currentPath.trimCharactersAtEnd (" ;") + "; " + pathName;
  104. text.setText (pathName);
  105. updateEditorColour();
  106. }
  107. void browse()
  108. {
  109. File currentFile = {};
  110. if (text.getText().isNotEmpty())
  111. currentFile = root.getChildFile (text.getText());
  112. if (isDirectory)
  113. {
  114. FileChooser chooser ("Select directory", currentFile);
  115. if (chooser.browseForDirectory())
  116. setTo (chooser.getResult());
  117. }
  118. else
  119. {
  120. FileChooser chooser ("Select file", currentFile, wildcards);
  121. if (chooser.browseForFileToOpen())
  122. setTo (chooser.getResult());
  123. }
  124. }
  125. void updateEditorColour()
  126. {
  127. if (supportsMultiplePaths || ! isThisOS)
  128. return;
  129. text.setColour (TextPropertyComponent::textColourId, findColour (widgetTextColourId));
  130. auto pathToCheck = text.getText();
  131. if (pathToCheck.isNotEmpty())
  132. {
  133. pathToCheck.replace ("${user.home}", "~");
  134. #if JUCE_WINDOWS
  135. if (pathToCheck.startsWith ("~"))
  136. pathToCheck = pathToCheck.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
  137. #endif
  138. if (! root.getChildFile (pathToCheck).exists())
  139. text.setColour (TextPropertyComponent::textColourId, Colours::red);
  140. }
  141. }
  142. void valueChanged (Value&) override
  143. {
  144. updateEditorColour();
  145. }
  146. void lookAndFeelChanged() override
  147. {
  148. browseButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
  149. browseButton.setColour (TextButton::textColourOffId, Colours::white);
  150. updateEditorColour();
  151. }
  152. //==============================================================================
  153. Value textValue;
  154. TextPropertyComponent text;
  155. TextButton browseButton { "..." };
  156. bool isDirectory, isThisOS, supportsMultiplePaths, highlightForDragAndDrop = false;
  157. String wildcards;
  158. File root;
  159. //==============================================================================
  160. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePathPropertyComponent)
  161. };