From 5ce2fc388e25cdf85b9fec5360800a513d49ae1f Mon Sep 17 00:00:00 2001 From: attila Date: Tue, 9 Jul 2024 18:58:42 +0200 Subject: [PATCH] Fix C++23 compilation --- .../Source/Application/jucer_AutoUpdater.cpp | 304 +++++------ .../CodeEditor/jucer_SourceCodeEditor.cpp | 166 +++--- .../juce_audio_devices/juce_audio_devices.cpp | 4 +- ...ce_MidiDeviceListConnectionBroadcaster.cpp | 115 +++++ .../midi_io/juce_MidiDevices.cpp | 68 --- .../scanning/juce_PluginListComponent.cpp | 483 +++++++++--------- modules/juce_core/juce_core.cpp | 17 +- .../juce_core/native/juce_SharedCode_posix.h | 2 +- modules/juce_core/system/juce_SystemStats.cpp | 2 - .../messages/juce_ApplicationBase.cpp | 24 +- modules/juce_graphics/juce_graphics.cpp | 1 + .../components/juce_Component.cpp | 88 ++-- modules/juce_gui_basics/juce_gui_basics.cpp | 2 +- modules/juce_gui_basics/juce_gui_basics.h | 20 +- .../juce_gui_basics/widgets/juce_TreeView.cpp | 266 +++++----- modules/juce_gui_extra/juce_gui_extra.cpp | 15 +- 16 files changed, 817 insertions(+), 760 deletions(-) create mode 100644 modules/juce_audio_devices/midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp diff --git a/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp b/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp index 73cd04f131..671d88e8d1 100644 --- a/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp +++ b/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp @@ -36,6 +36,158 @@ #include "jucer_Application.h" #include "jucer_AutoUpdater.h" +class DownloadAndInstallThread final : private ThreadWithProgressWindow +{ +public: + DownloadAndInstallThread (const VersionInfo::Asset& a, const File& t, std::function&& cb) + : ThreadWithProgressWindow ("Downloading New Version", true, true), + asset (a), targetFolder (t), completionCallback (std::move (cb)) + { + launchThread (Priority::low); + } + +private: + void run() override + { + setProgress (-1.0); + + MemoryBlock zipData; + auto result = download (zipData); + + if (result.wasOk() && ! threadShouldExit()) + result = install (zipData); + + MessageManager::callAsync ([result, callback = completionCallback] + { + callback (result); + }); + } + + Result download (MemoryBlock& dest) + { + setStatusMessage ("Downloading..."); + + int statusCode = 0; + auto inStream = VersionInfo::createInputStreamForAsset (asset, statusCode); + + if (inStream != nullptr && statusCode == 200) + { + int64 total = 0; + MemoryOutputStream mo (dest, true); + + for (;;) + { + if (threadShouldExit()) + return Result::fail ("Cancelled"); + + auto written = mo.writeFromInputStream (*inStream, 8192); + + if (written == 0) + break; + + total += written; + + setStatusMessage ("Downloading... " + File::descriptionOfSizeInBytes (total)); + } + + return Result::ok(); + } + + return Result::fail ("Failed to download from: " + asset.url); + } + + Result install (const MemoryBlock& data) + { + setStatusMessage ("Installing..."); + + MemoryInputStream input (data, false); + ZipFile zip (input); + + if (zip.getNumEntries() == 0) + return Result::fail ("The downloaded file was not a valid JUCE file!"); + + struct ScopedDownloadFolder + { + explicit ScopedDownloadFolder (const File& installTargetFolder) + { + folder = installTargetFolder.getSiblingFile (installTargetFolder.getFileNameWithoutExtension() + "_download").getNonexistentSibling(); + jassert (folder.createDirectory()); + } + + ~ScopedDownloadFolder() { folder.deleteRecursively(); } + + File folder; + }; + + ScopedDownloadFolder unzipTarget (targetFolder); + + if (! unzipTarget.folder.isDirectory()) + return Result::fail ("Couldn't create a temporary folder to unzip the new version!"); + + auto r = zip.uncompressTo (unzipTarget.folder); + + if (r.failed()) + return r; + + if (threadShouldExit()) + return Result::fail ("Cancelled"); + + #if JUCE_LINUX || JUCE_BSD || JUCE_MAC + r = setFilePermissions (unzipTarget.folder, zip); + + if (r.failed()) + return r; + + if (threadShouldExit()) + return Result::fail ("Cancelled"); + #endif + + if (targetFolder.exists()) + { + auto oldFolder = targetFolder.getSiblingFile (targetFolder.getFileNameWithoutExtension() + "_old").getNonexistentSibling(); + + if (! targetFolder.moveFileTo (oldFolder)) + return Result::fail ("Could not remove the existing folder!\n\n" + "This may happen if you are trying to download into a directory that requires administrator privileges to modify.\n" + "Please select a folder that is writable by the current user."); + } + + if (! unzipTarget.folder.getChildFile ("JUCE").moveFileTo (targetFolder)) + return Result::fail ("Could not overwrite the existing folder!\n\n" + "This may happen if you are trying to download into a directory that requires administrator privileges to modify.\n" + "Please select a folder that is writable by the current user."); + + return Result::ok(); + } + + Result setFilePermissions (const File& root, const ZipFile& zip) + { + constexpr uint32 executableFlag = (1 << 22); + + for (int i = 0; i < zip.getNumEntries(); ++i) + { + auto* entry = zip.getEntry (i); + + if ((entry->externalFileAttributes & executableFlag) != 0 && entry->filename.getLastCharacter() != '/') + { + auto exeFile = root.getChildFile (entry->filename); + + if (! exeFile.exists()) + return Result::fail ("Failed to find executable file when setting permissions " + exeFile.getFileName()); + + if (! exeFile.setExecutePermission (true)) + return Result::fail ("Failed to set executable file permission for " + exeFile.getFileName()); + } + } + + return Result::ok(); + } + + VersionInfo::Asset asset; + File targetFolder; + std::function completionCallback; +}; + //============================================================================== LatestVersionCheckerAndUpdater::LatestVersionCheckerAndUpdater() : Thread ("VersionChecker") @@ -394,158 +546,6 @@ void LatestVersionCheckerAndUpdater::addNotificationToOpenProjects (const Versio } //============================================================================== -class DownloadAndInstallThread final : private ThreadWithProgressWindow -{ -public: - DownloadAndInstallThread (const VersionInfo::Asset& a, const File& t, std::function&& cb) - : ThreadWithProgressWindow ("Downloading New Version", true, true), - asset (a), targetFolder (t), completionCallback (std::move (cb)) - { - launchThread (Priority::low); - } - -private: - void run() override - { - setProgress (-1.0); - - MemoryBlock zipData; - auto result = download (zipData); - - if (result.wasOk() && ! threadShouldExit()) - result = install (zipData); - - MessageManager::callAsync ([result, callback = completionCallback] - { - callback (result); - }); - } - - Result download (MemoryBlock& dest) - { - setStatusMessage ("Downloading..."); - - int statusCode = 0; - auto inStream = VersionInfo::createInputStreamForAsset (asset, statusCode); - - if (inStream != nullptr && statusCode == 200) - { - int64 total = 0; - MemoryOutputStream mo (dest, true); - - for (;;) - { - if (threadShouldExit()) - return Result::fail ("Cancelled"); - - auto written = mo.writeFromInputStream (*inStream, 8192); - - if (written == 0) - break; - - total += written; - - setStatusMessage ("Downloading... " + File::descriptionOfSizeInBytes (total)); - } - - return Result::ok(); - } - - return Result::fail ("Failed to download from: " + asset.url); - } - - Result install (const MemoryBlock& data) - { - setStatusMessage ("Installing..."); - - MemoryInputStream input (data, false); - ZipFile zip (input); - - if (zip.getNumEntries() == 0) - return Result::fail ("The downloaded file was not a valid JUCE file!"); - - struct ScopedDownloadFolder - { - explicit ScopedDownloadFolder (const File& installTargetFolder) - { - folder = installTargetFolder.getSiblingFile (installTargetFolder.getFileNameWithoutExtension() + "_download").getNonexistentSibling(); - jassert (folder.createDirectory()); - } - - ~ScopedDownloadFolder() { folder.deleteRecursively(); } - - File folder; - }; - - ScopedDownloadFolder unzipTarget (targetFolder); - - if (! unzipTarget.folder.isDirectory()) - return Result::fail ("Couldn't create a temporary folder to unzip the new version!"); - - auto r = zip.uncompressTo (unzipTarget.folder); - - if (r.failed()) - return r; - - if (threadShouldExit()) - return Result::fail ("Cancelled"); - - #if JUCE_LINUX || JUCE_BSD || JUCE_MAC - r = setFilePermissions (unzipTarget.folder, zip); - - if (r.failed()) - return r; - - if (threadShouldExit()) - return Result::fail ("Cancelled"); - #endif - - if (targetFolder.exists()) - { - auto oldFolder = targetFolder.getSiblingFile (targetFolder.getFileNameWithoutExtension() + "_old").getNonexistentSibling(); - - if (! targetFolder.moveFileTo (oldFolder)) - return Result::fail ("Could not remove the existing folder!\n\n" - "This may happen if you are trying to download into a directory that requires administrator privileges to modify.\n" - "Please select a folder that is writable by the current user."); - } - - if (! unzipTarget.folder.getChildFile ("JUCE").moveFileTo (targetFolder)) - return Result::fail ("Could not overwrite the existing folder!\n\n" - "This may happen if you are trying to download into a directory that requires administrator privileges to modify.\n" - "Please select a folder that is writable by the current user."); - - return Result::ok(); - } - - Result setFilePermissions (const File& root, const ZipFile& zip) - { - constexpr uint32 executableFlag = (1 << 22); - - for (int i = 0; i < zip.getNumEntries(); ++i) - { - auto* entry = zip.getEntry (i); - - if ((entry->externalFileAttributes & executableFlag) != 0 && entry->filename.getLastCharacter() != '/') - { - auto exeFile = root.getChildFile (entry->filename); - - if (! exeFile.exists()) - return Result::fail ("Failed to find executable file when setting permissions " + exeFile.getFileName()); - - if (! exeFile.setExecutePermission (true)) - return Result::fail ("Failed to set executable file permission for " + exeFile.getFileName()); - } - } - - return Result::ok(); - } - - VersionInfo::Asset asset; - File targetFolder; - std::function completionCallback; -}; - static void restartProcess (const File& targetFolder) { #if JUCE_MAC || JUCE_LINUX || JUCE_BSD diff --git a/extras/Projucer/Source/CodeEditor/jucer_SourceCodeEditor.cpp b/extras/Projucer/Source/CodeEditor/jucer_SourceCodeEditor.cpp index 551e05cc5c..2473876a83 100644 --- a/extras/Projucer/Source/CodeEditor/jucer_SourceCodeEditor.cpp +++ b/extras/Projucer/Source/CodeEditor/jucer_SourceCodeEditor.cpp @@ -275,6 +275,89 @@ void SourceCodeEditor::valueTreeRedirected (ValueTree&) void SourceCodeEditor::codeDocumentTextInserted (const String&, int) { checkSaveState(); } void SourceCodeEditor::codeDocumentTextDeleted (int, int) { checkSaveState(); } +class GenericCodeEditorComponent::FindPanel final : public Component +{ +public: + FindPanel() + { + editor.setColour (CaretComponent::caretColourId, Colours::black); + + addAndMakeVisible (editor); + label.setColour (Label::textColourId, Colours::white); + label.attachToComponent (&editor, false); + + addAndMakeVisible (caseButton); + caseButton.setColour (ToggleButton::textColourId, Colours::white); + caseButton.setToggleState (isCaseSensitiveSearch(), dontSendNotification); + caseButton.onClick = [this] { setCaseSensitiveSearch (caseButton.getToggleState()); }; + + findPrev.setConnectedEdges (Button::ConnectedOnRight); + findNext.setConnectedEdges (Button::ConnectedOnLeft); + addAndMakeVisible (findPrev); + addAndMakeVisible (findNext); + + setWantsKeyboardFocus (false); + setFocusContainerType (FocusContainerType::keyboardFocusContainer); + findPrev.setWantsKeyboardFocus (false); + findNext.setWantsKeyboardFocus (false); + + editor.setText (getSearchString()); + editor.onTextChange = [this] { changeSearchString(); }; + editor.onReturnKey = [] { ProjucerApplication::getCommandManager().invokeDirectly (CommandIDs::findNext, true); }; + editor.onEscapeKey = [this] + { + if (auto* ed = getOwner()) + ed->hideFindPanel(); + }; + } + + void setCommandManager (ApplicationCommandManager* cm) + { + findPrev.setCommandToTrigger (cm, CommandIDs::findPrevious, true); + findNext.setCommandToTrigger (cm, CommandIDs::findNext, true); + } + + void paint (Graphics& g) override + { + Path outline; + outline.addRoundedRectangle (1.0f, 1.0f, (float) getWidth() - 2.0f, (float) getHeight() - 2.0f, 8.0f); + + g.setColour (Colours::black.withAlpha (0.6f)); + g.fillPath (outline); + g.setColour (Colours::white.withAlpha (0.8f)); + g.strokePath (outline, PathStrokeType (1.0f)); + } + + void resized() override + { + int y = 30; + editor.setBounds (10, y, getWidth() - 20, 24); + y += 30; + caseButton.setBounds (10, y, getWidth() / 2 - 10, 22); + findNext.setBounds (getWidth() - 40, y, 30, 22); + findPrev.setBounds (getWidth() - 70, y, 30, 22); + } + + void changeSearchString() + { + setSearchString (editor.getText()); + + if (auto* ed = getOwner()) + ed->findNext (true, false); + } + + GenericCodeEditorComponent* getOwner() const + { + return findParentComponentOfClass (); + } + + TextEditor editor; + Label label { {}, "Find:" }; + ToggleButton caseButton { "Case-sensitive" }; + TextButton findPrev { "<" }, + findNext { ">" }; +}; + //============================================================================== GenericCodeEditorComponent::GenericCodeEditorComponent (const File& f, CodeDocument& codeDocument, CodeTokeniser* tokeniser) @@ -384,89 +467,6 @@ void GenericCodeEditorComponent::removeListener (GenericCodeEditorComponent::Lis } //============================================================================== -class GenericCodeEditorComponent::FindPanel final : public Component -{ -public: - FindPanel() - { - editor.setColour (CaretComponent::caretColourId, Colours::black); - - addAndMakeVisible (editor); - label.setColour (Label::textColourId, Colours::white); - label.attachToComponent (&editor, false); - - addAndMakeVisible (caseButton); - caseButton.setColour (ToggleButton::textColourId, Colours::white); - caseButton.setToggleState (isCaseSensitiveSearch(), dontSendNotification); - caseButton.onClick = [this] { setCaseSensitiveSearch (caseButton.getToggleState()); }; - - findPrev.setConnectedEdges (Button::ConnectedOnRight); - findNext.setConnectedEdges (Button::ConnectedOnLeft); - addAndMakeVisible (findPrev); - addAndMakeVisible (findNext); - - setWantsKeyboardFocus (false); - setFocusContainerType (FocusContainerType::keyboardFocusContainer); - findPrev.setWantsKeyboardFocus (false); - findNext.setWantsKeyboardFocus (false); - - editor.setText (getSearchString()); - editor.onTextChange = [this] { changeSearchString(); }; - editor.onReturnKey = [] { ProjucerApplication::getCommandManager().invokeDirectly (CommandIDs::findNext, true); }; - editor.onEscapeKey = [this] - { - if (auto* ed = getOwner()) - ed->hideFindPanel(); - }; - } - - void setCommandManager (ApplicationCommandManager* cm) - { - findPrev.setCommandToTrigger (cm, CommandIDs::findPrevious, true); - findNext.setCommandToTrigger (cm, CommandIDs::findNext, true); - } - - void paint (Graphics& g) override - { - Path outline; - outline.addRoundedRectangle (1.0f, 1.0f, (float) getWidth() - 2.0f, (float) getHeight() - 2.0f, 8.0f); - - g.setColour (Colours::black.withAlpha (0.6f)); - g.fillPath (outline); - g.setColour (Colours::white.withAlpha (0.8f)); - g.strokePath (outline, PathStrokeType (1.0f)); - } - - void resized() override - { - int y = 30; - editor.setBounds (10, y, getWidth() - 20, 24); - y += 30; - caseButton.setBounds (10, y, getWidth() / 2 - 10, 22); - findNext.setBounds (getWidth() - 40, y, 30, 22); - findPrev.setBounds (getWidth() - 70, y, 30, 22); - } - - void changeSearchString() - { - setSearchString (editor.getText()); - - if (auto* ed = getOwner()) - ed->findNext (true, false); - } - - GenericCodeEditorComponent* getOwner() const - { - return findParentComponentOfClass (); - } - - TextEditor editor; - Label label { {}, "Find:" }; - ToggleButton caseButton { "Case-sensitive" }; - TextButton findPrev { "<" }, - findNext { ">" }; -}; - void GenericCodeEditorComponent::resized() { CodeEditorComponent::resized(); diff --git a/modules/juce_audio_devices/juce_audio_devices.cpp b/modules/juce_audio_devices/juce_audio_devices.cpp index 77c06e5ab9..7d01a6246c 100644 --- a/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/modules/juce_audio_devices/juce_audio_devices.cpp @@ -58,7 +58,7 @@ #include "juce_audio_devices.h" #include "audio_io/juce_SampleRateHelpers.cpp" -#include "midi_io/juce_MidiDevices.cpp" +#include "midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp" //============================================================================== #if JUCE_MAC || JUCE_IOS @@ -258,6 +258,8 @@ namespace juce #endif +#include "midi_io/juce_MidiDevices.cpp" + #if ! JUCE_SYSTEMAUDIOVOL_IMPLEMENTED namespace juce { diff --git a/modules/juce_audio_devices/midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp b/modules/juce_audio_devices/midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp new file mode 100644 index 0000000000..89e843d765 --- /dev/null +++ b/modules/juce_audio_devices/midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp @@ -0,0 +1,115 @@ +/* + ============================================================================== + + This file is part of the JUCE framework. + Copyright (c) Raw Material Software Limited + + JUCE is an open source framework subject to commercial or open source + licensing. + + By downloading, installing, or using the JUCE framework, or combining the + JUCE framework with any other source code, object code, content or any other + copyrightable work, you agree to the terms of the JUCE End User Licence + Agreement, and all incorporated terms including the JUCE Privacy Policy and + the JUCE Website Terms of Service, as applicable, which will bind you. If you + do not agree to the terms of these agreements, we will not license the JUCE + framework to you, and you must discontinue the installation or download + process and cease use of the JUCE framework. + + JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/ + JUCE Privacy Policy: https://juce.com/juce-privacy-policy + JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/ + + Or: + + You may also use this code under the terms of the AGPLv3: + https://www.gnu.org/licenses/agpl-3.0.en.html + + THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL + WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +class MidiDeviceListConnectionBroadcaster final : private AsyncUpdater +{ +public: + ~MidiDeviceListConnectionBroadcaster() override + { + cancelPendingUpdate(); + } + + MidiDeviceListConnection::Key add (std::function callback) + { + JUCE_ASSERT_MESSAGE_THREAD + return callbacks.emplace (key++, std::move (callback)).first->first; + } + + void remove (const MidiDeviceListConnection::Key k) + { + JUCE_ASSERT_MESSAGE_THREAD + callbacks.erase (k); + } + + void notify() + { + if (MessageManager::getInstance()->isThisTheMessageThread()) + { + cancelPendingUpdate(); + + const State newState; + + if (std::exchange (lastNotifiedState, newState) != newState) + for (auto it = callbacks.begin(); it != callbacks.end();) + NullCheckedInvocation::invoke ((it++)->second); + } + else + { + triggerAsyncUpdate(); + } + } + + static auto& get() + { + static MidiDeviceListConnectionBroadcaster result; + return result; + } + +private: + MidiDeviceListConnectionBroadcaster() = default; + + class State + { + Array ins = MidiInput::getAvailableDevices(), + outs = MidiOutput::getAvailableDevices(); + auto tie() const + { + return std::tie (ins, outs); + } + + public: + bool operator== (const State& other) const + { + return tie() == other.tie(); + } + bool operator!= (const State& other) const + { + return tie() != other.tie(); + } + }; + + void handleAsyncUpdate() override + { + notify(); + } + + std::map> callbacks; + State lastNotifiedState; + MidiDeviceListConnection::Key key = 0; +}; + +} // namespace juce diff --git a/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp b/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp index 27c587e329..b7b0e35078 100644 --- a/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp +++ b/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp @@ -35,74 +35,6 @@ namespace juce { -class MidiDeviceListConnectionBroadcaster final : private AsyncUpdater -{ -public: - ~MidiDeviceListConnectionBroadcaster() override - { - cancelPendingUpdate(); - } - - MidiDeviceListConnection::Key add (std::function callback) - { - JUCE_ASSERT_MESSAGE_THREAD - return callbacks.emplace (key++, std::move (callback)).first->first; - } - - void remove (const MidiDeviceListConnection::Key k) - { - JUCE_ASSERT_MESSAGE_THREAD - callbacks.erase (k); - } - - void notify() - { - if (MessageManager::getInstance()->isThisTheMessageThread()) - { - cancelPendingUpdate(); - - const State newState; - - if (std::exchange (lastNotifiedState, newState) != newState) - for (auto it = callbacks.begin(); it != callbacks.end();) - NullCheckedInvocation::invoke ((it++)->second); - } - else - { - triggerAsyncUpdate(); - } - } - - static auto& get() - { - static MidiDeviceListConnectionBroadcaster result; - return result; - } - -private: - MidiDeviceListConnectionBroadcaster() = default; - - class State - { - Array ins = MidiInput::getAvailableDevices(), outs = MidiOutput::getAvailableDevices(); - auto tie() const { return std::tie (ins, outs); } - - public: - bool operator== (const State& other) const { return tie() == other.tie(); } - bool operator!= (const State& other) const { return tie() != other.tie(); } - }; - - void handleAsyncUpdate() override - { - notify(); - } - - std::map> callbacks; - State lastNotifiedState; - MidiDeviceListConnection::Key key = 0; -}; - -//============================================================================== MidiDeviceListConnection::~MidiDeviceListConnection() noexcept { if (broadcaster != nullptr) diff --git a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index 4b51311a37..3c748685d9 100644 --- a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -148,247 +148,6 @@ public: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableModel) }; -//============================================================================== -PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, KnownPluginList& listToEdit, - const File& deadMansPedal, PropertiesFile* const props, - bool allowPluginsWhichRequireAsynchronousInstantiation) - : formatManager (manager), - list (listToEdit), - deadMansPedalFile (deadMansPedal), - optionsButton ("Options..."), - propertiesToUse (props), - allowAsync (allowPluginsWhichRequireAsynchronousInstantiation), - numThreads (allowAsync ? 1 : 0) -{ - tableModel.reset (new TableModel (*this, listToEdit)); - - TableHeaderComponent& header = table.getHeader(); - - header.addColumn (TRANS ("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); - header.addColumn (TRANS ("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); - header.addColumn (TRANS ("Category"), TableModel::categoryCol, 100, 100, 200); - header.addColumn (TRANS ("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); - header.addColumn (TRANS ("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); - - table.setHeaderHeight (22); - table.setRowHeight (20); - table.setModel (tableModel.get()); - table.setMultipleSelectionEnabled (true); - addAndMakeVisible (table); - - addAndMakeVisible (optionsButton); - optionsButton.onClick = [this] - { - createOptionsMenu().showMenuAsync (PopupMenu::Options() - .withDeletionCheck (*this) - .withTargetComponent (optionsButton)); - }; - - optionsButton.setTriggeredOnMouseDown (true); - - setSize (400, 600); - list.addChangeListener (this); - updateList(); - table.getHeader().reSortTable(); - - PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (list, deadMansPedalFile); - deadMansPedalFile.deleteFile(); -} - -PluginListComponent::~PluginListComponent() -{ - list.removeChangeListener (this); -} - -void PluginListComponent::setOptionsButtonText (const String& newText) -{ - optionsButton.setButtonText (newText); - resized(); -} - -void PluginListComponent::setScanDialogText (const String& title, const String& content) -{ - dialogTitle = title; - dialogText = content; -} - -void PluginListComponent::setNumberOfThreadsForScanning (int num) -{ - numThreads = num; -} - -void PluginListComponent::resized() -{ - auto r = getLocalBounds().reduced (2); - - if (optionsButton.isVisible()) - { - optionsButton.setBounds (r.removeFromBottom (24)); - optionsButton.changeWidthToFitText (24); - r.removeFromBottom (3); - } - - table.setBounds (r); -} - -void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) -{ - table.getHeader().reSortTable(); - updateList(); -} - -void PluginListComponent::updateList() -{ - table.updateContent(); - table.repaint(); -} - -void PluginListComponent::removeSelectedPlugins() -{ - auto selected = table.getSelectedRows(); - - for (int i = table.getNumRows(); --i >= 0;) - if (selected.contains (i)) - removePluginItem (i); -} - -void PluginListComponent::setTableModel (TableListBoxModel* model) -{ - table.setModel (nullptr); - tableModel.reset (model); - table.setModel (tableModel.get()); - - table.getHeader().reSortTable(); - table.updateContent(); - table.repaint(); -} - -static bool canShowFolderForPlugin (KnownPluginList& list, int index) -{ - return File::createFileWithoutCheckingPath (list.getTypes()[index].fileOrIdentifier).exists(); -} - -static void showFolderForPlugin (KnownPluginList& list, int index) -{ - if (canShowFolderForPlugin (list, index)) - File (list.getTypes()[index].fileOrIdentifier).revealToUser(); -} - -void PluginListComponent::removeMissingPlugins() -{ - auto types = list.getTypes(); - - for (int i = types.size(); --i >= 0;) - { - auto type = types.getUnchecked (i); - - if (! formatManager.doesPluginStillExist (type)) - list.removeType (type); - } -} - -void PluginListComponent::removePluginItem (int index) -{ - if (index < list.getNumTypes()) - list.removeType (list.getTypes()[index]); - else - list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); -} - -PopupMenu PluginListComponent::createOptionsMenu() -{ - PopupMenu menu; - menu.addItem (PopupMenu::Item (TRANS ("Clear list")) - .setAction ([this] { list.clear(); })); - - menu.addSeparator(); - - for (auto format : formatManager.getFormats()) - if (format->canScanForPlugins()) - menu.addItem (PopupMenu::Item ("Remove all " + format->getName() + " plug-ins") - .setEnabled (! list.getTypesForFormat (*format).isEmpty()) - .setAction ([this, format] - { - for (auto& pd : list.getTypesForFormat (*format)) - list.removeType (pd); - })); - - menu.addSeparator(); - - menu.addItem (PopupMenu::Item (TRANS ("Remove selected plug-in from list")) - .setEnabled (table.getNumSelectedRows() > 0) - .setAction ([this] { removeSelectedPlugins(); })); - - menu.addItem (PopupMenu::Item (TRANS ("Remove any plug-ins whose files no longer exist")) - .setAction ([this] { removeMissingPlugins(); })); - - menu.addSeparator(); - - auto selectedRow = table.getSelectedRow(); - - menu.addItem (PopupMenu::Item (TRANS ("Show folder containing selected plug-in")) - .setEnabled (canShowFolderForPlugin (list, selectedRow)) - .setAction ([this, selectedRow] { showFolderForPlugin (list, selectedRow); })); - - menu.addSeparator(); - - for (auto format : formatManager.getFormats()) - if (format->canScanForPlugins()) - menu.addItem (PopupMenu::Item ("Scan for new or updated " + format->getName() + " plug-ins") - .setAction ([this, format] { scanFor (*format); })); - - return menu; -} - -PopupMenu PluginListComponent::createMenuForRow (int rowNumber) -{ - PopupMenu menu; - - if (rowNumber >= 0 && rowNumber < tableModel->getNumRows()) - { - menu.addItem (PopupMenu::Item (TRANS ("Remove plug-in from list")) - .setAction ([this, rowNumber] { removePluginItem (rowNumber); })); - - menu.addItem (PopupMenu::Item (TRANS ("Show folder containing plug-in")) - .setEnabled (canShowFolderForPlugin (list, rowNumber)) - .setAction ([this, rowNumber] { showFolderForPlugin (list, rowNumber); })); - } - - return menu; -} - -bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) -{ - return true; -} - -void PluginListComponent::filesDropped (const StringArray& files, int, int) -{ - OwnedArray typesFound; - list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); -} - -FileSearchPath PluginListComponent::getLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format) -{ - auto key = "lastPluginScanPath_" + format.getName(); - - if (properties.containsKey (key) && properties.getValue (key, {}).trim().isEmpty()) - properties.removeValue (key); - - return FileSearchPath (properties.getValue (key, format.getDefaultLocationsToSearch().toString())); -} - -void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format, - const FileSearchPath& newPath) -{ - auto key = "lastPluginScanPath_" + format.getName(); - - if (newPath.getNumPaths() == 0) - properties.removeValue (key); - else - properties.setValue (key, newPath.toString()); -} - //============================================================================== class PluginListComponent::Scanner final : private Timer { @@ -638,6 +397,248 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Scanner) }; +//============================================================================== +PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, KnownPluginList& listToEdit, + const File& deadMansPedal, PropertiesFile* const props, + bool allowPluginsWhichRequireAsynchronousInstantiation) + : formatManager (manager), + list (listToEdit), + deadMansPedalFile (deadMansPedal), + optionsButton ("Options..."), + propertiesToUse (props), + allowAsync (allowPluginsWhichRequireAsynchronousInstantiation), + numThreads (allowAsync ? 1 : 0) +{ + tableModel.reset (new TableModel (*this, listToEdit)); + + TableHeaderComponent& header = table.getHeader(); + + header.addColumn (TRANS ("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); + header.addColumn (TRANS ("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); + header.addColumn (TRANS ("Category"), TableModel::categoryCol, 100, 100, 200); + header.addColumn (TRANS ("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); + header.addColumn (TRANS ("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); + + table.setHeaderHeight (22); + table.setRowHeight (20); + table.setModel (tableModel.get()); + table.setMultipleSelectionEnabled (true); + addAndMakeVisible (table); + + addAndMakeVisible (optionsButton); + optionsButton.onClick = [this] + { + createOptionsMenu().showMenuAsync (PopupMenu::Options() + .withDeletionCheck (*this) + .withTargetComponent (optionsButton)); + }; + + optionsButton.setTriggeredOnMouseDown (true); + + setSize (400, 600); + list.addChangeListener (this); + updateList(); + table.getHeader().reSortTable(); + + PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (list, deadMansPedalFile); + deadMansPedalFile.deleteFile(); +} + +PluginListComponent::~PluginListComponent() +{ + list.removeChangeListener (this); +} + +void PluginListComponent::setOptionsButtonText (const String& newText) +{ + optionsButton.setButtonText (newText); + resized(); +} + +void PluginListComponent::setScanDialogText (const String& title, const String& content) +{ + dialogTitle = title; + dialogText = content; +} + +void PluginListComponent::setNumberOfThreadsForScanning (int num) +{ + numThreads = num; +} + +void PluginListComponent::resized() +{ + auto r = getLocalBounds().reduced (2); + + if (optionsButton.isVisible()) + { + optionsButton.setBounds (r.removeFromBottom (24)); + optionsButton.changeWidthToFitText (24); + r.removeFromBottom (3); + } + + table.setBounds (r); +} + +void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) +{ + table.getHeader().reSortTable(); + updateList(); +} + +void PluginListComponent::updateList() +{ + table.updateContent(); + table.repaint(); +} + +void PluginListComponent::removeSelectedPlugins() +{ + auto selected = table.getSelectedRows(); + + for (int i = table.getNumRows(); --i >= 0;) + if (selected.contains (i)) + removePluginItem (i); +} + +void PluginListComponent::setTableModel (TableListBoxModel* model) +{ + table.setModel (nullptr); + tableModel.reset (model); + table.setModel (tableModel.get()); + + table.getHeader().reSortTable(); + table.updateContent(); + table.repaint(); +} + +static bool canShowFolderForPlugin (KnownPluginList& list, int index) +{ + return File::createFileWithoutCheckingPath (list.getTypes()[index].fileOrIdentifier).exists(); +} + +static void showFolderForPlugin (KnownPluginList& list, int index) +{ + if (canShowFolderForPlugin (list, index)) + File (list.getTypes()[index].fileOrIdentifier).revealToUser(); +} + +void PluginListComponent::removeMissingPlugins() +{ + auto types = list.getTypes(); + + for (int i = types.size(); --i >= 0;) + { + auto type = types.getUnchecked (i); + + if (! formatManager.doesPluginStillExist (type)) + list.removeType (type); + } +} + +void PluginListComponent::removePluginItem (int index) +{ + if (index < list.getNumTypes()) + list.removeType (list.getTypes()[index]); + else + list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); +} + +PopupMenu PluginListComponent::createOptionsMenu() +{ + PopupMenu menu; + menu.addItem (PopupMenu::Item (TRANS ("Clear list")) + .setAction ([this] { list.clear(); })); + + menu.addSeparator(); + + for (auto format : formatManager.getFormats()) + if (format->canScanForPlugins()) + menu.addItem (PopupMenu::Item ("Remove all " + format->getName() + " plug-ins") + .setEnabled (! list.getTypesForFormat (*format).isEmpty()) + .setAction ([this, format] + { + for (auto& pd : list.getTypesForFormat (*format)) + list.removeType (pd); + })); + + menu.addSeparator(); + + menu.addItem (PopupMenu::Item (TRANS ("Remove selected plug-in from list")) + .setEnabled (table.getNumSelectedRows() > 0) + .setAction ([this] { removeSelectedPlugins(); })); + + menu.addItem (PopupMenu::Item (TRANS ("Remove any plug-ins whose files no longer exist")) + .setAction ([this] { removeMissingPlugins(); })); + + menu.addSeparator(); + + auto selectedRow = table.getSelectedRow(); + + menu.addItem (PopupMenu::Item (TRANS ("Show folder containing selected plug-in")) + .setEnabled (canShowFolderForPlugin (list, selectedRow)) + .setAction ([this, selectedRow] { showFolderForPlugin (list, selectedRow); })); + + menu.addSeparator(); + + for (auto format : formatManager.getFormats()) + if (format->canScanForPlugins()) + menu.addItem (PopupMenu::Item ("Scan for new or updated " + format->getName() + " plug-ins") + .setAction ([this, format] { scanFor (*format); })); + + return menu; +} + +PopupMenu PluginListComponent::createMenuForRow (int rowNumber) +{ + PopupMenu menu; + + if (rowNumber >= 0 && rowNumber < tableModel->getNumRows()) + { + menu.addItem (PopupMenu::Item (TRANS ("Remove plug-in from list")) + .setAction ([this, rowNumber] { removePluginItem (rowNumber); })); + + menu.addItem (PopupMenu::Item (TRANS ("Show folder containing plug-in")) + .setEnabled (canShowFolderForPlugin (list, rowNumber)) + .setAction ([this, rowNumber] { showFolderForPlugin (list, rowNumber); })); + } + + return menu; +} + +bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) +{ + return true; +} + +void PluginListComponent::filesDropped (const StringArray& files, int, int) +{ + OwnedArray typesFound; + list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); +} + +FileSearchPath PluginListComponent::getLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format) +{ + auto key = "lastPluginScanPath_" + format.getName(); + + if (properties.containsKey (key) && properties.getValue (key, {}).trim().isEmpty()) + properties.removeValue (key); + + return FileSearchPath (properties.getValue (key, format.getDefaultLocationsToSearch().toString())); +} + +void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format, + const FileSearchPath& newPath) +{ + auto key = "lastPluginScanPath_" + format.getName(); + + if (newPath.getNumPaths() == 0) + properties.removeValue (key); + else + properties.setValue (key, newPath.toString()); +} + +//============================================================================== void PluginListComponent::scanFor (AudioPluginFormat& format) { scanFor (format, StringArray()); diff --git a/modules/juce_core/juce_core.cpp b/modules/juce_core/juce_core.cpp index 5f44d2a50b..569ee83710 100644 --- a/modules/juce_core/juce_core.cpp +++ b/modules/juce_core/juce_core.cpp @@ -153,6 +153,14 @@ #include "misc/juce_ConsoleApplication.cpp" #include "misc/juce_ScopeGuard.cpp" #include "network/juce_MACAddress.cpp" + +#if ! JUCE_WINDOWS + #include "native/juce_SharedCode_posix.h" + #include "native/juce_NamedPipe_posix.cpp" +#else + #include "native/juce_Files_windows.cpp" +#endif + #include "network/juce_NamedPipe.cpp" #include "network/juce_Socket.cpp" #include "network/juce_IPAddress.cpp" @@ -197,12 +205,8 @@ #include "native/juce_PlatformTimerListener.h" //============================================================================== -#if ! JUCE_WINDOWS - #include "native/juce_SharedCode_posix.h" - #include "native/juce_NamedPipe_posix.cpp" - #if ! JUCE_ANDROID || __ANDROID_API__ >= 24 - #include "native/juce_IPAddress_posix.h" - #endif +#if ! JUCE_WINDOWS && (! JUCE_ANDROID || __ANDROID_API__ >= 24) + #include "native/juce_IPAddress_posix.h" #endif //============================================================================== @@ -218,7 +222,6 @@ //============================================================================== #elif JUCE_WINDOWS - #include "native/juce_Files_windows.cpp" #include "native/juce_Network_windows.cpp" #include "native/juce_Registry_windows.cpp" #include "native/juce_SystemStats_windows.cpp" diff --git a/modules/juce_core/native/juce_SharedCode_posix.h b/modules/juce_core/native/juce_SharedCode_posix.h index 0efb43a53d..c53ef0fff6 100644 --- a/modules/juce_core/native/juce_SharedCode_posix.h +++ b/modules/juce_core/native/juce_SharedCode_posix.h @@ -152,7 +152,7 @@ bool File::setAsCurrentWorkingDirectory() const //============================================================================== // The unix siginterrupt function is deprecated - this does the same job. -int juce_siginterrupt ([[maybe_unused]] int sig, [[maybe_unused]] int flag) +inline int juce_siginterrupt ([[maybe_unused]] int sig, [[maybe_unused]] int flag) { #if JUCE_WASM return 0; diff --git a/modules/juce_core/system/juce_SystemStats.cpp b/modules/juce_core/system/juce_SystemStats.cpp index e7ac5aceb9..6dafa07185 100644 --- a/modules/juce_core/system/juce_SystemStats.cpp +++ b/modules/juce_core/system/juce_SystemStats.cpp @@ -255,8 +255,6 @@ static void handleCrash (int signum) globalCrashHandler ((void*) (pointer_sized_int) signum); ::kill (getpid(), SIGKILL); } - -int juce_siginterrupt (int sig, int flag); #endif void SystemStats::setApplicationCrashHandler (CrashHandlerFunction handler) diff --git a/modules/juce_events/messages/juce_ApplicationBase.cpp b/modules/juce_events/messages/juce_ApplicationBase.cpp index b93bdf39fa..0dc5e6c0f6 100644 --- a/modules/juce_events/messages/juce_ApplicationBase.cpp +++ b/modules/juce_events/messages/juce_ApplicationBase.cpp @@ -42,18 +42,6 @@ JUCEApplicationBase* JUCEApplicationBase::appInstance = nullptr; void* JUCEApplicationBase::iOSCustomDelegate = nullptr; #endif -JUCEApplicationBase::JUCEApplicationBase() -{ - jassert (isStandaloneApp() && appInstance == nullptr); - appInstance = this; -} - -JUCEApplicationBase::~JUCEApplicationBase() -{ - jassert (appInstance == this); - appInstance = nullptr; -} - void JUCEApplicationBase::setApplicationReturnValue (const int newReturnValue) noexcept { appReturnValue = newReturnValue; @@ -154,6 +142,18 @@ bool JUCEApplicationBase::sendCommandLineToPreexistingInstance() struct JUCEApplicationBase::MultipleInstanceHandler {}; #endif +JUCEApplicationBase::JUCEApplicationBase() +{ + jassert (isStandaloneApp() && appInstance == nullptr); + appInstance = this; +} + +JUCEApplicationBase::~JUCEApplicationBase() +{ + jassert (appInstance == this); + appInstance = nullptr; +} + //============================================================================== #if JUCE_ANDROID diff --git a/modules/juce_graphics/juce_graphics.cpp b/modules/juce_graphics/juce_graphics.cpp index 296dae0975..b0ad24734a 100644 --- a/modules/juce_graphics/juce_graphics.cpp +++ b/modules/juce_graphics/juce_graphics.cpp @@ -52,6 +52,7 @@ //============================================================================== #if JUCE_MAC #import + #include #include #elif JUCE_WINDOWS diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp index 59bfaf7eae..1db1947129 100644 --- a/modules/juce_gui_basics/components/juce_Component.cpp +++ b/modules/juce_gui_basics/components/juce_Component.cpp @@ -172,6 +172,50 @@ private: JUCE_DECLARE_NON_COPYABLE (MouseListenerList) }; +class Component::EffectState +{ +public: + explicit EffectState (ImageEffectFilter& i) : effect (&i) {} + + ImageEffectFilter& getEffect() const + { + return *effect; + } + + bool setEffect (ImageEffectFilter& i) + { + return std::exchange (effect, &i) != &i; + } + + void paint (Graphics& g, Component& c, bool ignoreAlphaLevel) + { + auto scale = g.getInternalContext().getPhysicalPixelScaleFactor(); + auto scaledBounds = c.getLocalBounds() * scale; + + if (effectImage.getBounds() != scaledBounds) + effectImage = Image { c.isOpaque() ? Image::RGB : Image::ARGB, scaledBounds.getWidth(), scaledBounds.getHeight(), false }; + + if (! c.isOpaque()) + effectImage.clear (effectImage.getBounds()); + + { + Graphics g2 (effectImage); + g2.addTransform (AffineTransform::scale ((float) scaledBounds.getWidth() / (float) c.getWidth(), + (float) scaledBounds.getHeight() / (float) c.getHeight())); + c.paintComponentAndChildren (g2); + } + + Graphics::ScopedSaveState ss (g); + + g.addTransform (AffineTransform::scale (1.0f / scale)); + effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : c.getAlpha()); + } + +private: + Image effectImage; + ImageEffectFilter* effect; +}; + //============================================================================== Component::Component() noexcept : componentFlags (0) @@ -1695,50 +1739,6 @@ void Component::paintComponentAndChildren (Graphics& g) paintOverChildren (g); } -class Component::EffectState -{ -public: - explicit EffectState (ImageEffectFilter& i) : effect (&i) {} - - ImageEffectFilter& getEffect() const - { - return *effect; - } - - bool setEffect (ImageEffectFilter& i) - { - return std::exchange (effect, &i) != &i; - } - - void paint (Graphics& g, Component& c, bool ignoreAlphaLevel) - { - auto scale = g.getInternalContext().getPhysicalPixelScaleFactor(); - auto scaledBounds = c.getLocalBounds() * scale; - - if (effectImage.getBounds() != scaledBounds) - effectImage = Image { c.isOpaque() ? Image::RGB : Image::ARGB, scaledBounds.getWidth(), scaledBounds.getHeight(), false }; - - if (! c.isOpaque()) - effectImage.clear (effectImage.getBounds()); - - { - Graphics g2 (effectImage); - g2.addTransform (AffineTransform::scale ((float) scaledBounds.getWidth() / (float) c.getWidth(), - (float) scaledBounds.getHeight() / (float) c.getHeight())); - c.paintComponentAndChildren (g2); - } - - Graphics::ScopedSaveState ss (g); - - g.addTransform (AffineTransform::scale (1.0f / scale)); - effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : c.getAlpha()); - } - -private: - Image effectImage; - ImageEffectFilter* effect; -}; - void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel) { // If sizing a top-level-window and the OS paint message is delivered synchronously diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp index 6c9525a0b0..933ab8128b 100644 --- a/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/modules/juce_gui_basics/juce_gui_basics.cpp @@ -244,6 +244,7 @@ #endif //============================================================================== +#include "native/accessibility/juce_Accessibility.cpp" #include "accessibility/juce_AccessibilityHandler.cpp" #include "application/juce_Application.cpp" #include "buttons/juce_ArrowButton.cpp" @@ -330,7 +331,6 @@ #include "mouse/juce_MouseInactivityDetector.cpp" #include "mouse/juce_MouseInputSource.cpp" #include "mouse/juce_MouseListener.cpp" -#include "native/accessibility/juce_Accessibility.cpp" #include "native/juce_ScopedDPIAwarenessDisabler.cpp" #include "positioning/juce_MarkerList.cpp" #include "positioning/juce_RelativeCoordinate.cpp" diff --git a/modules/juce_gui_basics/juce_gui_basics.h b/modules/juce_gui_basics/juce_gui_basics.h index 05b8ff48e1..9351d8a9f4 100644 --- a/modules/juce_gui_basics/juce_gui_basics.h +++ b/modules/juce_gui_basics/juce_gui_basics.h @@ -249,6 +249,16 @@ namespace juce #include "layout/juce_StretchableObjectResizer.h" #include "layout/juce_TabbedButtonBar.h" #include "layout/juce_TabbedComponent.h" +#include "accessibility/interfaces/juce_AccessibilityCellInterface.h" +#include "accessibility/interfaces/juce_AccessibilityTableInterface.h" +#include "accessibility/interfaces/juce_AccessibilityTextInterface.h" +#include "accessibility/interfaces/juce_AccessibilityValueInterface.h" +#include "accessibility/enums/juce_AccessibilityActions.h" +#include "accessibility/enums/juce_AccessibilityEvent.h" +#include "accessibility/enums/juce_AccessibilityRole.h" +#include "accessibility/juce_AccessibilityState.h" +#include "accessibility/juce_AccessibilityHandler.h" +#include "drawables/juce_Drawable.h" #include "layout/juce_Viewport.h" #include "menus/juce_PopupMenu.h" #include "menus/juce_MenuBarModel.h" @@ -260,7 +270,6 @@ namespace juce #include "positioning/juce_RelativeCoordinatePositioner.h" #include "positioning/juce_RelativeParallelogram.h" #include "positioning/juce_RelativePointPath.h" -#include "drawables/juce_Drawable.h" #include "drawables/juce_DrawableShape.h" #include "drawables/juce_DrawableComposite.h" #include "drawables/juce_DrawableImage.h" @@ -331,15 +340,6 @@ namespace juce #include "lookandfeel/juce_LookAndFeel_V3.h" #include "lookandfeel/juce_LookAndFeel_V4.h" #include "mouse/juce_LassoComponent.h" -#include "accessibility/interfaces/juce_AccessibilityCellInterface.h" -#include "accessibility/interfaces/juce_AccessibilityTableInterface.h" -#include "accessibility/interfaces/juce_AccessibilityTextInterface.h" -#include "accessibility/interfaces/juce_AccessibilityValueInterface.h" -#include "accessibility/enums/juce_AccessibilityActions.h" -#include "accessibility/enums/juce_AccessibilityEvent.h" -#include "accessibility/enums/juce_AccessibilityRole.h" -#include "accessibility/juce_AccessibilityState.h" -#include "accessibility/juce_AccessibilityHandler.h" #if JUCE_LINUX || JUCE_BSD #if JUCE_GUI_BASICS_INCLUDE_XHEADERS diff --git a/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/modules/juce_gui_basics/widgets/juce_TreeView.cpp index 0610e2c3d6..251aeaa6fa 100644 --- a/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -827,6 +827,139 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeViewport) }; +//============================================================================== +struct TreeView::InsertPoint +{ + InsertPoint (TreeView& view, const StringArray& files, + const DragAndDropTarget::SourceDetails& dragSourceDetails) + : pos (dragSourceDetails.localPosition), + item (view.getItemAt (dragSourceDetails.localPosition.y)) + { + if (item != nullptr) + { + auto itemPos = item->getItemPosition (true); + insertIndex = item->getIndexInParent(); + auto oldY = pos.y; + pos.y = itemPos.getY(); + + if (item->getNumSubItems() == 0 || ! item->isOpen()) + { + if (files.size() > 0 ? item->isInterestedInFileDrag (files) + : item->isInterestedInDragSource (dragSourceDetails)) + { + // Check if we're trying to drag into an empty group item.. + if (oldY > itemPos.getY() + itemPos.getHeight() / 4 + && oldY < itemPos.getBottom() - itemPos.getHeight() / 4) + { + insertIndex = 0; + pos.x = itemPos.getX() + view.getIndentSize(); + pos.y = itemPos.getBottom(); + return; + } + } + } + + if (oldY > itemPos.getCentreY()) + { + pos.y += item->getItemHeight(); + + while (item->isLastOfSiblings() && item->getParentItem() != nullptr + && item->getParentItem()->getParentItem() != nullptr) + { + if (pos.x > itemPos.getX()) + break; + + item = item->getParentItem(); + itemPos = item->getItemPosition (true); + insertIndex = item->getIndexInParent(); + } + + ++insertIndex; + } + + pos.x = itemPos.getX(); + item = item->getParentItem(); + } + else if (auto* root = view.getRootItem()) + { + // If they're dragging beyond the bottom of the list, then insert at the end of the root item.. + item = root; + insertIndex = root->getNumSubItems(); + pos = root->getItemPosition (true).getBottomLeft(); + pos.x += view.getIndentSize(); + } + } + + Point pos; + TreeViewItem* item; + int insertIndex = 0; +}; + +//============================================================================== +class TreeView::InsertPointHighlight final : public Component +{ +public: + InsertPointHighlight() + { + setSize (100, 12); + setAlwaysOnTop (true); + setInterceptsMouseClicks (false, false); + } + + void setTargetPosition (const InsertPoint& insertPos, const int width) noexcept + { + lastItem = insertPos.item; + lastIndex = insertPos.insertIndex; + auto offset = getHeight() / 2; + setBounds (insertPos.pos.x - offset, insertPos.pos.y - offset, + width - (insertPos.pos.x - offset), getHeight()); + } + + void paint (Graphics& g) override + { + Path p; + auto h = (float) getHeight(); + p.addEllipse (2.0f, 2.0f, h - 4.0f, h - 4.0f); + p.startNewSubPath (h - 2.0f, h / 2.0f); + p.lineTo ((float) getWidth(), h / 2.0f); + + g.setColour (findColour (TreeView::dragAndDropIndicatorColourId, true)); + g.strokePath (p, PathStrokeType (2.0f)); + } + + TreeViewItem* lastItem = nullptr; + int lastIndex = 0; + +private: + JUCE_DECLARE_NON_COPYABLE (InsertPointHighlight) +}; + +//============================================================================== +class TreeView::TargetGroupHighlight final : public Component +{ +public: + TargetGroupHighlight() + { + setAlwaysOnTop (true); + setInterceptsMouseClicks (false, false); + } + + void setTargetPosition (TreeViewItem* const item) noexcept + { + setBounds (item->getItemPosition (true) + .withHeight (item->getItemHeight())); + } + + void paint (Graphics& g) override + { + g.setColour (findColour (TreeView::dragAndDropIndicatorColourId, true)); + g.drawRoundedRectangle (1.0f, 1.0f, (float) getWidth() - 2.0f, (float) getHeight() - 2.0f, 3.0f, 2.0f); + } + +private: + JUCE_DECLARE_NON_COPYABLE (TargetGroupHighlight) +}; + //============================================================================== TreeView::TreeView (const String& name) : Component (name) { @@ -1234,139 +1367,6 @@ void TreeView::updateVisibleItems (std::optional> viewportPosition) viewport->recalculatePositions (TreeViewport::Async::yes, std::move (viewportPosition)); } -//============================================================================== -struct TreeView::InsertPoint -{ - InsertPoint (TreeView& view, const StringArray& files, - const DragAndDropTarget::SourceDetails& dragSourceDetails) - : pos (dragSourceDetails.localPosition), - item (view.getItemAt (dragSourceDetails.localPosition.y)) - { - if (item != nullptr) - { - auto itemPos = item->getItemPosition (true); - insertIndex = item->getIndexInParent(); - auto oldY = pos.y; - pos.y = itemPos.getY(); - - if (item->getNumSubItems() == 0 || ! item->isOpen()) - { - if (files.size() > 0 ? item->isInterestedInFileDrag (files) - : item->isInterestedInDragSource (dragSourceDetails)) - { - // Check if we're trying to drag into an empty group item.. - if (oldY > itemPos.getY() + itemPos.getHeight() / 4 - && oldY < itemPos.getBottom() - itemPos.getHeight() / 4) - { - insertIndex = 0; - pos.x = itemPos.getX() + view.getIndentSize(); - pos.y = itemPos.getBottom(); - return; - } - } - } - - if (oldY > itemPos.getCentreY()) - { - pos.y += item->getItemHeight(); - - while (item->isLastOfSiblings() && item->getParentItem() != nullptr - && item->getParentItem()->getParentItem() != nullptr) - { - if (pos.x > itemPos.getX()) - break; - - item = item->getParentItem(); - itemPos = item->getItemPosition (true); - insertIndex = item->getIndexInParent(); - } - - ++insertIndex; - } - - pos.x = itemPos.getX(); - item = item->getParentItem(); - } - else if (auto* root = view.getRootItem()) - { - // If they're dragging beyond the bottom of the list, then insert at the end of the root item.. - item = root; - insertIndex = root->getNumSubItems(); - pos = root->getItemPosition (true).getBottomLeft(); - pos.x += view.getIndentSize(); - } - } - - Point pos; - TreeViewItem* item; - int insertIndex = 0; -}; - -//============================================================================== -class TreeView::InsertPointHighlight final : public Component -{ -public: - InsertPointHighlight() - { - setSize (100, 12); - setAlwaysOnTop (true); - setInterceptsMouseClicks (false, false); - } - - void setTargetPosition (const InsertPoint& insertPos, const int width) noexcept - { - lastItem = insertPos.item; - lastIndex = insertPos.insertIndex; - auto offset = getHeight() / 2; - setBounds (insertPos.pos.x - offset, insertPos.pos.y - offset, - width - (insertPos.pos.x - offset), getHeight()); - } - - void paint (Graphics& g) override - { - Path p; - auto h = (float) getHeight(); - p.addEllipse (2.0f, 2.0f, h - 4.0f, h - 4.0f); - p.startNewSubPath (h - 2.0f, h / 2.0f); - p.lineTo ((float) getWidth(), h / 2.0f); - - g.setColour (findColour (TreeView::dragAndDropIndicatorColourId, true)); - g.strokePath (p, PathStrokeType (2.0f)); - } - - TreeViewItem* lastItem = nullptr; - int lastIndex = 0; - -private: - JUCE_DECLARE_NON_COPYABLE (InsertPointHighlight) -}; - -//============================================================================== -class TreeView::TargetGroupHighlight final : public Component -{ -public: - TargetGroupHighlight() - { - setAlwaysOnTop (true); - setInterceptsMouseClicks (false, false); - } - - void setTargetPosition (TreeViewItem* const item) noexcept - { - setBounds (item->getItemPosition (true) - .withHeight (item->getItemHeight())); - } - - void paint (Graphics& g) override - { - g.setColour (findColour (TreeView::dragAndDropIndicatorColourId, true)); - g.drawRoundedRectangle (1.0f, 1.0f, (float) getWidth() - 2.0f, (float) getHeight() - 2.0f, 3.0f, 2.0f); - } - -private: - JUCE_DECLARE_NON_COPYABLE (TargetGroupHighlight) -}; - //============================================================================== void TreeView::showDragHighlight (const InsertPoint& insertPos) noexcept { diff --git a/modules/juce_gui_extra/juce_gui_extra.cpp b/modules/juce_gui_extra/juce_gui_extra.cpp index 5e8ca3bd2f..d40ebf780e 100644 --- a/modules/juce_gui_extra/juce_gui_extra.cpp +++ b/modules/juce_gui_extra/juce_gui_extra.cpp @@ -148,6 +148,16 @@ #include "misc/juce_PushNotifications.cpp" #include "misc/juce_RecentlyOpenedFilesList.cpp" #include "misc/juce_SplashScreen.cpp" + +#if JUCE_MAC + #include "native/juce_SystemTrayIcon_mac.cpp" +#elif JUCE_WINDOWS + #include "native/juce_ActiveXComponent_windows.cpp" + #include "native/juce_SystemTrayIcon_windows.cpp" +#elif JUCE_LINUX + #include "native/juce_SystemTrayIcon_linux.cpp" +#endif + #include "misc/juce_SystemTrayIconComponent.cpp" #include "misc/juce_LiveConstantEditor.cpp" #include "misc/juce_AnimatedAppComponent.cpp" @@ -161,7 +171,6 @@ #include "native/juce_NSViewFrameWatcher_mac.h" #include "native/juce_NSViewComponent_mac.mm" #include "native/juce_AppleRemote_mac.mm" - #include "native/juce_SystemTrayIcon_mac.cpp" #endif #if JUCE_IOS @@ -174,12 +183,10 @@ //============================================================================== #elif JUCE_WINDOWS - #include "native/juce_ActiveXComponent_windows.cpp" #include "native/juce_HWNDComponent_windows.cpp" #if JUCE_WEB_BROWSER #include "native/juce_WebBrowserComponent_windows.cpp" #endif - #include "native/juce_SystemTrayIcon_windows.cpp" //============================================================================== #elif JUCE_LINUX || JUCE_BSD @@ -198,8 +205,6 @@ JUCE_END_IGNORE_WARNINGS_GCC_LIKE - #include "native/juce_SystemTrayIcon_linux.cpp" - //============================================================================== #elif JUCE_ANDROID #include "native/juce_AndroidViewComponent.cpp"