/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited 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 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-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. ============================================================================== */ namespace juce { FilenameComponent::FilenameComponent (const String& name, const File& currentFile, bool canEditFilename, bool isDirectory, bool isForSaving, const String& fileBrowserWildcard, const String& suffix, const String& textWhenNothingSelected) : Component (name), isDir (isDirectory), isSaving (isForSaving), wildcard (fileBrowserWildcard), enforcedSuffix (suffix) { addAndMakeVisible (filenameBox); filenameBox.setEditableText (canEditFilename); filenameBox.setTextWhenNothingSelected (textWhenNothingSelected); filenameBox.setTextWhenNoChoicesAvailable (TRANS ("(no recently selected files)")); filenameBox.onChange = [this] { setCurrentFile (getCurrentFile(), true); }; setBrowseButtonText ("..."); setCurrentFile (currentFile, true, dontSendNotification); } FilenameComponent::~FilenameComponent() { } //============================================================================== void FilenameComponent::paintOverChildren (Graphics& g) { if (isFileDragOver) { g.setColour (Colours::red.withAlpha (0.2f)); g.drawRect (getLocalBounds(), 3); } } void FilenameComponent::resized() { getLookAndFeel().layoutFilenameComponent (*this, &filenameBox, browseButton.get()); } std::unique_ptr FilenameComponent::createKeyboardFocusTraverser() { // This prevents the sub-components from grabbing focus if the // FilenameComponent has been set to refuse focus. return getWantsKeyboardFocus() ? Component::createKeyboardFocusTraverser() : nullptr; } void FilenameComponent::setBrowseButtonText (const String& newBrowseButtonText) { browseButtonText = newBrowseButtonText; lookAndFeelChanged(); } void FilenameComponent::lookAndFeelChanged() { browseButton.reset(); browseButton.reset (getLookAndFeel().createFilenameComponentBrowseButton (browseButtonText)); addAndMakeVisible (browseButton.get()); browseButton->setConnectedEdges (Button::ConnectedOnLeft); browseButton->onClick = [this] { showChooser(); }; resized(); } void FilenameComponent::setTooltip (const String& newTooltip) { SettableTooltipClient::setTooltip (newTooltip); filenameBox.setTooltip (newTooltip); } void FilenameComponent::setDefaultBrowseTarget (const File& newDefaultDirectory) { defaultBrowseFile = newDefaultDirectory; } File FilenameComponent::getLocationToBrowse() { if (lastFilename.isEmpty() && defaultBrowseFile != File()) return defaultBrowseFile; return getCurrentFile(); } void FilenameComponent::showChooser() { chooser = std::make_unique (isDir ? TRANS ("Choose a new directory") : TRANS ("Choose a new file"), getLocationToBrowse(), wildcard); auto chooserFlags = isDir ? FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories : FileBrowserComponent::canSelectFiles | (isSaving ? FileBrowserComponent::saveMode : FileBrowserComponent::openMode); chooser->launchAsync (chooserFlags, [this] (const FileChooser&) { if (chooser->getResult() == File{}) return; setCurrentFile (chooser->getResult(), true); }); } bool FilenameComponent::isInterestedInFileDrag (const StringArray&) { return true; } void FilenameComponent::filesDropped (const StringArray& filenames, int, int) { isFileDragOver = false; repaint(); const File f (filenames[0]); if (f.exists() && (f.isDirectory() == isDir)) setCurrentFile (f, true); } void FilenameComponent::fileDragEnter (const StringArray&, int, int) { isFileDragOver = true; repaint(); } void FilenameComponent::fileDragExit (const StringArray&) { isFileDragOver = false; repaint(); } //============================================================================== String FilenameComponent::getCurrentFileText() const { return filenameBox.getText(); } File FilenameComponent::getCurrentFile() const { auto f = File::getCurrentWorkingDirectory().getChildFile (getCurrentFileText()); if (enforcedSuffix.isNotEmpty()) f = f.withFileExtension (enforcedSuffix); return f; } void FilenameComponent::setCurrentFile (File newFile, const bool addToRecentlyUsedList, NotificationType notification) { if (enforcedSuffix.isNotEmpty()) newFile = newFile.withFileExtension (enforcedSuffix); if (newFile.getFullPathName() != lastFilename) { lastFilename = newFile.getFullPathName(); if (addToRecentlyUsedList) addRecentlyUsedFile (newFile); filenameBox.setText (lastFilename, dontSendNotification); if (notification != dontSendNotification) { triggerAsyncUpdate(); if (notification == sendNotificationSync) handleUpdateNowIfNeeded(); } } } void FilenameComponent::setFilenameIsEditable (const bool shouldBeEditable) { filenameBox.setEditableText (shouldBeEditable); } StringArray FilenameComponent::getRecentlyUsedFilenames() const { StringArray names; for (int i = 0; i < filenameBox.getNumItems(); ++i) names.add (filenameBox.getItemText (i)); return names; } void FilenameComponent::setRecentlyUsedFilenames (const StringArray& filenames) { if (filenames != getRecentlyUsedFilenames()) { filenameBox.clear(); for (int i = 0; i < jmin (filenames.size(), maxRecentFiles); ++i) filenameBox.addItem (filenames[i], i + 1); } } void FilenameComponent::setMaxNumberOfRecentFiles (const int newMaximum) { maxRecentFiles = jmax (1, newMaximum); setRecentlyUsedFilenames (getRecentlyUsedFilenames()); } void FilenameComponent::addRecentlyUsedFile (const File& file) { auto files = getRecentlyUsedFilenames(); if (file.getFullPathName().isNotEmpty()) { files.removeString (file.getFullPathName(), true); files.insert (0, file.getFullPathName()); setRecentlyUsedFilenames (files); } } //============================================================================== void FilenameComponent::addListener (FilenameComponentListener* const listener) { listeners.add (listener); } void FilenameComponent::removeListener (FilenameComponentListener* const listener) { listeners.remove (listener); } void FilenameComponent::handleAsyncUpdate() { Component::BailOutChecker checker (this); listeners.callChecked (checker, [this] (FilenameComponentListener& l) { l.filenameComponentChanged (this); }); } } // namespace juce