Audio plugin host https://kx.studio/carla
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.

262 lines
7.5KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. FilenameComponent::FilenameComponent (const String& name,
  16. const File& currentFile,
  17. bool canEditFilename,
  18. bool isDirectory,
  19. bool isForSaving,
  20. const String& fileBrowserWildcard,
  21. const String& suffix,
  22. const String& textWhenNothingSelected)
  23. : Component (name),
  24. isDir (isDirectory),
  25. isSaving (isForSaving),
  26. wildcard (fileBrowserWildcard),
  27. enforcedSuffix (suffix)
  28. {
  29. addAndMakeVisible (filenameBox);
  30. filenameBox.setEditableText (canEditFilename);
  31. filenameBox.setTextWhenNothingSelected (textWhenNothingSelected);
  32. filenameBox.setTextWhenNoChoicesAvailable (TRANS ("(no recently selected files)"));
  33. filenameBox.onChange = [this] { setCurrentFile (getCurrentFile(), true); };
  34. setBrowseButtonText ("...");
  35. setCurrentFile (currentFile, true, dontSendNotification);
  36. }
  37. FilenameComponent::~FilenameComponent()
  38. {
  39. }
  40. //==============================================================================
  41. void FilenameComponent::paintOverChildren (Graphics& g)
  42. {
  43. if (isFileDragOver)
  44. {
  45. g.setColour (Colours::red.withAlpha (0.2f));
  46. g.drawRect (getLocalBounds(), 3);
  47. }
  48. }
  49. void FilenameComponent::resized()
  50. {
  51. getLookAndFeel().layoutFilenameComponent (*this, &filenameBox, browseButton.get());
  52. }
  53. KeyboardFocusTraverser* FilenameComponent::createFocusTraverser()
  54. {
  55. // This prevents the sub-components from grabbing focus if the
  56. // FilenameComponent has been set to refuse focus.
  57. return getWantsKeyboardFocus() ? Component::createFocusTraverser() : nullptr;
  58. }
  59. void FilenameComponent::setBrowseButtonText (const String& newBrowseButtonText)
  60. {
  61. browseButtonText = newBrowseButtonText;
  62. lookAndFeelChanged();
  63. }
  64. void FilenameComponent::lookAndFeelChanged()
  65. {
  66. browseButton.reset();
  67. browseButton.reset (getLookAndFeel().createFilenameComponentBrowseButton (browseButtonText));
  68. addAndMakeVisible (browseButton.get());
  69. browseButton->setConnectedEdges (Button::ConnectedOnLeft);
  70. browseButton->onClick = [this] { showChooser(); };
  71. resized();
  72. }
  73. void FilenameComponent::setTooltip (const String& newTooltip)
  74. {
  75. SettableTooltipClient::setTooltip (newTooltip);
  76. filenameBox.setTooltip (newTooltip);
  77. }
  78. void FilenameComponent::setDefaultBrowseTarget (const File& newDefaultDirectory)
  79. {
  80. defaultBrowseFile = newDefaultDirectory;
  81. }
  82. File FilenameComponent::getLocationToBrowse()
  83. {
  84. if (lastFilename.isEmpty() && defaultBrowseFile != File())
  85. return defaultBrowseFile;
  86. return getCurrentFile();
  87. }
  88. void FilenameComponent::showChooser()
  89. {
  90. #if JUCE_MODAL_LOOPS_PERMITTED
  91. FileChooser fc (isDir ? TRANS ("Choose a new directory")
  92. : TRANS ("Choose a new file"),
  93. getLocationToBrowse(),
  94. wildcard);
  95. if (isDir ? fc.browseForDirectory()
  96. : (isSaving ? fc.browseForFileToSave (false)
  97. : fc.browseForFileToOpen()))
  98. {
  99. setCurrentFile (fc.getResult(), true);
  100. }
  101. #else
  102. ignoreUnused (isSaving);
  103. jassertfalse; // needs rewriting to deal with non-modal environments
  104. #endif
  105. }
  106. bool FilenameComponent::isInterestedInFileDrag (const StringArray&)
  107. {
  108. return true;
  109. }
  110. void FilenameComponent::filesDropped (const StringArray& filenames, int, int)
  111. {
  112. isFileDragOver = false;
  113. repaint();
  114. const File f (filenames[0]);
  115. if (f.exists() && (f.isDirectory() == isDir))
  116. setCurrentFile (f, true);
  117. }
  118. void FilenameComponent::fileDragEnter (const StringArray&, int, int)
  119. {
  120. isFileDragOver = true;
  121. repaint();
  122. }
  123. void FilenameComponent::fileDragExit (const StringArray&)
  124. {
  125. isFileDragOver = false;
  126. repaint();
  127. }
  128. //==============================================================================
  129. String FilenameComponent::getCurrentFileText() const
  130. {
  131. return filenameBox.getText();
  132. }
  133. File FilenameComponent::getCurrentFile() const
  134. {
  135. auto f = File::getCurrentWorkingDirectory().getChildFile (getCurrentFileText());
  136. if (enforcedSuffix.isNotEmpty())
  137. f = f.withFileExtension (enforcedSuffix);
  138. return f;
  139. }
  140. void FilenameComponent::setCurrentFile (File newFile,
  141. const bool addToRecentlyUsedList,
  142. NotificationType notification)
  143. {
  144. if (enforcedSuffix.isNotEmpty())
  145. newFile = newFile.withFileExtension (enforcedSuffix);
  146. if (newFile.getFullPathName() != lastFilename)
  147. {
  148. lastFilename = newFile.getFullPathName();
  149. if (addToRecentlyUsedList)
  150. addRecentlyUsedFile (newFile);
  151. filenameBox.setText (lastFilename, dontSendNotification);
  152. if (notification != dontSendNotification)
  153. {
  154. triggerAsyncUpdate();
  155. if (notification == sendNotificationSync)
  156. handleUpdateNowIfNeeded();
  157. }
  158. }
  159. }
  160. void FilenameComponent::setFilenameIsEditable (const bool shouldBeEditable)
  161. {
  162. filenameBox.setEditableText (shouldBeEditable);
  163. }
  164. StringArray FilenameComponent::getRecentlyUsedFilenames() const
  165. {
  166. StringArray names;
  167. for (int i = 0; i < filenameBox.getNumItems(); ++i)
  168. names.add (filenameBox.getItemText (i));
  169. return names;
  170. }
  171. void FilenameComponent::setRecentlyUsedFilenames (const StringArray& filenames)
  172. {
  173. if (filenames != getRecentlyUsedFilenames())
  174. {
  175. filenameBox.clear();
  176. for (int i = 0; i < jmin (filenames.size(), maxRecentFiles); ++i)
  177. filenameBox.addItem (filenames[i], i + 1);
  178. }
  179. }
  180. void FilenameComponent::setMaxNumberOfRecentFiles (const int newMaximum)
  181. {
  182. maxRecentFiles = jmax (1, newMaximum);
  183. setRecentlyUsedFilenames (getRecentlyUsedFilenames());
  184. }
  185. void FilenameComponent::addRecentlyUsedFile (const File& file)
  186. {
  187. auto files = getRecentlyUsedFilenames();
  188. if (file.getFullPathName().isNotEmpty())
  189. {
  190. files.removeString (file.getFullPathName(), true);
  191. files.insert (0, file.getFullPathName());
  192. setRecentlyUsedFilenames (files);
  193. }
  194. }
  195. //==============================================================================
  196. void FilenameComponent::addListener (FilenameComponentListener* const listener)
  197. {
  198. listeners.add (listener);
  199. }
  200. void FilenameComponent::removeListener (FilenameComponentListener* const listener)
  201. {
  202. listeners.remove (listener);
  203. }
  204. void FilenameComponent::handleAsyncUpdate()
  205. {
  206. Component::BailOutChecker checker (this);
  207. listeners.callChecked (checker, [this] (FilenameComponentListener& l) { l.filenameComponentChanged (this); });
  208. }
  209. } // namespace juce