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.

206 lines
6.9KB

  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. browseButton.setBounds (bounds.removeFromRight (50).reduced (5, 0));
  55. text.setBounds (bounds);
  56. }
  57. void paintOverChildren (Graphics& g) override
  58. {
  59. if (highlightForDragAndDrop)
  60. {
  61. g.setColour (findColour (defaultHighlightColourId).withAlpha (0.5f));
  62. g.fillRect (text.getBounds().withTrimmedRight (50));
  63. }
  64. }
  65. //==============================================================================
  66. bool isInterestedInFileDrag (const StringArray&) override { return true; }
  67. void fileDragEnter (const StringArray&, int, int) override { highlightForDragAndDrop = true; repaint(); }
  68. void fileDragExit (const StringArray&) override { highlightForDragAndDrop = false; repaint(); }
  69. void filesDropped (const StringArray& selectedFiles, int, int) override
  70. {
  71. if (supportsMultiplePaths)
  72. {
  73. for (auto& f : selectedFiles)
  74. setTo (f);
  75. }
  76. else
  77. {
  78. setTo (selectedFiles[0]);
  79. }
  80. highlightForDragAndDrop = false;
  81. repaint();
  82. }
  83. private:
  84. //==============================================================================
  85. void init()
  86. {
  87. textValue.addListener (this);
  88. text.setInterestedInFileDrag (false);
  89. addAndMakeVisible (text);
  90. browseButton.onClick = [this] { browse(); };
  91. addAndMakeVisible (browseButton);
  92. lookAndFeelChanged();
  93. }
  94. void setTo (File f)
  95. {
  96. if (isDirectory && ! f.isDirectory())
  97. f = f.getParentDirectory();
  98. auto pathName = (root == File()) ? f.getFullPathName()
  99. : f.getRelativePathFrom (root);
  100. auto currentPath = text.getText();
  101. if (supportsMultiplePaths && currentPath.isNotEmpty())
  102. pathName = currentPath.trimCharactersAtEnd (" ;") + "; " + pathName;
  103. text.setText (pathName);
  104. updateEditorColour();
  105. }
  106. void browse()
  107. {
  108. auto currentFile = root.getChildFile (text.getText());
  109. if (isDirectory)
  110. {
  111. FileChooser chooser ("Select directory", currentFile);
  112. if (chooser.browseForDirectory())
  113. setTo (chooser.getResult());
  114. }
  115. else
  116. {
  117. FileChooser chooser ("Select file", currentFile, wildcards);
  118. if (chooser.browseForFileToOpen())
  119. setTo (chooser.getResult());
  120. }
  121. }
  122. void updateEditorColour()
  123. {
  124. if (supportsMultiplePaths || ! isThisOS)
  125. return;
  126. text.setColour (TextPropertyComponent::textColourId, findColour (widgetTextColourId));
  127. auto pathToCheck = text.getText();
  128. if (pathToCheck.isNotEmpty())
  129. {
  130. pathToCheck.replace ("${user.home}", "~");
  131. #if JUCE_WINDOWS
  132. if (pathToCheck.startsWith ("~"))
  133. pathToCheck = pathToCheck.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
  134. #endif
  135. if (! root.getChildFile (pathToCheck).exists())
  136. text.setColour (TextPropertyComponent::textColourId, Colours::red);
  137. }
  138. }
  139. void valueChanged (Value&) override
  140. {
  141. updateEditorColour();
  142. }
  143. void lookAndFeelChanged() override
  144. {
  145. browseButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
  146. browseButton.setColour (TextButton::textColourOffId, Colours::white);
  147. updateEditorColour();
  148. }
  149. //==============================================================================
  150. Value textValue;
  151. TextPropertyComponent text;
  152. TextButton browseButton { "..." };
  153. bool isDirectory, isThisOS, supportsMultiplePaths, highlightForDragAndDrop = false;
  154. String wildcards;
  155. File root;
  156. //==============================================================================
  157. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePathPropertyComponent)
  158. };