mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
Fix C++23 compilation
This commit is contained in:
parent
b77249ad52
commit
5ce2fc388e
16 changed files with 817 additions and 760 deletions
|
|
@ -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<void (Result)>&& 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<void (Result)> 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<void (Result)>&& 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<void (Result)> completionCallback;
|
||||
};
|
||||
|
||||
static void restartProcess (const File& targetFolder)
|
||||
{
|
||||
#if JUCE_MAC || JUCE_LINUX || JUCE_BSD
|
||||
|
|
|
|||
|
|
@ -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 <GenericCodeEditorComponent>();
|
||||
}
|
||||
|
||||
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 <GenericCodeEditorComponent>();
|
||||
}
|
||||
|
||||
TextEditor editor;
|
||||
Label label { {}, "Find:" };
|
||||
ToggleButton caseButton { "Case-sensitive" };
|
||||
TextButton findPrev { "<" },
|
||||
findNext { ">" };
|
||||
};
|
||||
|
||||
void GenericCodeEditorComponent::resized()
|
||||
{
|
||||
CodeEditorComponent::resized();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<void()> 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<MidiDeviceInfo> 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<MidiDeviceListConnection::Key, std::function<void()>> callbacks;
|
||||
State lastNotifiedState;
|
||||
MidiDeviceListConnection::Key key = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -35,74 +35,6 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
class MidiDeviceListConnectionBroadcaster final : private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
~MidiDeviceListConnectionBroadcaster() override
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
}
|
||||
|
||||
MidiDeviceListConnection::Key add (std::function<void()> 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<MidiDeviceInfo> 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<MidiDeviceListConnection::Key, std::function<void()>> callbacks;
|
||||
State lastNotifiedState;
|
||||
MidiDeviceListConnection::Key key = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
MidiDeviceListConnection::~MidiDeviceListConnection() noexcept
|
||||
{
|
||||
if (broadcaster != nullptr)
|
||||
|
|
|
|||
|
|
@ -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<PluginDescription> 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<PluginDescription> 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());
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#include <CoreImage/CIRenderDestination.h>
|
||||
#include <CoreText/CTFont.h>
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<int> 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<Point<int>> 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<int> 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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue