migrating to the latest JUCE version
This commit is contained in:
@@ -1,326 +1,326 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../ProjectSaving/jucer_ProjectExporter.h"
|
||||
#include "../../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h"
|
||||
#include "../../Utility/Helpers/jucer_ValueWithDefaultWrapper.h"
|
||||
|
||||
#include "jucer_NewProjectWizard.h"
|
||||
|
||||
//==============================================================================
|
||||
class ItemHeader : public Component
|
||||
{
|
||||
public:
|
||||
ItemHeader (StringRef name, StringRef description, const char* iconSvgData)
|
||||
: nameLabel ({}, name),
|
||||
descriptionLabel ({}, description),
|
||||
icon (makeIcon (iconSvgData))
|
||||
{
|
||||
addAndMakeVisible (nameLabel);
|
||||
nameLabel.setFont (18.0f);
|
||||
nameLabel.setMinimumHorizontalScale (1.0f);
|
||||
|
||||
addAndMakeVisible (descriptionLabel);
|
||||
descriptionLabel.setMinimumHorizontalScale (1.0f);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
auto topSlice = bounds.removeFromTop (50);
|
||||
iconBounds = topSlice.removeFromRight (75);
|
||||
nameLabel.setBounds (topSlice);
|
||||
|
||||
bounds.removeFromTop (10);
|
||||
descriptionLabel.setBounds (bounds.removeFromTop (50));
|
||||
bounds.removeFromTop (20);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
|
||||
if (icon != nullptr)
|
||||
icon->drawWithin (g, iconBounds.toFloat(), RectanglePlacement::centred, 1.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
static std::unique_ptr<Drawable> makeIcon (const char* iconSvgData)
|
||||
{
|
||||
if (auto svg = XmlDocument::parse (iconSvgData))
|
||||
return Drawable::createFromSVG (*svg);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Label nameLabel, descriptionLabel;
|
||||
|
||||
Rectangle<int> iconBounds;
|
||||
std::unique_ptr<Drawable> icon;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemHeader)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TemplateComponent : public Component
|
||||
{
|
||||
public:
|
||||
TemplateComponent (const NewProjectTemplates::ProjectTemplate& temp,
|
||||
std::function<void (std::unique_ptr<Project>)>&& createdCallback)
|
||||
: projectTemplate (temp),
|
||||
projectCreatedCallback (std::move (createdCallback)),
|
||||
header (projectTemplate.displayName, projectTemplate.description, projectTemplate.icon)
|
||||
{
|
||||
createProjectButton.onClick = [this]
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Save Project", NewProjectWizard::getLastWizardFolder());
|
||||
auto browserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
chooser->launchAsync (browserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
auto dir = fc.getResult();
|
||||
|
||||
if (dir == File{})
|
||||
return;
|
||||
|
||||
SafePointer<TemplateComponent> safeThis { this };
|
||||
NewProjectWizard::createNewProject (projectTemplate,
|
||||
dir.getChildFile (projectNameValue.get().toString()),
|
||||
projectNameValue.get(),
|
||||
modulesValue.get(),
|
||||
exportersValue.get(),
|
||||
fileOptionsValue.get(),
|
||||
modulePathValue.getCurrentValue(),
|
||||
modulePathValue.getWrappedValueWithDefault().isUsingDefault(),
|
||||
[safeThis, dir] (std::unique_ptr<Project> project)
|
||||
{
|
||||
if (safeThis == nullptr)
|
||||
return;
|
||||
|
||||
safeThis->projectCreatedCallback (std::move (project));
|
||||
getAppSettings().lastWizardFolder = dir;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
addAndMakeVisible (createProjectButton);
|
||||
addAndMakeVisible (header);
|
||||
|
||||
modulePathValue.init ({ settingsTree, Ids::defaultJuceModulePath, nullptr },
|
||||
getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()),
|
||||
TargetOS::getThisOS());
|
||||
|
||||
panel.addProperties (buildPropertyList(), 2);
|
||||
addAndMakeVisible (panel);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (10);
|
||||
|
||||
header.setBounds (bounds.removeFromTop (150));
|
||||
createProjectButton.setBounds (bounds.removeFromBottom (30).removeFromRight (150));
|
||||
bounds.removeFromBottom (5);
|
||||
|
||||
panel.setBounds (bounds);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
private:
|
||||
NewProjectTemplates::ProjectTemplate projectTemplate;
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
std::function<void (std::unique_ptr<Project>)> projectCreatedCallback;
|
||||
|
||||
ItemHeader header;
|
||||
TextButton createProjectButton { "Create Project..." };
|
||||
|
||||
ValueTree settingsTree { "NewProjectSettings" };
|
||||
|
||||
ValueWithDefault projectNameValue { settingsTree, Ids::name, nullptr, "NewProject" },
|
||||
modulesValue { settingsTree, Ids::dependencies_, nullptr, projectTemplate.requiredModules, "," },
|
||||
exportersValue { settingsTree, Ids::exporters, nullptr, StringArray (ProjectExporter::getCurrentPlatformExporterTypeInfo().identifier.toString()), "," },
|
||||
fileOptionsValue { settingsTree, Ids::file, nullptr, NewProjectTemplates::getVarForFileOption (projectTemplate.defaultFileOption) };
|
||||
|
||||
ValueWithDefaultWrapper modulePathValue;
|
||||
|
||||
PropertyPanel panel;
|
||||
|
||||
//==============================================================================
|
||||
PropertyComponent* createProjectNamePropertyComponent()
|
||||
{
|
||||
return new TextPropertyComponent (projectNameValue, "Project Name", 1024, false);
|
||||
}
|
||||
|
||||
PropertyComponent* createModulesPropertyComponent()
|
||||
{
|
||||
Array<var> moduleVars;
|
||||
var requiredModules;
|
||||
|
||||
for (auto& m : getJUCEModules())
|
||||
{
|
||||
moduleVars.add (m);
|
||||
|
||||
if (projectTemplate.requiredModules.contains (m))
|
||||
requiredModules.append (m);
|
||||
}
|
||||
|
||||
modulesValue = requiredModules;
|
||||
|
||||
return new MultiChoicePropertyComponent (modulesValue, "Modules",
|
||||
getJUCEModules(), moduleVars);
|
||||
}
|
||||
|
||||
PropertyComponent* createModulePathPropertyComponent()
|
||||
{
|
||||
return new FilePathPropertyComponent (modulePathValue.getWrappedValueWithDefault(), "Path to Modules", true);
|
||||
}
|
||||
|
||||
PropertyComponent* createExportersPropertyValue()
|
||||
{
|
||||
Array<var> exporterVars;
|
||||
StringArray exporterNames;
|
||||
|
||||
for (auto& exporterTypeInfo : ProjectExporter::getExporterTypeInfos())
|
||||
{
|
||||
exporterVars.add (exporterTypeInfo.identifier.toString());
|
||||
exporterNames.add (exporterTypeInfo.displayName);
|
||||
}
|
||||
|
||||
return new MultiChoicePropertyComponent (exportersValue, "Exporters", exporterNames, exporterVars);
|
||||
}
|
||||
|
||||
PropertyComponent* createFileCreationOptionsPropertyComponent()
|
||||
{
|
||||
Array<var> optionVars;
|
||||
StringArray optionStrings;
|
||||
|
||||
for (auto& opt : projectTemplate.fileOptionsAndFiles)
|
||||
{
|
||||
optionVars.add (NewProjectTemplates::getVarForFileOption (opt.first));
|
||||
optionStrings.add (NewProjectTemplates::getStringForFileOption (opt.first));
|
||||
}
|
||||
|
||||
return new ChoicePropertyComponent (fileOptionsValue, "File Creation Options", optionStrings, optionVars);
|
||||
}
|
||||
|
||||
Array<PropertyComponent*> buildPropertyList()
|
||||
{
|
||||
PropertyListBuilder builder;
|
||||
|
||||
builder.add (createProjectNamePropertyComponent());
|
||||
builder.add (createModulesPropertyComponent());
|
||||
builder.add (createModulePathPropertyComponent());
|
||||
builder.add (createExportersPropertyValue());
|
||||
|
||||
if (! projectTemplate.fileOptionsAndFiles.empty())
|
||||
builder.add (createFileCreationOptionsPropertyComponent());
|
||||
|
||||
return builder.components;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemplateComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ExampleComponent : public Component
|
||||
{
|
||||
public:
|
||||
ExampleComponent (const File& f, std::function<void (const File&)> selectedCallback)
|
||||
: exampleFile (f),
|
||||
metadata (parseJUCEHeaderMetadata (exampleFile)),
|
||||
exampleSelectedCallback (std::move (selectedCallback)),
|
||||
header (metadata[Ids::name].toString(), metadata[Ids::description].toString(), BinaryData::background_logo_svg),
|
||||
codeViewer (doc, &cppTokeniser)
|
||||
{
|
||||
setTitle (exampleFile.getFileName());
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addAndMakeVisible (header);
|
||||
|
||||
openExampleButton.onClick = [this] { exampleSelectedCallback (exampleFile); };
|
||||
addAndMakeVisible (openExampleButton);
|
||||
|
||||
setupCodeViewer();
|
||||
addAndMakeVisible (codeViewer);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (10);
|
||||
|
||||
header.setBounds (bounds.removeFromTop (125));
|
||||
openExampleButton.setBounds (bounds.removeFromBottom (30).removeFromRight (150));
|
||||
codeViewer.setBounds (bounds);
|
||||
}
|
||||
|
||||
private:
|
||||
void setupCodeViewer()
|
||||
{
|
||||
auto fileString = exampleFile.loadFileAsString();
|
||||
|
||||
doc.replaceAllContent (fileString);
|
||||
|
||||
codeViewer.setScrollbarThickness (6);
|
||||
codeViewer.setReadOnly (true);
|
||||
codeViewer.setTitle ("Code");
|
||||
getAppSettings().appearance.applyToCodeEditor (codeViewer);
|
||||
|
||||
codeViewer.scrollToLine (findBestLineToScrollToForClass (StringArray::fromLines (fileString),
|
||||
metadata[Ids::name].toString(),
|
||||
metadata[Ids::type] == "AudioProcessor"));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File exampleFile;
|
||||
var metadata;
|
||||
|
||||
std::function<void (const File&)> exampleSelectedCallback;
|
||||
|
||||
ItemHeader header;
|
||||
|
||||
CPlusPlusCodeTokeniser cppTokeniser;
|
||||
CodeDocument doc;
|
||||
CodeEditorComponent codeViewer;
|
||||
|
||||
TextButton openExampleButton { "Open Example..." };
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExampleComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../ProjectSaving/jucer_ProjectExporter.h"
|
||||
#include "../../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h"
|
||||
#include "../../Utility/Helpers/jucer_ValueTreePropertyWithDefaultWrapper.h"
|
||||
|
||||
#include "jucer_NewProjectWizard.h"
|
||||
|
||||
//==============================================================================
|
||||
class ItemHeader : public Component
|
||||
{
|
||||
public:
|
||||
ItemHeader (StringRef name, StringRef description, const char* iconSvgData)
|
||||
: nameLabel ({}, name),
|
||||
descriptionLabel ({}, description),
|
||||
icon (makeIcon (iconSvgData))
|
||||
{
|
||||
addAndMakeVisible (nameLabel);
|
||||
nameLabel.setFont (18.0f);
|
||||
nameLabel.setMinimumHorizontalScale (1.0f);
|
||||
|
||||
addAndMakeVisible (descriptionLabel);
|
||||
descriptionLabel.setMinimumHorizontalScale (1.0f);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
auto topSlice = bounds.removeFromTop (50);
|
||||
iconBounds = topSlice.removeFromRight (75);
|
||||
nameLabel.setBounds (topSlice);
|
||||
|
||||
bounds.removeFromTop (10);
|
||||
descriptionLabel.setBounds (bounds.removeFromTop (50));
|
||||
bounds.removeFromTop (20);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
|
||||
if (icon != nullptr)
|
||||
icon->drawWithin (g, iconBounds.toFloat(), RectanglePlacement::centred, 1.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
static std::unique_ptr<Drawable> makeIcon (const char* iconSvgData)
|
||||
{
|
||||
if (auto svg = XmlDocument::parse (iconSvgData))
|
||||
return Drawable::createFromSVG (*svg);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Label nameLabel, descriptionLabel;
|
||||
|
||||
Rectangle<int> iconBounds;
|
||||
std::unique_ptr<Drawable> icon;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemHeader)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TemplateComponent : public Component
|
||||
{
|
||||
public:
|
||||
TemplateComponent (const NewProjectTemplates::ProjectTemplate& temp,
|
||||
std::function<void (std::unique_ptr<Project>)>&& createdCallback)
|
||||
: projectTemplate (temp),
|
||||
projectCreatedCallback (std::move (createdCallback)),
|
||||
header (projectTemplate.displayName, projectTemplate.description, projectTemplate.icon)
|
||||
{
|
||||
createProjectButton.onClick = [this]
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Save Project", NewProjectWizard::getLastWizardFolder());
|
||||
auto browserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
chooser->launchAsync (browserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
auto dir = fc.getResult();
|
||||
|
||||
if (dir == File{})
|
||||
return;
|
||||
|
||||
SafePointer<TemplateComponent> safeThis { this };
|
||||
NewProjectWizard::createNewProject (projectTemplate,
|
||||
dir.getChildFile (projectNameValue.get().toString()),
|
||||
projectNameValue.get(),
|
||||
modulesValue.get(),
|
||||
exportersValue.get(),
|
||||
fileOptionsValue.get(),
|
||||
modulePathValue.getCurrentValue(),
|
||||
modulePathValue.getWrappedValueTreePropertyWithDefault().isUsingDefault(),
|
||||
[safeThis, dir] (std::unique_ptr<Project> project)
|
||||
{
|
||||
if (safeThis == nullptr)
|
||||
return;
|
||||
|
||||
safeThis->projectCreatedCallback (std::move (project));
|
||||
getAppSettings().lastWizardFolder = dir;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
addAndMakeVisible (createProjectButton);
|
||||
addAndMakeVisible (header);
|
||||
|
||||
modulePathValue.init ({ settingsTree, Ids::defaultJuceModulePath, nullptr },
|
||||
getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()),
|
||||
TargetOS::getThisOS());
|
||||
|
||||
panel.addProperties (buildPropertyList(), 2);
|
||||
addAndMakeVisible (panel);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (10);
|
||||
|
||||
header.setBounds (bounds.removeFromTop (150));
|
||||
createProjectButton.setBounds (bounds.removeFromBottom (30).removeFromRight (150));
|
||||
bounds.removeFromBottom (5);
|
||||
|
||||
panel.setBounds (bounds);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
private:
|
||||
NewProjectTemplates::ProjectTemplate projectTemplate;
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
std::function<void (std::unique_ptr<Project>)> projectCreatedCallback;
|
||||
|
||||
ItemHeader header;
|
||||
TextButton createProjectButton { "Create Project..." };
|
||||
|
||||
ValueTree settingsTree { "NewProjectSettings" };
|
||||
|
||||
ValueTreePropertyWithDefault projectNameValue { settingsTree, Ids::name, nullptr, "NewProject" },
|
||||
modulesValue { settingsTree, Ids::dependencies_, nullptr, projectTemplate.requiredModules, "," },
|
||||
exportersValue { settingsTree, Ids::exporters, nullptr, StringArray (ProjectExporter::getCurrentPlatformExporterTypeInfo().identifier.toString()), "," },
|
||||
fileOptionsValue { settingsTree, Ids::file, nullptr, NewProjectTemplates::getVarForFileOption (projectTemplate.defaultFileOption) };
|
||||
|
||||
ValueTreePropertyWithDefaultWrapper modulePathValue;
|
||||
|
||||
PropertyPanel panel;
|
||||
|
||||
//==============================================================================
|
||||
PropertyComponent* createProjectNamePropertyComponent()
|
||||
{
|
||||
return new TextPropertyComponent (projectNameValue, "Project Name", 1024, false);
|
||||
}
|
||||
|
||||
PropertyComponent* createModulesPropertyComponent()
|
||||
{
|
||||
Array<var> moduleVars;
|
||||
var requiredModules;
|
||||
|
||||
for (auto& m : getJUCEModules())
|
||||
{
|
||||
moduleVars.add (m);
|
||||
|
||||
if (projectTemplate.requiredModules.contains (m))
|
||||
requiredModules.append (m);
|
||||
}
|
||||
|
||||
modulesValue = requiredModules;
|
||||
|
||||
return new MultiChoicePropertyComponent (modulesValue, "Modules",
|
||||
getJUCEModules(), moduleVars);
|
||||
}
|
||||
|
||||
PropertyComponent* createModulePathPropertyComponent()
|
||||
{
|
||||
return new FilePathPropertyComponent (modulePathValue.getWrappedValueTreePropertyWithDefault(), "Path to Modules", true);
|
||||
}
|
||||
|
||||
PropertyComponent* createExportersPropertyValue()
|
||||
{
|
||||
Array<var> exporterVars;
|
||||
StringArray exporterNames;
|
||||
|
||||
for (auto& exporterTypeInfo : ProjectExporter::getExporterTypeInfos())
|
||||
{
|
||||
exporterVars.add (exporterTypeInfo.identifier.toString());
|
||||
exporterNames.add (exporterTypeInfo.displayName);
|
||||
}
|
||||
|
||||
return new MultiChoicePropertyComponent (exportersValue, "Exporters", exporterNames, exporterVars);
|
||||
}
|
||||
|
||||
PropertyComponent* createFileCreationOptionsPropertyComponent()
|
||||
{
|
||||
Array<var> optionVars;
|
||||
StringArray optionStrings;
|
||||
|
||||
for (auto& opt : projectTemplate.fileOptionsAndFiles)
|
||||
{
|
||||
optionVars.add (NewProjectTemplates::getVarForFileOption (opt.first));
|
||||
optionStrings.add (NewProjectTemplates::getStringForFileOption (opt.first));
|
||||
}
|
||||
|
||||
return new ChoicePropertyComponent (fileOptionsValue, "File Creation Options", optionStrings, optionVars);
|
||||
}
|
||||
|
||||
Array<PropertyComponent*> buildPropertyList()
|
||||
{
|
||||
PropertyListBuilder builder;
|
||||
|
||||
builder.add (createProjectNamePropertyComponent());
|
||||
builder.add (createModulesPropertyComponent());
|
||||
builder.add (createModulePathPropertyComponent());
|
||||
builder.add (createExportersPropertyValue());
|
||||
|
||||
if (! projectTemplate.fileOptionsAndFiles.empty())
|
||||
builder.add (createFileCreationOptionsPropertyComponent());
|
||||
|
||||
return builder.components;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemplateComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ExampleComponent : public Component
|
||||
{
|
||||
public:
|
||||
ExampleComponent (const File& f, std::function<void (const File&)> selectedCallback)
|
||||
: exampleFile (f),
|
||||
metadata (parseJUCEHeaderMetadata (exampleFile)),
|
||||
exampleSelectedCallback (std::move (selectedCallback)),
|
||||
header (metadata[Ids::name].toString(), metadata[Ids::description].toString(), BinaryData::background_logo_svg),
|
||||
codeViewer (doc, &cppTokeniser)
|
||||
{
|
||||
setTitle (exampleFile.getFileName());
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addAndMakeVisible (header);
|
||||
|
||||
openExampleButton.onClick = [this] { exampleSelectedCallback (exampleFile); };
|
||||
addAndMakeVisible (openExampleButton);
|
||||
|
||||
setupCodeViewer();
|
||||
addAndMakeVisible (codeViewer);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (10);
|
||||
|
||||
header.setBounds (bounds.removeFromTop (125));
|
||||
openExampleButton.setBounds (bounds.removeFromBottom (30).removeFromRight (150));
|
||||
codeViewer.setBounds (bounds);
|
||||
}
|
||||
|
||||
private:
|
||||
void setupCodeViewer()
|
||||
{
|
||||
auto fileString = exampleFile.loadFileAsString();
|
||||
|
||||
doc.replaceAllContent (fileString);
|
||||
|
||||
codeViewer.setScrollbarThickness (6);
|
||||
codeViewer.setReadOnly (true);
|
||||
codeViewer.setTitle ("Code");
|
||||
getAppSettings().appearance.applyToCodeEditor (codeViewer);
|
||||
|
||||
codeViewer.scrollToLine (findBestLineToScrollToForClass (StringArray::fromLines (fileString),
|
||||
metadata[Ids::name].toString(),
|
||||
metadata[Ids::type] == "AudioProcessor"));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File exampleFile;
|
||||
var metadata;
|
||||
|
||||
std::function<void (const File&)> exampleSelectedCallback;
|
||||
|
||||
ItemHeader header;
|
||||
|
||||
CPlusPlusCodeTokeniser cppTokeniser;
|
||||
CodeDocument doc;
|
||||
CodeEditorComponent codeViewer;
|
||||
|
||||
TextButton openExampleButton { "Open Example..." };
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExampleComponent)
|
||||
};
|
||||
|
||||
@@ -1,251 +1,273 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/Helpers/jucer_MiscUtilities.h"
|
||||
|
||||
//==============================================================================
|
||||
namespace NewProjectTemplates
|
||||
{
|
||||
enum class ProjectCategory
|
||||
{
|
||||
application,
|
||||
plugin,
|
||||
library
|
||||
};
|
||||
|
||||
inline String getProjectCategoryString (ProjectCategory category)
|
||||
{
|
||||
if (category == ProjectCategory::application) return "Application";
|
||||
if (category == ProjectCategory::plugin) return "Plug-In";
|
||||
if (category == ProjectCategory::library) return "Library";
|
||||
|
||||
jassertfalse;
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
enum class FileCreationOptions
|
||||
{
|
||||
noFiles,
|
||||
main,
|
||||
header,
|
||||
headerAndCpp,
|
||||
processorAndEditor
|
||||
};
|
||||
|
||||
using FilenameAndContent = std::pair<String, String>;
|
||||
using OptionAndFilenameAndContent = std::pair<FileCreationOptions, std::vector<FilenameAndContent>>;
|
||||
using OptionsAndFiles = std::vector<OptionAndFilenameAndContent>;
|
||||
|
||||
struct ProjectTemplate
|
||||
{
|
||||
ProjectCategory category;
|
||||
String displayName, description, projectTypeString;
|
||||
|
||||
const char* icon;
|
||||
StringArray requiredModules;
|
||||
OptionsAndFiles fileOptionsAndFiles;
|
||||
FileCreationOptions defaultFileOption;
|
||||
|
||||
std::vector<FilenameAndContent> getFilesForOption (FileCreationOptions option) const
|
||||
{
|
||||
auto iter = std::find_if (fileOptionsAndFiles.begin(), fileOptionsAndFiles.end(),
|
||||
[option] (const OptionAndFilenameAndContent& opt) { return opt.first == option; });
|
||||
|
||||
if (iter != fileOptionsAndFiles.end())
|
||||
return iter->second;
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
inline bool isApplication (const ProjectTemplate& t) noexcept { return t.category == ProjectCategory::application; }
|
||||
inline bool isPlugin (const ProjectTemplate& t) noexcept { return t.category == ProjectCategory::plugin; }
|
||||
inline bool isLibrary (const ProjectTemplate& t) noexcept { return t.category == ProjectCategory::library; }
|
||||
|
||||
//==============================================================================
|
||||
inline var getVarForFileOption (FileCreationOptions opt)
|
||||
{
|
||||
if (opt == FileCreationOptions::noFiles) return "none";
|
||||
if (opt == FileCreationOptions::main) return "main";
|
||||
if (opt == FileCreationOptions::header) return "header";
|
||||
if (opt == FileCreationOptions::headerAndCpp) return "headercpp";
|
||||
if (opt == FileCreationOptions::processorAndEditor) return "processoreditor";
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
inline FileCreationOptions getFileOptionForVar (var opt)
|
||||
{
|
||||
if (opt == "none") return FileCreationOptions::noFiles;
|
||||
if (opt == "main") return FileCreationOptions::main;
|
||||
if (opt == "header") return FileCreationOptions::header;
|
||||
if (opt == "headercpp") return FileCreationOptions::headerAndCpp;
|
||||
if (opt == "processoreditor") return FileCreationOptions::processorAndEditor;
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
inline String getStringForFileOption (FileCreationOptions opt)
|
||||
{
|
||||
if (opt == FileCreationOptions::noFiles) return "No Files";
|
||||
if (opt == FileCreationOptions::main) return "Main.cpp";
|
||||
if (opt == FileCreationOptions::header) return "Main.cpp + .h";
|
||||
if (opt == FileCreationOptions::headerAndCpp) return "Main.cpp + .h/.cpp ";
|
||||
if (opt == FileCreationOptions::processorAndEditor) return "Processor and Editor";
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename... Strings>
|
||||
inline StringArray addAndReturn (StringArray arr, Strings... strings)
|
||||
{
|
||||
arr.addArray ({ strings... });
|
||||
return arr;
|
||||
}
|
||||
|
||||
inline std::vector<ProjectTemplate> getAllTemplates()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ ProjectCategory::application,
|
||||
"Blank", "Creates a blank JUCE GUI application.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_GUI_svg,
|
||||
getModulesRequiredForComponent(),
|
||||
{},
|
||||
FileCreationOptions::noFiles
|
||||
},
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"GUI", "Creates a blank JUCE GUI application with a single window component.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_GUI_svg,
|
||||
getModulesRequiredForComponent(),
|
||||
{
|
||||
{ FileCreationOptions::noFiles, {} },
|
||||
{ FileCreationOptions::main, { { "Main.cpp", "jucer_MainTemplate_NoWindow_cpp" } } },
|
||||
{ FileCreationOptions::header, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_ContentCompSimpleTemplate_h" } } },
|
||||
{ FileCreationOptions::headerAndCpp, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_ContentCompTemplate_h" },
|
||||
{ "MainComponent.cpp", "jucer_ContentCompTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::headerAndCpp },
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"Audio", "Creates a blank JUCE GUI application with a single window component and audio and MIDI in/out functions.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_AudioApp_svg,
|
||||
addAndReturn (getModulesRequiredForComponent(), "juce_audio_basics", "juce_audio_devices", "juce_audio_formats",
|
||||
"juce_audio_processors", "juce_audio_utils", "juce_gui_extra"),
|
||||
{
|
||||
{ FileCreationOptions::header, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AudioComponentSimpleTemplate_h" } } },
|
||||
{ FileCreationOptions::headerAndCpp, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AudioComponentTemplate_h" },
|
||||
{ "MainComponent.cpp", "jucer_AudioComponentTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::headerAndCpp },
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"Console", "Creates a command-line application without GUI support.",
|
||||
build_tools::ProjectType_ConsoleApp::getTypeName(),
|
||||
BinaryData::wizard_ConsoleApp_svg,
|
||||
getModulesRequiredForConsole(),
|
||||
{
|
||||
{ FileCreationOptions::noFiles, {} },
|
||||
{ FileCreationOptions::main, { { "Main.cpp", "jucer_MainConsoleAppTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::main },
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"Animated", "Creates a JUCE GUI application which draws an animated graphical display.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_AnimatedApp_svg,
|
||||
addAndReturn (getModulesRequiredForComponent(), "juce_gui_extra"),
|
||||
{
|
||||
{ FileCreationOptions::header, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AudioComponentSimpleTemplate_h" } } },
|
||||
{ FileCreationOptions::headerAndCpp, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AnimatedComponentTemplate_h" },
|
||||
{ "MainComponent.cpp", "jucer_AnimatedComponentTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::headerAndCpp },
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"OpenGL", "Creates a blank JUCE application with a single window component. "
|
||||
"This component supports openGL drawing features including 3D model import and GLSL shaders.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_OpenGL_svg,
|
||||
addAndReturn (getModulesRequiredForComponent(), "juce_gui_extra", "juce_opengl"),
|
||||
{
|
||||
{ FileCreationOptions::header, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AudioComponentSimpleTemplate_h" } } },
|
||||
{ FileCreationOptions::headerAndCpp, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_OpenGLComponentTemplate_h" },
|
||||
{ "MainComponent.cpp", "jucer_OpenGLComponentTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::headerAndCpp },
|
||||
|
||||
{ ProjectCategory::plugin,
|
||||
"Basic", "Creates an audio plug-in with a single window GUI and audio/MIDI IO functions.",
|
||||
build_tools::ProjectType_AudioPlugin::getTypeName(),
|
||||
BinaryData::wizard_AudioPlugin_svg,
|
||||
getModulesRequiredForAudioProcessor(),
|
||||
{
|
||||
{ FileCreationOptions::processorAndEditor, { { "PluginProcessor.cpp", "jucer_AudioPluginFilterTemplate_cpp" },
|
||||
{ "PluginProcessor.h", "jucer_AudioPluginFilterTemplate_h" },
|
||||
{ "PluginEditor.cpp", "jucer_AudioPluginEditorTemplate_cpp" },
|
||||
{ "PluginEditor.h", "jucer_AudioPluginEditorTemplate_h" } } }
|
||||
},
|
||||
FileCreationOptions::processorAndEditor
|
||||
},
|
||||
|
||||
{ ProjectCategory::library,
|
||||
"Static Library", "Creates a static library.",
|
||||
build_tools::ProjectType_StaticLibrary::getTypeName(),
|
||||
BinaryData::wizard_StaticLibrary_svg,
|
||||
getModulesRequiredForConsole(),
|
||||
{},
|
||||
FileCreationOptions::noFiles
|
||||
},
|
||||
|
||||
{ ProjectCategory::library,
|
||||
"Dynamic Library", "Creates a dynamic library.",
|
||||
build_tools::ProjectType_DLL::getTypeName(),
|
||||
BinaryData::wizard_DLL_svg,
|
||||
getModulesRequiredForConsole(),
|
||||
{},
|
||||
FileCreationOptions::noFiles
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/Helpers/jucer_MiscUtilities.h"
|
||||
|
||||
//==============================================================================
|
||||
namespace NewProjectTemplates
|
||||
{
|
||||
enum class ProjectCategory
|
||||
{
|
||||
application,
|
||||
plugin,
|
||||
library
|
||||
};
|
||||
|
||||
inline String getProjectCategoryString (ProjectCategory category)
|
||||
{
|
||||
if (category == ProjectCategory::application) return "Application";
|
||||
if (category == ProjectCategory::plugin) return "Plug-In";
|
||||
if (category == ProjectCategory::library) return "Library";
|
||||
|
||||
jassertfalse;
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
enum class FileCreationOptions
|
||||
{
|
||||
noFiles,
|
||||
main,
|
||||
header,
|
||||
headerAndCpp,
|
||||
processorAndEditor,
|
||||
araPluginFiles
|
||||
};
|
||||
|
||||
using FilenameAndContent = std::pair<String, String>;
|
||||
using OptionAndFilenameAndContent = std::pair<FileCreationOptions, std::vector<FilenameAndContent>>;
|
||||
using OptionsAndFiles = std::vector<OptionAndFilenameAndContent>;
|
||||
|
||||
struct ProjectTemplate
|
||||
{
|
||||
ProjectCategory category;
|
||||
String displayName, description, projectTypeString;
|
||||
|
||||
const char* icon;
|
||||
StringArray requiredModules;
|
||||
OptionsAndFiles fileOptionsAndFiles;
|
||||
FileCreationOptions defaultFileOption;
|
||||
|
||||
std::vector<FilenameAndContent> getFilesForOption (FileCreationOptions option) const
|
||||
{
|
||||
auto iter = std::find_if (fileOptionsAndFiles.begin(), fileOptionsAndFiles.end(),
|
||||
[option] (const OptionAndFilenameAndContent& opt) { return opt.first == option; });
|
||||
|
||||
if (iter != fileOptionsAndFiles.end())
|
||||
return iter->second;
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
inline bool isApplication (const ProjectTemplate& t) noexcept { return t.category == ProjectCategory::application; }
|
||||
inline bool isPlugin (const ProjectTemplate& t) noexcept { return t.category == ProjectCategory::plugin; }
|
||||
inline bool isLibrary (const ProjectTemplate& t) noexcept { return t.category == ProjectCategory::library; }
|
||||
|
||||
//==============================================================================
|
||||
inline var getVarForFileOption (FileCreationOptions opt)
|
||||
{
|
||||
if (opt == FileCreationOptions::noFiles) return "none";
|
||||
if (opt == FileCreationOptions::main) return "main";
|
||||
if (opt == FileCreationOptions::header) return "header";
|
||||
if (opt == FileCreationOptions::headerAndCpp) return "headercpp";
|
||||
if (opt == FileCreationOptions::processorAndEditor) return "processoreditor";
|
||||
if (opt == FileCreationOptions::araPluginFiles) return "arapluginfiles";
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
inline FileCreationOptions getFileOptionForVar (var opt)
|
||||
{
|
||||
if (opt == "none") return FileCreationOptions::noFiles;
|
||||
if (opt == "main") return FileCreationOptions::main;
|
||||
if (opt == "header") return FileCreationOptions::header;
|
||||
if (opt == "headercpp") return FileCreationOptions::headerAndCpp;
|
||||
if (opt == "processoreditor") return FileCreationOptions::processorAndEditor;
|
||||
if (opt == "arapluginfiles") return FileCreationOptions::araPluginFiles;
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
inline String getStringForFileOption (FileCreationOptions opt)
|
||||
{
|
||||
if (opt == FileCreationOptions::noFiles) return "No Files";
|
||||
if (opt == FileCreationOptions::main) return "Main.cpp";
|
||||
if (opt == FileCreationOptions::header) return "Main.cpp + .h";
|
||||
if (opt == FileCreationOptions::headerAndCpp) return "Main.cpp + .h/.cpp ";
|
||||
if (opt == FileCreationOptions::processorAndEditor) return "Processor and Editor";
|
||||
if (opt == FileCreationOptions::araPluginFiles) return "ARA Plugin Files";
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename... Strings>
|
||||
inline StringArray addAndReturn (StringArray arr, Strings... strings)
|
||||
{
|
||||
arr.addArray ({ strings... });
|
||||
return arr;
|
||||
}
|
||||
|
||||
inline std::vector<ProjectTemplate> getAllTemplates()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ ProjectCategory::application,
|
||||
"Blank", "Creates a blank JUCE GUI application.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_GUI_svg,
|
||||
getModulesRequiredForComponent(),
|
||||
{},
|
||||
FileCreationOptions::noFiles
|
||||
},
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"GUI", "Creates a blank JUCE GUI application with a single window component.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_GUI_svg,
|
||||
getModulesRequiredForComponent(),
|
||||
{
|
||||
{ FileCreationOptions::noFiles, {} },
|
||||
{ FileCreationOptions::main, { { "Main.cpp", "jucer_MainTemplate_NoWindow_cpp" } } },
|
||||
{ FileCreationOptions::header, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_ContentCompSimpleTemplate_h" } } },
|
||||
{ FileCreationOptions::headerAndCpp, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_ContentCompTemplate_h" },
|
||||
{ "MainComponent.cpp", "jucer_ContentCompTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::headerAndCpp },
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"Audio", "Creates a blank JUCE GUI application with a single window component and audio and MIDI in/out functions.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_AudioApp_svg,
|
||||
addAndReturn (getModulesRequiredForComponent(), "juce_audio_basics", "juce_audio_devices", "juce_audio_formats",
|
||||
"juce_audio_processors", "juce_audio_utils", "juce_gui_extra"),
|
||||
{
|
||||
{ FileCreationOptions::header, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AudioComponentSimpleTemplate_h" } } },
|
||||
{ FileCreationOptions::headerAndCpp, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AudioComponentTemplate_h" },
|
||||
{ "MainComponent.cpp", "jucer_AudioComponentTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::headerAndCpp },
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"Console", "Creates a command-line application without GUI support.",
|
||||
build_tools::ProjectType_ConsoleApp::getTypeName(),
|
||||
BinaryData::wizard_ConsoleApp_svg,
|
||||
getModulesRequiredForConsole(),
|
||||
{
|
||||
{ FileCreationOptions::noFiles, {} },
|
||||
{ FileCreationOptions::main, { { "Main.cpp", "jucer_MainConsoleAppTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::main },
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"Animated", "Creates a JUCE GUI application which draws an animated graphical display.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_AnimatedApp_svg,
|
||||
addAndReturn (getModulesRequiredForComponent(), "juce_gui_extra"),
|
||||
{
|
||||
{ FileCreationOptions::header, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AudioComponentSimpleTemplate_h" } } },
|
||||
{ FileCreationOptions::headerAndCpp, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AnimatedComponentTemplate_h" },
|
||||
{ "MainComponent.cpp", "jucer_AnimatedComponentTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::headerAndCpp },
|
||||
|
||||
{ ProjectCategory::application,
|
||||
"OpenGL", "Creates a blank JUCE application with a single window component. "
|
||||
"This component supports openGL drawing features including 3D model import and GLSL shaders.",
|
||||
build_tools::ProjectType_GUIApp::getTypeName(),
|
||||
BinaryData::wizard_OpenGL_svg,
|
||||
addAndReturn (getModulesRequiredForComponent(), "juce_gui_extra", "juce_opengl"),
|
||||
{
|
||||
{ FileCreationOptions::header, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_AudioComponentSimpleTemplate_h" } } },
|
||||
{ FileCreationOptions::headerAndCpp, { { "Main.cpp", "jucer_MainTemplate_Window_cpp" },
|
||||
{ "MainComponent.h", "jucer_OpenGLComponentTemplate_h" },
|
||||
{ "MainComponent.cpp", "jucer_OpenGLComponentTemplate_cpp" } } }
|
||||
},
|
||||
FileCreationOptions::headerAndCpp },
|
||||
|
||||
{ ProjectCategory::plugin,
|
||||
"Basic", "Creates an audio plug-in with a single window GUI and audio/MIDI IO functions.",
|
||||
build_tools::ProjectType_AudioPlugin::getTypeName(),
|
||||
BinaryData::wizard_AudioPlugin_svg,
|
||||
getModulesRequiredForAudioProcessor(),
|
||||
{
|
||||
{ FileCreationOptions::processorAndEditor, { { "PluginProcessor.cpp", "jucer_AudioPluginFilterTemplate_cpp" },
|
||||
{ "PluginProcessor.h", "jucer_AudioPluginFilterTemplate_h" },
|
||||
{ "PluginEditor.cpp", "jucer_AudioPluginEditorTemplate_cpp" },
|
||||
{ "PluginEditor.h", "jucer_AudioPluginEditorTemplate_h" } } }
|
||||
},
|
||||
FileCreationOptions::processorAndEditor
|
||||
},
|
||||
|
||||
{ ProjectCategory::plugin,
|
||||
"ARA", "Creates an ARA audio plug-in, augmenting the basic audio plug-in with ARA functionality.",
|
||||
build_tools::ProjectType_ARAAudioPlugin::getTypeName(),
|
||||
BinaryData::wizard_AudioPlugin_svg,
|
||||
getModulesRequiredForAudioProcessor(),
|
||||
{
|
||||
{ FileCreationOptions::araPluginFiles, { { "PluginProcessor.cpp", "jucer_AudioPluginFilterTemplate_cpp" },
|
||||
{ "PluginProcessor.h", "jucer_AudioPluginFilterTemplate_h" },
|
||||
{ "PluginEditor.cpp", "jucer_AudioPluginEditorTemplate_cpp" },
|
||||
{ "PluginEditor.h", "jucer_AudioPluginEditorTemplate_h" },
|
||||
{ "PluginARADocumentController.cpp", "jucer_AudioPluginARADocumentControllerTemplate_cpp" },
|
||||
{ "PluginARADocumentController.h", "jucer_AudioPluginARADocumentControllerTemplate_h" },
|
||||
{ "PluginARAPlaybackRenderer.cpp", "jucer_AudioPluginARAPlaybackRendererTemplate_cpp" },
|
||||
{ "PluginARAPlaybackRenderer.h", "jucer_AudioPluginARAPlaybackRendererTemplate_h" }} }
|
||||
},
|
||||
FileCreationOptions::araPluginFiles
|
||||
},
|
||||
|
||||
{ ProjectCategory::library,
|
||||
"Static Library", "Creates a static library.",
|
||||
build_tools::ProjectType_StaticLibrary::getTypeName(),
|
||||
BinaryData::wizard_StaticLibrary_svg,
|
||||
getModulesRequiredForConsole(),
|
||||
{},
|
||||
FileCreationOptions::noFiles
|
||||
},
|
||||
|
||||
{ ProjectCategory::library,
|
||||
"Dynamic Library", "Creates a dynamic library.",
|
||||
build_tools::ProjectType_DLL::getTypeName(),
|
||||
BinaryData::wizard_DLL_svg,
|
||||
getModulesRequiredForConsole(),
|
||||
{},
|
||||
FileCreationOptions::noFiles
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,310 +1,327 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../jucer_Application.h"
|
||||
#include "../../ProjectSaving/jucer_ProjectExporter.h"
|
||||
|
||||
#include "jucer_NewProjectWizard.h"
|
||||
|
||||
//==============================================================================
|
||||
static String getFileTemplate (const String& templateName)
|
||||
{
|
||||
int dataSize = 0;
|
||||
|
||||
if (auto* data = BinaryData::getNamedResource (templateName.toUTF8(), dataSize))
|
||||
return String::fromUTF8 (data, dataSize);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
static String getJuceHeaderInclude()
|
||||
{
|
||||
return CodeHelpers::createIncludePathIncludeStatement (Project::getJuceSourceHFilename());
|
||||
}
|
||||
|
||||
static String getContentComponentName()
|
||||
{
|
||||
return "MainComponent";
|
||||
}
|
||||
|
||||
using Opts = NewProjectTemplates::FileCreationOptions;
|
||||
|
||||
static bool shouldCreateHeaderFile (Opts opts) noexcept { return opts == Opts::header || opts == Opts::headerAndCpp; }
|
||||
static bool shouldCreateCppFile (Opts opts) noexcept { return opts == Opts::headerAndCpp; }
|
||||
|
||||
static void doBasicProjectSetup (Project& project, const NewProjectTemplates::ProjectTemplate& projectTemplate, const String& name)
|
||||
{
|
||||
project.setTitle (name);
|
||||
project.setProjectType (projectTemplate.projectTypeString);
|
||||
project.getMainGroup().addNewSubGroup ("Source", 0);
|
||||
|
||||
project.getConfigFlag ("JUCE_STRICT_REFCOUNTEDPOINTER") = true;
|
||||
project.getProjectValue (Ids::useAppConfig) = false;
|
||||
project.getProjectValue (Ids::addUsingNamespaceToJuceHeader) = false;
|
||||
|
||||
if (! ProjucerApplication::getApp().getLicenseController().getCurrentState().canUnlockFullFeatures())
|
||||
project.getProjectValue (Ids::displaySplashScreen) = true;
|
||||
|
||||
if (NewProjectTemplates::isPlugin (projectTemplate))
|
||||
project.getConfigFlag ("JUCE_VST3_CAN_REPLACE_VST2") = 0;
|
||||
}
|
||||
|
||||
static std::map<String, String> getSharedFileTokenReplacements()
|
||||
{
|
||||
return { { "%%app_headers%%", getJuceHeaderInclude() } };
|
||||
}
|
||||
|
||||
static std::map<String, String> getApplicationFileTokenReplacements (const String& name,
|
||||
NewProjectTemplates::FileCreationOptions fileOptions,
|
||||
const File& sourceFolder)
|
||||
{
|
||||
auto tokenReplacements = getSharedFileTokenReplacements();
|
||||
|
||||
tokenReplacements.insert ({ "%%app_class_name%%",
|
||||
build_tools::makeValidIdentifier (name + "Application", false, true, false) });
|
||||
tokenReplacements.insert ({ "%%content_component_class%%",
|
||||
getContentComponentName() });
|
||||
tokenReplacements.insert ({ "%%include_juce%%",
|
||||
getJuceHeaderInclude() });
|
||||
|
||||
if (shouldCreateHeaderFile (fileOptions))
|
||||
tokenReplacements["%%app_headers%%"] << newLine
|
||||
<< CodeHelpers::createIncludeStatement (sourceFolder.getChildFile ("MainComponent.h"),
|
||||
sourceFolder.getChildFile ("Main.cpp"));
|
||||
|
||||
if (shouldCreateCppFile (fileOptions))
|
||||
tokenReplacements.insert ({ "%%include_corresponding_header%%",
|
||||
CodeHelpers::createIncludeStatement (sourceFolder.getChildFile ("MainComponent.h"),
|
||||
sourceFolder.getChildFile ("MainComponent.cpp")) });
|
||||
|
||||
return tokenReplacements;
|
||||
}
|
||||
|
||||
static std::map<String, String> getPluginFileTokenReplacements (const String& name,
|
||||
const File& sourceFolder)
|
||||
{
|
||||
auto tokenReplacements = getSharedFileTokenReplacements();
|
||||
|
||||
auto processorCppFile = sourceFolder.getChildFile ("PluginProcessor.cpp");
|
||||
auto processorHFile = processorCppFile.withFileExtension (".h");
|
||||
auto editorCppFile = sourceFolder.getChildFile ("PluginEditor.cpp");
|
||||
auto editorHFile = editorCppFile.withFileExtension (".h");
|
||||
|
||||
auto processorHInclude = CodeHelpers::createIncludeStatement (processorHFile, processorCppFile);
|
||||
auto editorHInclude = CodeHelpers::createIncludeStatement (editorHFile, processorCppFile);
|
||||
|
||||
auto processorClassName = build_tools::makeValidIdentifier (name, false, true, false) + "AudioProcessor";
|
||||
processorClassName = processorClassName.substring (0, 1).toUpperCase() + processorClassName.substring (1);
|
||||
auto editorClassName = processorClassName + "Editor";
|
||||
|
||||
tokenReplacements.insert ({"%%filter_headers%%", processorHInclude + newLine + editorHInclude });
|
||||
tokenReplacements.insert ({"%%filter_class_name%%", processorClassName });
|
||||
tokenReplacements.insert ({"%%editor_class_name%%", editorClassName });
|
||||
tokenReplacements.insert ({"%%editor_cpp_headers%%", processorHInclude + newLine + editorHInclude });
|
||||
tokenReplacements.insert ({"%%editor_headers%%", getJuceHeaderInclude() + newLine + processorHInclude });
|
||||
|
||||
return tokenReplacements;
|
||||
}
|
||||
|
||||
static bool addFiles (Project& project, const NewProjectTemplates::ProjectTemplate& projectTemplate,
|
||||
const String& name, var fileOptionsVar, StringArray& failedFiles)
|
||||
{
|
||||
auto sourceFolder = project.getFile().getSiblingFile ("Source");
|
||||
|
||||
if (! sourceFolder.createDirectory())
|
||||
{
|
||||
failedFiles.add (sourceFolder.getFullPathName());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fileOptions = NewProjectTemplates::getFileOptionForVar (fileOptionsVar);
|
||||
|
||||
if (fileOptions == Opts::noFiles)
|
||||
return true;
|
||||
|
||||
auto tokenReplacements = [&]() -> std::map<String, String>
|
||||
{
|
||||
if (NewProjectTemplates::isApplication (projectTemplate))
|
||||
return getApplicationFileTokenReplacements (name, fileOptions, sourceFolder);
|
||||
|
||||
if (NewProjectTemplates::isPlugin (projectTemplate))
|
||||
return getPluginFileTokenReplacements (name, sourceFolder);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}();
|
||||
|
||||
auto sourceGroup = project.getMainGroup().getOrCreateSubGroup ("Source");
|
||||
|
||||
for (auto& files : projectTemplate.getFilesForOption (fileOptions))
|
||||
{
|
||||
auto file = sourceFolder.getChildFile (files.first);
|
||||
auto fileContent = getFileTemplate (files.second);
|
||||
|
||||
for (auto& tokenReplacement : tokenReplacements)
|
||||
fileContent = fileContent.replace (tokenReplacement.first, tokenReplacement.second, false);
|
||||
|
||||
if (! build_tools::overwriteFileWithNewDataIfDifferent (file, fileContent))
|
||||
{
|
||||
failedFiles.add (file.getFullPathName());
|
||||
return false;
|
||||
}
|
||||
|
||||
sourceGroup.addFileAtIndex (file, -1, (file.hasFileExtension (sourceFileExtensions)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void addModules (Project& project, Array<var> modules, const String& modulePath, bool useGlobalPath)
|
||||
{
|
||||
AvailableModulesList list;
|
||||
list.scanPaths ({ modulePath });
|
||||
|
||||
auto& projectModules = project.getEnabledModules();
|
||||
|
||||
for (auto& mod : list.getAllModules())
|
||||
if (modules.contains (mod.first))
|
||||
projectModules.addModule (mod.second, false, useGlobalPath);
|
||||
|
||||
for (auto& mod : projectModules.getModulesWithMissingDependencies())
|
||||
projectModules.tryToFixMissingDependencies (mod);
|
||||
}
|
||||
|
||||
static void addExporters (Project& project, Array<var> exporters)
|
||||
{
|
||||
for (auto exporter : exporters)
|
||||
project.addNewExporter (exporter.toString());
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
|
||||
config->getValue (Ids::targetName) = project.getProjectFilenameRootString();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File NewProjectWizard::getLastWizardFolder()
|
||||
{
|
||||
if (getAppSettings().lastWizardFolder.isDirectory())
|
||||
return getAppSettings().lastWizardFolder;
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
static File lastFolderFallback (File::getSpecialLocation (File::userDocumentsDirectory));
|
||||
#else
|
||||
static File lastFolderFallback (File::getSpecialLocation (File::userHomeDirectory));
|
||||
#endif
|
||||
|
||||
return lastFolderFallback;
|
||||
}
|
||||
|
||||
static void displayFailedFilesMessage (const StringArray& failedFiles)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
TRANS("Errors in Creating Project!"),
|
||||
TRANS("The following files couldn't be written:")
|
||||
+ "\n\n"
|
||||
+ failedFiles.joinIntoString ("\n", 0, 10));
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
static void prepareDirectory (const File& targetFolder, Callback&& callback)
|
||||
{
|
||||
StringArray failedFiles;
|
||||
|
||||
if (! targetFolder.exists())
|
||||
{
|
||||
if (! targetFolder.createDirectory())
|
||||
{
|
||||
displayFailedFilesMessage ({ targetFolder.getFullPathName() });
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (FileHelpers::containsAnyNonHiddenFiles (targetFolder))
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::InfoIcon,
|
||||
TRANS("New JUCE Project"),
|
||||
TRANS("You chose the folder:\n\nXFLDRX\n\n").replace ("XFLDRX", targetFolder.getFullPathName())
|
||||
+ TRANS("This folder isn't empty - are you sure you want to create the project there?")
|
||||
+ "\n\n"
|
||||
+ TRANS("Any existing files with the same names may be overwritten by the new files."),
|
||||
{},
|
||||
{},
|
||||
nullptr,
|
||||
ModalCallbackFunction::create ([callback] (int result)
|
||||
{
|
||||
if (result != 0)
|
||||
callback();
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
void NewProjectWizard::createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate,
|
||||
const File& targetFolder, const String& name, var modules, var exporters, var fileOptions,
|
||||
const String& modulePath, bool useGlobalModulePath,
|
||||
std::function<void (std::unique_ptr<Project>)> callback)
|
||||
{
|
||||
prepareDirectory (targetFolder, [=]
|
||||
{
|
||||
auto project = std::make_unique<Project> (targetFolder.getChildFile (File::createLegalFileName (name))
|
||||
.withFileExtension (Project::projectFileExtension));
|
||||
|
||||
doBasicProjectSetup (*project, projectTemplate, name);
|
||||
|
||||
StringArray failedFiles;
|
||||
|
||||
if (addFiles (*project, projectTemplate, name, fileOptions, failedFiles))
|
||||
{
|
||||
addExporters (*project, *exporters.getArray());
|
||||
addModules (*project, *modules.getArray(), modulePath, useGlobalModulePath);
|
||||
|
||||
auto sharedProject = std::make_shared<std::unique_ptr<Project>> (std::move (project));
|
||||
(*sharedProject)->saveAsync (false, true, [sharedProject, failedFiles, callback] (FileBasedDocument::SaveResult r)
|
||||
{
|
||||
auto uniqueProject = std::move (*sharedProject.get());
|
||||
|
||||
if (r == FileBasedDocument::savedOk)
|
||||
{
|
||||
uniqueProject->setChangedFlag (false);
|
||||
uniqueProject->loadFrom (uniqueProject->getFile(), true);
|
||||
callback (std::move (uniqueProject));
|
||||
return;
|
||||
}
|
||||
|
||||
auto failedFilesCopy = failedFiles;
|
||||
failedFilesCopy.add (uniqueProject->getFile().getFullPathName());
|
||||
displayFailedFilesMessage (failedFilesCopy);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
displayFailedFilesMessage (failedFiles);
|
||||
});
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../jucer_Application.h"
|
||||
#include "../../ProjectSaving/jucer_ProjectExporter.h"
|
||||
|
||||
#include "jucer_NewProjectWizard.h"
|
||||
|
||||
//==============================================================================
|
||||
static String getFileTemplate (const String& templateName)
|
||||
{
|
||||
int dataSize = 0;
|
||||
|
||||
if (auto* data = BinaryData::getNamedResource (templateName.toUTF8(), dataSize))
|
||||
return String::fromUTF8 (data, dataSize);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
static String getJuceHeaderInclude()
|
||||
{
|
||||
return CodeHelpers::createIncludePathIncludeStatement (Project::getJuceSourceHFilename());
|
||||
}
|
||||
|
||||
static String getContentComponentName()
|
||||
{
|
||||
return "MainComponent";
|
||||
}
|
||||
|
||||
using Opts = NewProjectTemplates::FileCreationOptions;
|
||||
|
||||
static bool shouldCreateHeaderFile (Opts opts) noexcept { return opts == Opts::header || opts == Opts::headerAndCpp; }
|
||||
static bool shouldCreateCppFile (Opts opts) noexcept { return opts == Opts::headerAndCpp; }
|
||||
|
||||
static void doBasicProjectSetup (Project& project, const NewProjectTemplates::ProjectTemplate& projectTemplate, const String& name)
|
||||
{
|
||||
project.setTitle (name);
|
||||
project.setProjectType (projectTemplate.projectTypeString);
|
||||
project.getMainGroup().addNewSubGroup ("Source", 0);
|
||||
|
||||
project.getConfigFlag ("JUCE_STRICT_REFCOUNTEDPOINTER") = true;
|
||||
project.getProjectValue (Ids::useAppConfig) = false;
|
||||
project.getProjectValue (Ids::addUsingNamespaceToJuceHeader) = false;
|
||||
|
||||
if (! ProjucerApplication::getApp().getLicenseController().getCurrentState().canUnlockFullFeatures())
|
||||
project.getProjectValue (Ids::displaySplashScreen) = true;
|
||||
|
||||
if (NewProjectTemplates::isPlugin (projectTemplate))
|
||||
project.getConfigFlag ("JUCE_VST3_CAN_REPLACE_VST2") = 0;
|
||||
}
|
||||
|
||||
static std::map<String, String> getSharedFileTokenReplacements()
|
||||
{
|
||||
return { { "%%app_headers%%", getJuceHeaderInclude() } };
|
||||
}
|
||||
|
||||
static std::map<String, String> getApplicationFileTokenReplacements (const String& name,
|
||||
NewProjectTemplates::FileCreationOptions fileOptions,
|
||||
const File& sourceFolder)
|
||||
{
|
||||
auto tokenReplacements = getSharedFileTokenReplacements();
|
||||
|
||||
tokenReplacements.insert ({ "%%app_class_name%%",
|
||||
build_tools::makeValidIdentifier (name + "Application", false, true, false) });
|
||||
tokenReplacements.insert ({ "%%content_component_class%%",
|
||||
getContentComponentName() });
|
||||
tokenReplacements.insert ({ "%%include_juce%%",
|
||||
getJuceHeaderInclude() });
|
||||
|
||||
if (shouldCreateHeaderFile (fileOptions))
|
||||
tokenReplacements["%%app_headers%%"] << newLine
|
||||
<< CodeHelpers::createIncludeStatement (sourceFolder.getChildFile ("MainComponent.h"),
|
||||
sourceFolder.getChildFile ("Main.cpp"));
|
||||
|
||||
if (shouldCreateCppFile (fileOptions))
|
||||
tokenReplacements.insert ({ "%%include_corresponding_header%%",
|
||||
CodeHelpers::createIncludeStatement (sourceFolder.getChildFile ("MainComponent.h"),
|
||||
sourceFolder.getChildFile ("MainComponent.cpp")) });
|
||||
|
||||
return tokenReplacements;
|
||||
}
|
||||
|
||||
static std::map<String, String> getPluginFileTokenReplacements (const String& name,
|
||||
const File& sourceFolder)
|
||||
{
|
||||
auto tokenReplacements = getSharedFileTokenReplacements();
|
||||
|
||||
auto processorCppFile = sourceFolder.getChildFile ("PluginProcessor.cpp");
|
||||
auto processorHFile = processorCppFile.withFileExtension (".h");
|
||||
auto editorCppFile = sourceFolder.getChildFile ("PluginEditor.cpp");
|
||||
auto editorHFile = editorCppFile.withFileExtension (".h");
|
||||
|
||||
auto processorHInclude = CodeHelpers::createIncludeStatement (processorHFile, processorCppFile);
|
||||
auto editorHInclude = CodeHelpers::createIncludeStatement (editorHFile, processorCppFile);
|
||||
|
||||
auto processorClassName = build_tools::makeValidIdentifier (name, false, true, false) + "AudioProcessor";
|
||||
processorClassName = processorClassName.substring (0, 1).toUpperCase() + processorClassName.substring (1);
|
||||
auto editorClassName = processorClassName + "Editor";
|
||||
|
||||
const auto araDocumentControllerCppFile = sourceFolder.getChildFile ("PluginARADocumentController.cpp");
|
||||
const auto araDocumentControllerHFile = araDocumentControllerCppFile.withFileExtension (".h");
|
||||
const auto araPlaybackRendererCppFile = sourceFolder.getChildFile ("PluginARAPlaybackRenderer.cpp");
|
||||
const auto araPlaybackRendererHFile = araPlaybackRendererCppFile.withFileExtension (".h");
|
||||
|
||||
const auto araDocumentControllerHInclude = CodeHelpers::createIncludeStatement (araDocumentControllerHFile, araDocumentControllerCppFile);
|
||||
const auto araPlaybackRendererHInclude = CodeHelpers::createIncludeStatement (araPlaybackRendererHFile, araPlaybackRendererCppFile);
|
||||
|
||||
auto araDocumentControllerClassName = build_tools::makeValidIdentifier (name, true, true, false) + "DocumentController";
|
||||
araDocumentControllerClassName = araDocumentControllerClassName.substring (0, 1).toUpperCase() + araDocumentControllerClassName.substring (1);
|
||||
auto araPlaybackRendererClassName = build_tools::makeValidIdentifier (name, true, true, false) + "PlaybackRenderer";
|
||||
araPlaybackRendererClassName = araPlaybackRendererClassName.substring (0, 1).toUpperCase() + araPlaybackRendererClassName.substring (1);
|
||||
|
||||
tokenReplacements.insert ({"%%filter_headers%%", processorHInclude + newLine + editorHInclude });
|
||||
tokenReplacements.insert ({"%%filter_class_name%%", processorClassName });
|
||||
tokenReplacements.insert ({"%%editor_class_name%%", editorClassName });
|
||||
tokenReplacements.insert ({"%%editor_cpp_headers%%", processorHInclude + newLine + editorHInclude });
|
||||
tokenReplacements.insert ({"%%editor_headers%%", getJuceHeaderInclude() + newLine + processorHInclude });
|
||||
tokenReplacements.insert ({"%%aradocumentcontroller_headers%%", araDocumentControllerHInclude });
|
||||
tokenReplacements.insert ({"%%aradocumentcontroller_class_name%%", araDocumentControllerClassName });
|
||||
tokenReplacements.insert ({"%%araplaybackrenderer_headers%%", araPlaybackRendererHInclude });
|
||||
tokenReplacements.insert ({"%%araplaybackrenderer_class_name%%", araPlaybackRendererClassName });
|
||||
|
||||
return tokenReplacements;
|
||||
}
|
||||
|
||||
static bool addFiles (Project& project, const NewProjectTemplates::ProjectTemplate& projectTemplate,
|
||||
const String& name, var fileOptionsVar, StringArray& failedFiles)
|
||||
{
|
||||
auto sourceFolder = project.getFile().getSiblingFile ("Source");
|
||||
|
||||
if (! sourceFolder.createDirectory())
|
||||
{
|
||||
failedFiles.add (sourceFolder.getFullPathName());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fileOptions = NewProjectTemplates::getFileOptionForVar (fileOptionsVar);
|
||||
|
||||
if (fileOptions == Opts::noFiles)
|
||||
return true;
|
||||
|
||||
auto tokenReplacements = [&]() -> std::map<String, String>
|
||||
{
|
||||
if (NewProjectTemplates::isApplication (projectTemplate))
|
||||
return getApplicationFileTokenReplacements (name, fileOptions, sourceFolder);
|
||||
|
||||
if (NewProjectTemplates::isPlugin (projectTemplate))
|
||||
return getPluginFileTokenReplacements (name, sourceFolder);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}();
|
||||
|
||||
auto sourceGroup = project.getMainGroup().getOrCreateSubGroup ("Source");
|
||||
|
||||
for (auto& files : projectTemplate.getFilesForOption (fileOptions))
|
||||
{
|
||||
auto file = sourceFolder.getChildFile (files.first);
|
||||
auto fileContent = getFileTemplate (files.second);
|
||||
|
||||
for (auto& tokenReplacement : tokenReplacements)
|
||||
fileContent = fileContent.replace (tokenReplacement.first, tokenReplacement.second, false);
|
||||
|
||||
if (! build_tools::overwriteFileWithNewDataIfDifferent (file, fileContent))
|
||||
{
|
||||
failedFiles.add (file.getFullPathName());
|
||||
return false;
|
||||
}
|
||||
|
||||
sourceGroup.addFileAtIndex (file, -1, (file.hasFileExtension (sourceFileExtensions)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void addModules (Project& project, Array<var> modules, const String& modulePath, bool useGlobalPath)
|
||||
{
|
||||
AvailableModulesList list;
|
||||
list.scanPaths ({ modulePath });
|
||||
|
||||
auto& projectModules = project.getEnabledModules();
|
||||
|
||||
for (auto& mod : list.getAllModules())
|
||||
if (modules.contains (mod.first))
|
||||
projectModules.addModule (mod.second, false, useGlobalPath);
|
||||
|
||||
for (auto& mod : projectModules.getModulesWithMissingDependencies())
|
||||
projectModules.tryToFixMissingDependencies (mod);
|
||||
}
|
||||
|
||||
static void addExporters (Project& project, Array<var> exporters)
|
||||
{
|
||||
for (auto exporter : exporters)
|
||||
project.addNewExporter (exporter.toString());
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
|
||||
config->getValue (Ids::targetName) = project.getProjectFilenameRootString();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File NewProjectWizard::getLastWizardFolder()
|
||||
{
|
||||
if (getAppSettings().lastWizardFolder.isDirectory())
|
||||
return getAppSettings().lastWizardFolder;
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
static File lastFolderFallback (File::getSpecialLocation (File::userDocumentsDirectory));
|
||||
#else
|
||||
static File lastFolderFallback (File::getSpecialLocation (File::userHomeDirectory));
|
||||
#endif
|
||||
|
||||
return lastFolderFallback;
|
||||
}
|
||||
|
||||
static void displayFailedFilesMessage (const StringArray& failedFiles)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
TRANS("Errors in Creating Project!"),
|
||||
TRANS("The following files couldn't be written:")
|
||||
+ "\n\n"
|
||||
+ failedFiles.joinIntoString ("\n", 0, 10));
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
static void prepareDirectory (const File& targetFolder, Callback&& callback)
|
||||
{
|
||||
StringArray failedFiles;
|
||||
|
||||
if (! targetFolder.exists())
|
||||
{
|
||||
if (! targetFolder.createDirectory())
|
||||
{
|
||||
displayFailedFilesMessage ({ targetFolder.getFullPathName() });
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (FileHelpers::containsAnyNonHiddenFiles (targetFolder))
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::InfoIcon,
|
||||
TRANS("New JUCE Project"),
|
||||
TRANS("You chose the folder:\n\nXFLDRX\n\n").replace ("XFLDRX", targetFolder.getFullPathName())
|
||||
+ TRANS("This folder isn't empty - are you sure you want to create the project there?")
|
||||
+ "\n\n"
|
||||
+ TRANS("Any existing files with the same names may be overwritten by the new files."),
|
||||
{},
|
||||
{},
|
||||
nullptr,
|
||||
ModalCallbackFunction::create ([callback] (int result)
|
||||
{
|
||||
if (result != 0)
|
||||
callback();
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
void NewProjectWizard::createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate,
|
||||
const File& targetFolder, const String& name, var modules, var exporters, var fileOptions,
|
||||
const String& modulePath, bool useGlobalModulePath,
|
||||
std::function<void (std::unique_ptr<Project>)> callback)
|
||||
{
|
||||
prepareDirectory (targetFolder, [=]
|
||||
{
|
||||
auto project = std::make_unique<Project> (targetFolder.getChildFile (File::createLegalFileName (name))
|
||||
.withFileExtension (Project::projectFileExtension));
|
||||
|
||||
doBasicProjectSetup (*project, projectTemplate, name);
|
||||
|
||||
StringArray failedFiles;
|
||||
|
||||
if (addFiles (*project, projectTemplate, name, fileOptions, failedFiles))
|
||||
{
|
||||
addExporters (*project, *exporters.getArray());
|
||||
addModules (*project, *modules.getArray(), modulePath, useGlobalModulePath);
|
||||
|
||||
auto sharedProject = std::make_shared<std::unique_ptr<Project>> (std::move (project));
|
||||
(*sharedProject)->saveAsync (false, true, [sharedProject, failedFiles, callback] (FileBasedDocument::SaveResult r)
|
||||
{
|
||||
auto uniqueProject = std::move (*sharedProject.get());
|
||||
|
||||
if (r == FileBasedDocument::savedOk)
|
||||
{
|
||||
uniqueProject->setChangedFlag (false);
|
||||
uniqueProject->loadFrom (uniqueProject->getFile(), true);
|
||||
callback (std::move (uniqueProject));
|
||||
return;
|
||||
}
|
||||
|
||||
auto failedFilesCopy = failedFiles;
|
||||
failedFilesCopy.add (uniqueProject->getFile().getFullPathName());
|
||||
displayFailedFilesMessage (failedFilesCopy);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
displayFailedFilesMessage (failedFiles);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_NewProjectTemplates.h"
|
||||
|
||||
//==============================================================================
|
||||
namespace NewProjectWizard
|
||||
{
|
||||
File getLastWizardFolder();
|
||||
|
||||
void createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate,
|
||||
const File& targetFolder, const String& name, var modules, var exporters, var fileOptions,
|
||||
const String& modulePath, bool useGlobalModulePath,
|
||||
std::function<void (std::unique_ptr<Project>)> callback);
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_NewProjectTemplates.h"
|
||||
|
||||
//==============================================================================
|
||||
namespace NewProjectWizard
|
||||
{
|
||||
File getLastWizardFolder();
|
||||
|
||||
void createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate,
|
||||
const File& targetFolder, const String& name, var modules, var exporters, var fileOptions,
|
||||
const String& modulePath, bool useGlobalModulePath,
|
||||
std::function<void (std::unique_ptr<Project>)> callback);
|
||||
}
|
||||
|
||||
@@ -1,288 +1,288 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../jucer_Application.h"
|
||||
|
||||
#include "jucer_StartPageComponent.h"
|
||||
#include "jucer_StartPageTreeHolder.h"
|
||||
#include "jucer_NewProjectTemplates.h"
|
||||
#include "jucer_ContentComponents.h"
|
||||
|
||||
//==============================================================================
|
||||
struct ContentComponent : public Component
|
||||
{
|
||||
ContentComponent()
|
||||
{
|
||||
setTitle ("Content");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
if (content != nullptr)
|
||||
content->setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
void setContent (std::unique_ptr<Component>&& newContent)
|
||||
{
|
||||
if (content.get() != newContent.get())
|
||||
{
|
||||
content = std::move (newContent);
|
||||
addAndMakeVisible (content.get());
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Component> content;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (ContentComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static File findExampleFile (int dirIndex, int index)
|
||||
{
|
||||
auto dir = ProjucerApplication::getSortedExampleDirectories()[dirIndex];
|
||||
return ProjucerApplication::getSortedExampleFilesInDirectory (dir)[index];
|
||||
}
|
||||
|
||||
static std::unique_ptr<Component> createExampleProjectsTab (ContentComponent& content, std::function<void (const File&)> cb)
|
||||
{
|
||||
StringArray exampleCategories;
|
||||
std::vector<StringArray> examples;
|
||||
|
||||
for (auto& dir : ProjucerApplication::getSortedExampleDirectories())
|
||||
{
|
||||
exampleCategories.add (dir.getFileName());
|
||||
|
||||
StringArray ex;
|
||||
for (auto& f : ProjucerApplication::getSortedExampleFilesInDirectory (dir))
|
||||
ex.add (f.getFileNameWithoutExtension());
|
||||
|
||||
examples.push_back (ex);
|
||||
}
|
||||
|
||||
if (exampleCategories.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
auto selectedCallback = [&, cb] (int category, int index) mutable
|
||||
{
|
||||
content.setContent (std::make_unique<ExampleComponent> (findExampleFile (category, index), cb));
|
||||
};
|
||||
|
||||
return std::make_unique<StartPageTreeHolder> ("Examples",
|
||||
exampleCategories,
|
||||
examples,
|
||||
std::move (selectedCallback),
|
||||
StartPageTreeHolder::Open::no);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static StringArray getAllTemplateCategoryStrings()
|
||||
{
|
||||
StringArray categories;
|
||||
|
||||
for (auto& t : NewProjectTemplates::getAllTemplates())
|
||||
categories.addIfNotAlreadyThere (NewProjectTemplates::getProjectCategoryString (t.category));
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
static std::vector<NewProjectTemplates::ProjectTemplate> getTemplatesInCategory (const String& category)
|
||||
{
|
||||
std::vector<NewProjectTemplates::ProjectTemplate> templates;
|
||||
|
||||
for (auto& t : NewProjectTemplates::getAllTemplates())
|
||||
if (NewProjectTemplates::getProjectCategoryString (t.category) == category)
|
||||
templates.push_back (t);
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
static StringArray getAllTemplateNamesForCategory (const String& category)
|
||||
{
|
||||
StringArray types;
|
||||
|
||||
for (auto& t : getTemplatesInCategory (category))
|
||||
types.add (t.displayName);
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Component> createProjectTemplatesTab (ContentComponent& content,
|
||||
std::function<void (std::unique_ptr<Project>&&)>&& cb)
|
||||
{
|
||||
auto categories = getAllTemplateCategoryStrings();
|
||||
|
||||
std::vector<StringArray> templateNames;
|
||||
|
||||
for (auto& c : categories)
|
||||
templateNames.push_back (getAllTemplateNamesForCategory (c));
|
||||
|
||||
auto selectedCallback = [&, cb] (int category, int index)
|
||||
{
|
||||
auto categoryString = getAllTemplateCategoryStrings()[category];
|
||||
auto templates = getTemplatesInCategory (categoryString);
|
||||
|
||||
content.setContent (std::make_unique<TemplateComponent> (templates[(size_t) index], std::move (cb)));
|
||||
};
|
||||
|
||||
auto holder = std::make_unique<StartPageTreeHolder> ("Templates",
|
||||
categories,
|
||||
templateNames,
|
||||
std::move (selectedCallback),
|
||||
StartPageTreeHolder::Open::yes);
|
||||
holder->setSelectedItem (categories[0], 1);
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
|
||||
return std::move (holder);
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ProjectTemplatesAndExamples : public TabbedComponent
|
||||
{
|
||||
ProjectTemplatesAndExamples (ContentComponent& c,
|
||||
std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
|
||||
std::function<void (const File&)>&& exampleCb)
|
||||
: TabbedComponent (TabbedButtonBar::Orientation::TabsAtTop),
|
||||
content (c),
|
||||
exampleSelectedCallback (std::move (exampleCb))
|
||||
{
|
||||
setTitle ("Templates and Examples");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addTab ("New Project",
|
||||
Colours::transparentBlack,
|
||||
createProjectTemplatesTab (content, std::move (newProjectCb)).release(),
|
||||
true);
|
||||
|
||||
refreshExamplesTab();
|
||||
}
|
||||
|
||||
void refreshExamplesTab()
|
||||
{
|
||||
auto wasOpen = (getCurrentTabIndex() == 1);
|
||||
|
||||
removeTab (1);
|
||||
|
||||
auto exampleTabs = createExampleProjectsTab (content, exampleSelectedCallback);
|
||||
|
||||
addTab ("Open Example",
|
||||
Colours::transparentBlack,
|
||||
exampleTabs == nullptr ? new SetJUCEPathComponent (*this) : exampleTabs.release(),
|
||||
true);
|
||||
|
||||
if (wasOpen)
|
||||
setCurrentTabIndex (1);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct SetJUCEPathComponent : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
explicit SetJUCEPathComponent (ProjectTemplatesAndExamples& o)
|
||||
: owner (o)
|
||||
{
|
||||
getGlobalProperties().addChangeListener (this);
|
||||
|
||||
setPathButton.setButtonText ("Set path to JUCE...");
|
||||
setPathButton.onClick = [] { ProjucerApplication::getApp().showPathsWindow (true); };
|
||||
|
||||
addAndMakeVisible (setPathButton);
|
||||
}
|
||||
|
||||
~SetJUCEPathComponent() override
|
||||
{
|
||||
getGlobalProperties().removeChangeListener (this);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (5);
|
||||
bounds.removeFromTop (25);
|
||||
|
||||
setPathButton.setBounds (bounds.removeFromTop (25));
|
||||
}
|
||||
|
||||
private:
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
if (isValidJUCEExamplesDirectory (ProjucerApplication::getJUCEExamplesDirectoryPathFromGlobal()))
|
||||
owner.refreshExamplesTab();
|
||||
}
|
||||
|
||||
ProjectTemplatesAndExamples& owner;
|
||||
TextButton setPathButton;
|
||||
};
|
||||
|
||||
ContentComponent& content;
|
||||
std::function<void (const File&)> exampleSelectedCallback;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
StartPageComponent::StartPageComponent (std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
|
||||
std::function<void (const File&)>&& exampleCb)
|
||||
: content (std::make_unique<ContentComponent>()),
|
||||
tabs (std::make_unique<ProjectTemplatesAndExamples> (*content, std::move (newProjectCb), std::move (exampleCb)))
|
||||
{
|
||||
tabs->setOutline (0);
|
||||
addAndMakeVisible (*tabs);
|
||||
|
||||
addAndMakeVisible (openExistingButton);
|
||||
openExistingButton.setCommandToTrigger (&ProjucerApplication::getCommandManager(), CommandIDs::open, true);
|
||||
|
||||
addAndMakeVisible (*content);
|
||||
|
||||
setSize (900, 600);
|
||||
}
|
||||
|
||||
void StartPageComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void StartPageComponent::resized()
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (10);
|
||||
|
||||
auto tabBounds = bounds.removeFromLeft (bounds.getWidth() / 3);
|
||||
|
||||
openExistingButton.setBounds (tabBounds.removeFromBottom (30).reduced (10, 0));
|
||||
tabBounds.removeFromBottom (5);
|
||||
|
||||
tabs->setBounds (tabBounds);
|
||||
bounds.removeFromLeft (10);
|
||||
|
||||
content->setBounds (bounds);
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../jucer_Application.h"
|
||||
|
||||
#include "jucer_StartPageComponent.h"
|
||||
#include "jucer_StartPageTreeHolder.h"
|
||||
#include "jucer_NewProjectTemplates.h"
|
||||
#include "jucer_ContentComponents.h"
|
||||
|
||||
//==============================================================================
|
||||
struct ContentComponent : public Component
|
||||
{
|
||||
ContentComponent()
|
||||
{
|
||||
setTitle ("Content");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
if (content != nullptr)
|
||||
content->setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
void setContent (std::unique_ptr<Component>&& newContent)
|
||||
{
|
||||
if (content.get() != newContent.get())
|
||||
{
|
||||
content = std::move (newContent);
|
||||
addAndMakeVisible (content.get());
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Component> content;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (ContentComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static File findExampleFile (int dirIndex, int index)
|
||||
{
|
||||
auto dir = ProjucerApplication::getSortedExampleDirectories()[dirIndex];
|
||||
return ProjucerApplication::getSortedExampleFilesInDirectory (dir)[index];
|
||||
}
|
||||
|
||||
static std::unique_ptr<Component> createExampleProjectsTab (ContentComponent& content, std::function<void (const File&)> cb)
|
||||
{
|
||||
StringArray exampleCategories;
|
||||
std::vector<StringArray> examples;
|
||||
|
||||
for (auto& dir : ProjucerApplication::getSortedExampleDirectories())
|
||||
{
|
||||
exampleCategories.add (dir.getFileName());
|
||||
|
||||
StringArray ex;
|
||||
for (auto& f : ProjucerApplication::getSortedExampleFilesInDirectory (dir))
|
||||
ex.add (f.getFileNameWithoutExtension());
|
||||
|
||||
examples.push_back (ex);
|
||||
}
|
||||
|
||||
if (exampleCategories.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
auto selectedCallback = [&, cb] (int category, int index) mutable
|
||||
{
|
||||
content.setContent (std::make_unique<ExampleComponent> (findExampleFile (category, index), cb));
|
||||
};
|
||||
|
||||
return std::make_unique<StartPageTreeHolder> ("Examples",
|
||||
exampleCategories,
|
||||
examples,
|
||||
std::move (selectedCallback),
|
||||
StartPageTreeHolder::Open::no);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static StringArray getAllTemplateCategoryStrings()
|
||||
{
|
||||
StringArray categories;
|
||||
|
||||
for (auto& t : NewProjectTemplates::getAllTemplates())
|
||||
categories.addIfNotAlreadyThere (NewProjectTemplates::getProjectCategoryString (t.category));
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
static std::vector<NewProjectTemplates::ProjectTemplate> getTemplatesInCategory (const String& category)
|
||||
{
|
||||
std::vector<NewProjectTemplates::ProjectTemplate> templates;
|
||||
|
||||
for (auto& t : NewProjectTemplates::getAllTemplates())
|
||||
if (NewProjectTemplates::getProjectCategoryString (t.category) == category)
|
||||
templates.push_back (t);
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
static StringArray getAllTemplateNamesForCategory (const String& category)
|
||||
{
|
||||
StringArray types;
|
||||
|
||||
for (auto& t : getTemplatesInCategory (category))
|
||||
types.add (t.displayName);
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Component> createProjectTemplatesTab (ContentComponent& content,
|
||||
std::function<void (std::unique_ptr<Project>&&)>&& cb)
|
||||
{
|
||||
auto categories = getAllTemplateCategoryStrings();
|
||||
|
||||
std::vector<StringArray> templateNames;
|
||||
|
||||
for (auto& c : categories)
|
||||
templateNames.push_back (getAllTemplateNamesForCategory (c));
|
||||
|
||||
auto selectedCallback = [&, cb] (int category, int index)
|
||||
{
|
||||
auto categoryString = getAllTemplateCategoryStrings()[category];
|
||||
auto templates = getTemplatesInCategory (categoryString);
|
||||
|
||||
content.setContent (std::make_unique<TemplateComponent> (templates[(size_t) index], std::move (cb)));
|
||||
};
|
||||
|
||||
auto holder = std::make_unique<StartPageTreeHolder> ("Templates",
|
||||
categories,
|
||||
templateNames,
|
||||
std::move (selectedCallback),
|
||||
StartPageTreeHolder::Open::yes);
|
||||
holder->setSelectedItem (categories[0], 1);
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
|
||||
return std::move (holder);
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ProjectTemplatesAndExamples : public TabbedComponent
|
||||
{
|
||||
ProjectTemplatesAndExamples (ContentComponent& c,
|
||||
std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
|
||||
std::function<void (const File&)>&& exampleCb)
|
||||
: TabbedComponent (TabbedButtonBar::Orientation::TabsAtTop),
|
||||
content (c),
|
||||
exampleSelectedCallback (std::move (exampleCb))
|
||||
{
|
||||
setTitle ("Templates and Examples");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addTab ("New Project",
|
||||
Colours::transparentBlack,
|
||||
createProjectTemplatesTab (content, std::move (newProjectCb)).release(),
|
||||
true);
|
||||
|
||||
refreshExamplesTab();
|
||||
}
|
||||
|
||||
void refreshExamplesTab()
|
||||
{
|
||||
auto wasOpen = (getCurrentTabIndex() == 1);
|
||||
|
||||
removeTab (1);
|
||||
|
||||
auto exampleTabs = createExampleProjectsTab (content, exampleSelectedCallback);
|
||||
|
||||
addTab ("Open Example",
|
||||
Colours::transparentBlack,
|
||||
exampleTabs == nullptr ? new SetJUCEPathComponent (*this) : exampleTabs.release(),
|
||||
true);
|
||||
|
||||
if (wasOpen)
|
||||
setCurrentTabIndex (1);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct SetJUCEPathComponent : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
explicit SetJUCEPathComponent (ProjectTemplatesAndExamples& o)
|
||||
: owner (o)
|
||||
{
|
||||
getGlobalProperties().addChangeListener (this);
|
||||
|
||||
setPathButton.setButtonText ("Set path to JUCE...");
|
||||
setPathButton.onClick = [] { ProjucerApplication::getApp().showPathsWindow (true); };
|
||||
|
||||
addAndMakeVisible (setPathButton);
|
||||
}
|
||||
|
||||
~SetJUCEPathComponent() override
|
||||
{
|
||||
getGlobalProperties().removeChangeListener (this);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (5);
|
||||
bounds.removeFromTop (25);
|
||||
|
||||
setPathButton.setBounds (bounds.removeFromTop (25));
|
||||
}
|
||||
|
||||
private:
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
if (isValidJUCEExamplesDirectory (ProjucerApplication::getJUCEExamplesDirectoryPathFromGlobal()))
|
||||
owner.refreshExamplesTab();
|
||||
}
|
||||
|
||||
ProjectTemplatesAndExamples& owner;
|
||||
TextButton setPathButton;
|
||||
};
|
||||
|
||||
ContentComponent& content;
|
||||
std::function<void (const File&)> exampleSelectedCallback;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
StartPageComponent::StartPageComponent (std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
|
||||
std::function<void (const File&)>&& exampleCb)
|
||||
: content (std::make_unique<ContentComponent>()),
|
||||
tabs (std::make_unique<ProjectTemplatesAndExamples> (*content, std::move (newProjectCb), std::move (exampleCb)))
|
||||
{
|
||||
tabs->setOutline (0);
|
||||
addAndMakeVisible (*tabs);
|
||||
|
||||
addAndMakeVisible (openExistingButton);
|
||||
openExistingButton.setCommandToTrigger (&ProjucerApplication::getCommandManager(), CommandIDs::open, true);
|
||||
|
||||
addAndMakeVisible (*content);
|
||||
|
||||
setSize (900, 600);
|
||||
}
|
||||
|
||||
void StartPageComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void StartPageComponent::resized()
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (10);
|
||||
|
||||
auto tabBounds = bounds.removeFromLeft (bounds.getWidth() / 3);
|
||||
|
||||
openExistingButton.setBounds (tabBounds.removeFromBottom (30).reduced (10, 0));
|
||||
tabBounds.removeFromBottom (5);
|
||||
|
||||
tabs->setBounds (tabBounds);
|
||||
bounds.removeFromLeft (10);
|
||||
|
||||
content->setBounds (bounds);
|
||||
}
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ContentComponent;
|
||||
struct ProjectTemplatesAndExamples;
|
||||
|
||||
//==============================================================================
|
||||
class StartPageComponent : public Component
|
||||
{
|
||||
public:
|
||||
StartPageComponent (std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
|
||||
std::function<void (const File&)>&& exampleCb);
|
||||
|
||||
void paint (Graphics& g) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<ContentComponent> content;
|
||||
std::unique_ptr<ProjectTemplatesAndExamples> tabs;
|
||||
|
||||
TextButton openExistingButton { "Open Existing Project..." };
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StartPageComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ContentComponent;
|
||||
struct ProjectTemplatesAndExamples;
|
||||
|
||||
//==============================================================================
|
||||
class StartPageComponent : public Component
|
||||
{
|
||||
public:
|
||||
StartPageComponent (std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
|
||||
std::function<void (const File&)>&& exampleCb);
|
||||
|
||||
void paint (Graphics& g) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<ContentComponent> content;
|
||||
std::unique_ptr<ProjectTemplatesAndExamples> tabs;
|
||||
|
||||
TextButton openExistingButton { "Open Existing Project..." };
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StartPageComponent)
|
||||
};
|
||||
|
||||
@@ -1,182 +1,182 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
class StartPageTreeHolder : public Component
|
||||
{
|
||||
public:
|
||||
enum class Open { no, yes };
|
||||
|
||||
StartPageTreeHolder (const String& title,
|
||||
const StringArray& headerNames,
|
||||
const std::vector<StringArray>& itemNames,
|
||||
std::function<void (int, int)>&& selectedCallback,
|
||||
Open shouldBeOpen)
|
||||
: headers (headerNames),
|
||||
items (itemNames),
|
||||
itemSelectedCallback (std::move (selectedCallback))
|
||||
{
|
||||
jassert (headers.size() == (int) items.size());
|
||||
|
||||
tree.setTitle (title);
|
||||
tree.setRootItem (new TreeRootItem (*this));
|
||||
tree.setRootItemVisible (false);
|
||||
tree.setIndentSize (15);
|
||||
tree.setDefaultOpenness (shouldBeOpen == Open::yes);
|
||||
|
||||
addAndMakeVisible (tree);
|
||||
}
|
||||
|
||||
~StartPageTreeHolder() override
|
||||
{
|
||||
tree.deleteRootItem();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
tree.setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
void setSelectedItem (const String& category, int index)
|
||||
{
|
||||
auto* root = tree.getRootItem();
|
||||
|
||||
for (int i = root->getNumSubItems(); --i >=0;)
|
||||
{
|
||||
if (auto* item = root->getSubItem (i))
|
||||
{
|
||||
if (item->getUniqueName() == category)
|
||||
item->getSubItem (index)->setSelected (true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class TreeSubItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
TreeSubItem (StartPageTreeHolder& o, const String& n, const StringArray& subItemsIn)
|
||||
: owner (o), name (n), isHeader (subItemsIn.size() > 0)
|
||||
{
|
||||
for (auto& s : subItemsIn)
|
||||
addSubItem (new TreeSubItem (owner, s, {}));
|
||||
}
|
||||
|
||||
bool mightContainSubItems() override { return isHeader; }
|
||||
bool canBeSelected() const override { return ! isHeader; }
|
||||
|
||||
int getItemWidth() const override { return -1; }
|
||||
int getItemHeight() const override { return 25; }
|
||||
|
||||
String getUniqueName() const override { return name; }
|
||||
String getAccessibilityName() override { return getUniqueName(); }
|
||||
|
||||
void paintOpenCloseButton (Graphics& g, const Rectangle<float>& area, Colour, bool isMouseOver) override
|
||||
{
|
||||
g.setColour (getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId
|
||||
: treeIconColourId));
|
||||
|
||||
TreeViewItem::paintOpenCloseButton (g, area, getOwnerView()->findColour (defaultIconColourId), isMouseOver);
|
||||
}
|
||||
|
||||
void paintItem (Graphics& g, int w, int h) override
|
||||
{
|
||||
Rectangle<int> bounds (w, h);
|
||||
|
||||
auto shouldBeHighlighted = isSelected();
|
||||
|
||||
if (shouldBeHighlighted)
|
||||
{
|
||||
g.setColour (getOwnerView()->findColour (defaultHighlightColourId));
|
||||
g.fillRect (bounds);
|
||||
}
|
||||
|
||||
g.setColour (shouldBeHighlighted ? getOwnerView()->findColour (defaultHighlightedTextColourId)
|
||||
: getOwnerView()->findColour (defaultTextColourId));
|
||||
|
||||
g.drawFittedText (name, bounds.reduced (5).withTrimmedLeft (10), Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
void itemClicked (const MouseEvent& e) override
|
||||
{
|
||||
if (isSelected())
|
||||
itemSelectionChanged (true);
|
||||
|
||||
if (e.mods.isPopupMenu() && mightContainSubItems())
|
||||
setOpen (! isOpen());
|
||||
}
|
||||
|
||||
void itemSelectionChanged (bool isNowSelected) override
|
||||
{
|
||||
jassert (! isHeader);
|
||||
|
||||
if (isNowSelected)
|
||||
owner.itemSelectedCallback (getParentItem()->getIndexInParent(), getIndexInParent());
|
||||
}
|
||||
|
||||
private:
|
||||
StartPageTreeHolder& owner;
|
||||
String name;
|
||||
bool isHeader = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeSubItem)
|
||||
};
|
||||
|
||||
struct TreeRootItem : public TreeViewItem
|
||||
{
|
||||
explicit TreeRootItem (StartPageTreeHolder& o)
|
||||
: owner (o)
|
||||
{
|
||||
for (int i = 0; i < owner.headers.size(); ++i)
|
||||
addSubItem (new TreeSubItem (owner, owner.headers[i], owner.items[(size_t) i]));
|
||||
}
|
||||
|
||||
bool mightContainSubItems() override { return ! owner.headers.isEmpty();}
|
||||
|
||||
StartPageTreeHolder& owner;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeRootItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
TreeView tree;
|
||||
StringArray headers;
|
||||
std::vector<StringArray> items;
|
||||
|
||||
std::function<void (int, int)> itemSelectedCallback;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StartPageTreeHolder)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
class StartPageTreeHolder : public Component
|
||||
{
|
||||
public:
|
||||
enum class Open { no, yes };
|
||||
|
||||
StartPageTreeHolder (const String& title,
|
||||
const StringArray& headerNames,
|
||||
const std::vector<StringArray>& itemNames,
|
||||
std::function<void (int, int)>&& selectedCallback,
|
||||
Open shouldBeOpen)
|
||||
: headers (headerNames),
|
||||
items (itemNames),
|
||||
itemSelectedCallback (std::move (selectedCallback))
|
||||
{
|
||||
jassert (headers.size() == (int) items.size());
|
||||
|
||||
tree.setTitle (title);
|
||||
tree.setRootItem (new TreeRootItem (*this));
|
||||
tree.setRootItemVisible (false);
|
||||
tree.setIndentSize (15);
|
||||
tree.setDefaultOpenness (shouldBeOpen == Open::yes);
|
||||
|
||||
addAndMakeVisible (tree);
|
||||
}
|
||||
|
||||
~StartPageTreeHolder() override
|
||||
{
|
||||
tree.deleteRootItem();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
tree.setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
void setSelectedItem (const String& category, int index)
|
||||
{
|
||||
auto* root = tree.getRootItem();
|
||||
|
||||
for (int i = root->getNumSubItems(); --i >=0;)
|
||||
{
|
||||
if (auto* item = root->getSubItem (i))
|
||||
{
|
||||
if (item->getUniqueName() == category)
|
||||
item->getSubItem (index)->setSelected (true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class TreeSubItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
TreeSubItem (StartPageTreeHolder& o, const String& n, const StringArray& subItemsIn)
|
||||
: owner (o), name (n), isHeader (subItemsIn.size() > 0)
|
||||
{
|
||||
for (auto& s : subItemsIn)
|
||||
addSubItem (new TreeSubItem (owner, s, {}));
|
||||
}
|
||||
|
||||
bool mightContainSubItems() override { return isHeader; }
|
||||
bool canBeSelected() const override { return ! isHeader; }
|
||||
|
||||
int getItemWidth() const override { return -1; }
|
||||
int getItemHeight() const override { return 25; }
|
||||
|
||||
String getUniqueName() const override { return name; }
|
||||
String getAccessibilityName() override { return getUniqueName(); }
|
||||
|
||||
void paintOpenCloseButton (Graphics& g, const Rectangle<float>& area, Colour, bool isMouseOver) override
|
||||
{
|
||||
g.setColour (getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId
|
||||
: treeIconColourId));
|
||||
|
||||
TreeViewItem::paintOpenCloseButton (g, area, getOwnerView()->findColour (defaultIconColourId), isMouseOver);
|
||||
}
|
||||
|
||||
void paintItem (Graphics& g, int w, int h) override
|
||||
{
|
||||
Rectangle<int> bounds (w, h);
|
||||
|
||||
auto shouldBeHighlighted = isSelected();
|
||||
|
||||
if (shouldBeHighlighted)
|
||||
{
|
||||
g.setColour (getOwnerView()->findColour (defaultHighlightColourId));
|
||||
g.fillRect (bounds);
|
||||
}
|
||||
|
||||
g.setColour (shouldBeHighlighted ? getOwnerView()->findColour (defaultHighlightedTextColourId)
|
||||
: getOwnerView()->findColour (defaultTextColourId));
|
||||
|
||||
g.drawFittedText (name, bounds.reduced (5).withTrimmedLeft (10), Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
void itemClicked (const MouseEvent& e) override
|
||||
{
|
||||
if (isSelected())
|
||||
itemSelectionChanged (true);
|
||||
|
||||
if (e.mods.isPopupMenu() && mightContainSubItems())
|
||||
setOpen (! isOpen());
|
||||
}
|
||||
|
||||
void itemSelectionChanged (bool isNowSelected) override
|
||||
{
|
||||
jassert (! isHeader);
|
||||
|
||||
if (isNowSelected)
|
||||
owner.itemSelectedCallback (getParentItem()->getIndexInParent(), getIndexInParent());
|
||||
}
|
||||
|
||||
private:
|
||||
StartPageTreeHolder& owner;
|
||||
String name;
|
||||
bool isHeader = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeSubItem)
|
||||
};
|
||||
|
||||
struct TreeRootItem : public TreeViewItem
|
||||
{
|
||||
explicit TreeRootItem (StartPageTreeHolder& o)
|
||||
: owner (o)
|
||||
{
|
||||
for (int i = 0; i < owner.headers.size(); ++i)
|
||||
addSubItem (new TreeSubItem (owner, owner.headers[i], owner.items[(size_t) i]));
|
||||
}
|
||||
|
||||
bool mightContainSubItems() override { return ! owner.headers.isEmpty();}
|
||||
|
||||
StartPageTreeHolder& owner;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeRootItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
TreeView tree;
|
||||
StringArray headers;
|
||||
std::vector<StringArray> items;
|
||||
|
||||
std::function<void (int, int)> itemSelectedCallback;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StartPageTreeHolder)
|
||||
};
|
||||
|
||||
@@ -1,224 +1,224 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_LicenseState.h"
|
||||
#include "jucer_LicenseQueryThread.h"
|
||||
|
||||
//==============================================================================
|
||||
class LicenseController : private Timer
|
||||
{
|
||||
public:
|
||||
LicenseController()
|
||||
{
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static LicenseState getGPLState()
|
||||
{
|
||||
return { LicenseState::Type::gpl, projucerMajorVersion, {}, {} };
|
||||
}
|
||||
|
||||
LicenseState getCurrentState() const noexcept
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
void setState (const LicenseState& newState)
|
||||
{
|
||||
if (state != newState)
|
||||
{
|
||||
state = newState;
|
||||
licenseStateToSettings (state, getGlobalProperties());
|
||||
|
||||
stateListeners.call ([] (LicenseStateListener& l) { l.licenseStateChanged(); });
|
||||
}
|
||||
}
|
||||
|
||||
void resetState()
|
||||
{
|
||||
setState ({});
|
||||
}
|
||||
|
||||
void signIn (const String& email, const String& password,
|
||||
std::function<void (const String&)> completionCallback)
|
||||
{
|
||||
licenseQueryThread.doSignIn (email, password,
|
||||
[this, completionCallback] (LicenseQueryThread::ErrorMessageAndType error,
|
||||
LicenseState newState)
|
||||
{
|
||||
completionCallback (error.first);
|
||||
setState (newState);
|
||||
});
|
||||
}
|
||||
|
||||
void cancelSignIn()
|
||||
{
|
||||
licenseQueryThread.cancelRunningJobs();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct LicenseStateListener
|
||||
{
|
||||
virtual ~LicenseStateListener() = default;
|
||||
virtual void licenseStateChanged() = 0;
|
||||
};
|
||||
|
||||
void addListener (LicenseStateListener* listenerToAdd)
|
||||
{
|
||||
stateListeners.add (listenerToAdd);
|
||||
}
|
||||
|
||||
void removeListener (LicenseStateListener* listenerToRemove)
|
||||
{
|
||||
stateListeners.remove (listenerToRemove);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static const char* getLicenseStateValue (LicenseState::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LicenseState::Type::gpl: return "GPL";
|
||||
case LicenseState::Type::personal: return "personal";
|
||||
case LicenseState::Type::educational: return "edu";
|
||||
case LicenseState::Type::indie: return "indie";
|
||||
case LicenseState::Type::pro: return "pro";
|
||||
case LicenseState::Type::none:
|
||||
default: break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LicenseState::Type getLicenseTypeFromValue (const String& d)
|
||||
{
|
||||
if (d == getLicenseStateValue (LicenseState::Type::gpl)) return LicenseState::Type::gpl;
|
||||
if (d == getLicenseStateValue (LicenseState::Type::personal)) return LicenseState::Type::personal;
|
||||
if (d == getLicenseStateValue (LicenseState::Type::educational)) return LicenseState::Type::educational;
|
||||
if (d == getLicenseStateValue (LicenseState::Type::indie)) return LicenseState::Type::indie;
|
||||
if (d == getLicenseStateValue (LicenseState::Type::pro)) return LicenseState::Type::pro;
|
||||
return LicenseState::Type::none;
|
||||
}
|
||||
|
||||
static LicenseState licenseStateFromSettings (PropertiesFile& props)
|
||||
{
|
||||
if (auto licenseXml = props.getXmlValue ("license"))
|
||||
{
|
||||
// this is here for backwards compatibility with old-style settings files using XML text elements
|
||||
if (licenseXml->getChildElementAllSubText ("type", {}).isNotEmpty())
|
||||
{
|
||||
auto stateFromOldSettings = [&licenseXml]() -> LicenseState
|
||||
{
|
||||
return { getLicenseTypeFromValue (licenseXml->getChildElementAllSubText ("type", {})),
|
||||
licenseXml->getChildElementAllSubText ("version", "-1").getIntValue(),
|
||||
licenseXml->getChildElementAllSubText ("username", {}),
|
||||
licenseXml->getChildElementAllSubText ("authToken", {}) };
|
||||
}();
|
||||
|
||||
licenseStateToSettings (stateFromOldSettings, props);
|
||||
|
||||
return stateFromOldSettings;
|
||||
}
|
||||
|
||||
return { getLicenseTypeFromValue (licenseXml->getStringAttribute ("type", {})),
|
||||
licenseXml->getIntAttribute ("version", -1),
|
||||
licenseXml->getStringAttribute ("username", {}),
|
||||
licenseXml->getStringAttribute ("authToken", {}) };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static void licenseStateToSettings (const LicenseState& state, PropertiesFile& props)
|
||||
{
|
||||
props.removeValue ("license");
|
||||
|
||||
if (state.isSignedIn())
|
||||
{
|
||||
XmlElement licenseXml ("license");
|
||||
|
||||
if (auto* typeString = getLicenseStateValue (state.type))
|
||||
licenseXml.setAttribute ("type", typeString);
|
||||
|
||||
licenseXml.setAttribute ("version", state.version);
|
||||
licenseXml.setAttribute ("username", state.username);
|
||||
licenseXml.setAttribute ("authToken", state.authToken);
|
||||
|
||||
props.setValue ("license", &licenseXml);
|
||||
}
|
||||
|
||||
props.saveIfNeeded();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void checkLicense()
|
||||
{
|
||||
if (state.authToken.isNotEmpty() && ! state.isGPL())
|
||||
{
|
||||
auto completionCallback = [this] (LicenseQueryThread::ErrorMessageAndType error,
|
||||
LicenseState updatedState)
|
||||
{
|
||||
if (error == LicenseQueryThread::ErrorMessageAndType())
|
||||
{
|
||||
setState (updatedState);
|
||||
}
|
||||
else if ((error.second == LicenseQueryThread::ErrorType::busy
|
||||
|| error.second == LicenseQueryThread::ErrorType::cancelled
|
||||
|| error.second == LicenseQueryThread::ErrorType::connectionError)
|
||||
&& ! hasRetriedLicenseCheck)
|
||||
{
|
||||
hasRetriedLicenseCheck = true;
|
||||
startTimer (10000);
|
||||
}
|
||||
};
|
||||
|
||||
licenseQueryThread.checkLicenseValidity (state, std::move (completionCallback));
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCER_ENABLE_GPL_MODE
|
||||
LicenseState state = getGPLState();
|
||||
#else
|
||||
LicenseState state = licenseStateFromSettings (getGlobalProperties());
|
||||
#endif
|
||||
|
||||
ListenerList<LicenseStateListener> stateListeners;
|
||||
LicenseQueryThread licenseQueryThread;
|
||||
bool hasRetriedLicenseCheck = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseController)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_LicenseState.h"
|
||||
#include "jucer_LicenseQueryThread.h"
|
||||
|
||||
//==============================================================================
|
||||
class LicenseController : private Timer
|
||||
{
|
||||
public:
|
||||
LicenseController()
|
||||
{
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static LicenseState getGPLState()
|
||||
{
|
||||
return { LicenseState::Type::gpl, projucerMajorVersion, {}, {} };
|
||||
}
|
||||
|
||||
LicenseState getCurrentState() const noexcept
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
void setState (const LicenseState& newState)
|
||||
{
|
||||
if (state != newState)
|
||||
{
|
||||
state = newState;
|
||||
licenseStateToSettings (state, getGlobalProperties());
|
||||
|
||||
stateListeners.call ([] (LicenseStateListener& l) { l.licenseStateChanged(); });
|
||||
}
|
||||
}
|
||||
|
||||
void resetState()
|
||||
{
|
||||
setState ({});
|
||||
}
|
||||
|
||||
void signIn (const String& email, const String& password,
|
||||
std::function<void (const String&)> completionCallback)
|
||||
{
|
||||
licenseQueryThread.doSignIn (email, password,
|
||||
[this, completionCallback] (LicenseQueryThread::ErrorMessageAndType error,
|
||||
LicenseState newState)
|
||||
{
|
||||
completionCallback (error.first);
|
||||
setState (newState);
|
||||
});
|
||||
}
|
||||
|
||||
void cancelSignIn()
|
||||
{
|
||||
licenseQueryThread.cancelRunningJobs();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct LicenseStateListener
|
||||
{
|
||||
virtual ~LicenseStateListener() = default;
|
||||
virtual void licenseStateChanged() = 0;
|
||||
};
|
||||
|
||||
void addListener (LicenseStateListener* listenerToAdd)
|
||||
{
|
||||
stateListeners.add (listenerToAdd);
|
||||
}
|
||||
|
||||
void removeListener (LicenseStateListener* listenerToRemove)
|
||||
{
|
||||
stateListeners.remove (listenerToRemove);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static const char* getLicenseStateValue (LicenseState::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LicenseState::Type::gpl: return "GPL";
|
||||
case LicenseState::Type::personal: return "personal";
|
||||
case LicenseState::Type::educational: return "edu";
|
||||
case LicenseState::Type::indie: return "indie";
|
||||
case LicenseState::Type::pro: return "pro";
|
||||
case LicenseState::Type::none:
|
||||
default: break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LicenseState::Type getLicenseTypeFromValue (const String& d)
|
||||
{
|
||||
if (d == getLicenseStateValue (LicenseState::Type::gpl)) return LicenseState::Type::gpl;
|
||||
if (d == getLicenseStateValue (LicenseState::Type::personal)) return LicenseState::Type::personal;
|
||||
if (d == getLicenseStateValue (LicenseState::Type::educational)) return LicenseState::Type::educational;
|
||||
if (d == getLicenseStateValue (LicenseState::Type::indie)) return LicenseState::Type::indie;
|
||||
if (d == getLicenseStateValue (LicenseState::Type::pro)) return LicenseState::Type::pro;
|
||||
return LicenseState::Type::none;
|
||||
}
|
||||
|
||||
static LicenseState licenseStateFromSettings (PropertiesFile& props)
|
||||
{
|
||||
if (auto licenseXml = props.getXmlValue ("license"))
|
||||
{
|
||||
// this is here for backwards compatibility with old-style settings files using XML text elements
|
||||
if (licenseXml->getChildElementAllSubText ("type", {}).isNotEmpty())
|
||||
{
|
||||
auto stateFromOldSettings = [&licenseXml]() -> LicenseState
|
||||
{
|
||||
return { getLicenseTypeFromValue (licenseXml->getChildElementAllSubText ("type", {})),
|
||||
licenseXml->getChildElementAllSubText ("version", "-1").getIntValue(),
|
||||
licenseXml->getChildElementAllSubText ("username", {}),
|
||||
licenseXml->getChildElementAllSubText ("authToken", {}) };
|
||||
}();
|
||||
|
||||
licenseStateToSettings (stateFromOldSettings, props);
|
||||
|
||||
return stateFromOldSettings;
|
||||
}
|
||||
|
||||
return { getLicenseTypeFromValue (licenseXml->getStringAttribute ("type", {})),
|
||||
licenseXml->getIntAttribute ("version", -1),
|
||||
licenseXml->getStringAttribute ("username", {}),
|
||||
licenseXml->getStringAttribute ("authToken", {}) };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static void licenseStateToSettings (const LicenseState& state, PropertiesFile& props)
|
||||
{
|
||||
props.removeValue ("license");
|
||||
|
||||
if (state.isSignedIn())
|
||||
{
|
||||
XmlElement licenseXml ("license");
|
||||
|
||||
if (auto* typeString = getLicenseStateValue (state.type))
|
||||
licenseXml.setAttribute ("type", typeString);
|
||||
|
||||
licenseXml.setAttribute ("version", state.version);
|
||||
licenseXml.setAttribute ("username", state.username);
|
||||
licenseXml.setAttribute ("authToken", state.authToken);
|
||||
|
||||
props.setValue ("license", &licenseXml);
|
||||
}
|
||||
|
||||
props.saveIfNeeded();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void checkLicense()
|
||||
{
|
||||
if (state.authToken.isNotEmpty() && ! state.isGPL())
|
||||
{
|
||||
auto completionCallback = [this] (LicenseQueryThread::ErrorMessageAndType error,
|
||||
LicenseState updatedState)
|
||||
{
|
||||
if (error == LicenseQueryThread::ErrorMessageAndType())
|
||||
{
|
||||
setState (updatedState);
|
||||
}
|
||||
else if ((error.second == LicenseQueryThread::ErrorType::busy
|
||||
|| error.second == LicenseQueryThread::ErrorType::cancelled
|
||||
|| error.second == LicenseQueryThread::ErrorType::connectionError)
|
||||
&& ! hasRetriedLicenseCheck)
|
||||
{
|
||||
hasRetriedLicenseCheck = true;
|
||||
startTimer (10000);
|
||||
}
|
||||
};
|
||||
|
||||
licenseQueryThread.checkLicenseValidity (state, std::move (completionCallback));
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCER_ENABLE_GPL_MODE
|
||||
LicenseState state = getGPLState();
|
||||
#else
|
||||
LicenseState state = licenseStateFromSettings (getGlobalProperties());
|
||||
#endif
|
||||
|
||||
ListenerList<LicenseStateListener> stateListeners;
|
||||
LicenseQueryThread licenseQueryThread;
|
||||
bool hasRetriedLicenseCheck = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseController)
|
||||
};
|
||||
|
||||
@@ -1,371 +1,371 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
namespace LicenseHelpers
|
||||
{
|
||||
inline LicenseState::Type licenseTypeForString (const String& licenseString)
|
||||
{
|
||||
if (licenseString == "juce-pro") return LicenseState::Type::pro;
|
||||
if (licenseString == "juce-indie") return LicenseState::Type::indie;
|
||||
if (licenseString == "juce-edu") return LicenseState::Type::educational;
|
||||
if (licenseString == "juce-personal") return LicenseState::Type::personal;
|
||||
|
||||
jassertfalse; // unknown type
|
||||
return LicenseState::Type::none;
|
||||
}
|
||||
|
||||
using LicenseVersionAndType = std::pair<int, LicenseState::Type>;
|
||||
|
||||
inline LicenseVersionAndType findBestLicense (std::vector<LicenseVersionAndType>&& licenses)
|
||||
{
|
||||
if (licenses.size() == 1)
|
||||
return licenses[0];
|
||||
|
||||
auto getValueForLicenceType = [] (LicenseState::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LicenseState::Type::pro: return 4;
|
||||
case LicenseState::Type::indie: return 3;
|
||||
case LicenseState::Type::educational: return 2;
|
||||
case LicenseState::Type::personal: return 1;
|
||||
case LicenseState::Type::gpl:
|
||||
case LicenseState::Type::none:
|
||||
default: return -1;
|
||||
}
|
||||
};
|
||||
|
||||
std::sort (licenses.begin(), licenses.end(),
|
||||
[getValueForLicenceType] (const LicenseVersionAndType& l1, const LicenseVersionAndType& l2)
|
||||
{
|
||||
if (l1.first > l2.first)
|
||||
return true;
|
||||
|
||||
if (l1.first == l2.first)
|
||||
return getValueForLicenceType (l1.second) > getValueForLicenceType (l2.second);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
auto findFirstLicense = [&licenses] (bool isPaid)
|
||||
{
|
||||
auto iter = std::find_if (licenses.begin(), licenses.end(),
|
||||
[isPaid] (const LicenseVersionAndType& l)
|
||||
{
|
||||
auto proOrIndie = (l.second == LicenseState::Type::pro || l.second == LicenseState::Type::indie);
|
||||
return isPaid ? proOrIndie : ! proOrIndie;
|
||||
});
|
||||
|
||||
return iter != licenses.end() ? *iter
|
||||
: LicenseVersionAndType();
|
||||
};
|
||||
|
||||
auto newestPaid = findFirstLicense (true);
|
||||
auto newestFree = findFirstLicense (false);
|
||||
|
||||
if (newestPaid.first >= projucerMajorVersion || newestPaid.first >= newestFree.first)
|
||||
return newestPaid;
|
||||
|
||||
return newestFree;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class LicenseQueryThread
|
||||
{
|
||||
public:
|
||||
enum class ErrorType
|
||||
{
|
||||
busy,
|
||||
cancelled,
|
||||
connectionError,
|
||||
webResponseError
|
||||
};
|
||||
|
||||
using ErrorMessageAndType = std::pair<String, ErrorType>;
|
||||
using LicenseQueryCallback = std::function<void (ErrorMessageAndType, LicenseState)>;
|
||||
|
||||
//==============================================================================
|
||||
LicenseQueryThread() = default;
|
||||
|
||||
void checkLicenseValidity (const LicenseState& state, LicenseQueryCallback completionCallback)
|
||||
{
|
||||
if (jobPool.getNumJobs() > 0)
|
||||
{
|
||||
completionCallback ({ {}, ErrorType::busy }, {});
|
||||
return;
|
||||
}
|
||||
|
||||
jobPool.addJob ([this, state, completionCallback]
|
||||
{
|
||||
auto updatedState = state;
|
||||
|
||||
auto result = runTask (std::make_unique<UserLicenseQuery> (state.authToken), updatedState);
|
||||
|
||||
WeakReference<LicenseQueryThread> weakThis (this);
|
||||
MessageManager::callAsync ([weakThis, result, updatedState, completionCallback]
|
||||
{
|
||||
if (weakThis != nullptr)
|
||||
completionCallback (result, updatedState);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void doSignIn (const String& email, const String& password, LicenseQueryCallback completionCallback)
|
||||
{
|
||||
cancelRunningJobs();
|
||||
|
||||
jobPool.addJob ([this, email, password, completionCallback]
|
||||
{
|
||||
LicenseState state;
|
||||
|
||||
auto result = runTask (std::make_unique<UserLogin> (email, password), state);
|
||||
|
||||
if (result == ErrorMessageAndType())
|
||||
result = runTask (std::make_unique<UserLicenseQuery> (state.authToken), state);
|
||||
|
||||
if (result != ErrorMessageAndType())
|
||||
state = {};
|
||||
|
||||
WeakReference<LicenseQueryThread> weakThis (this);
|
||||
MessageManager::callAsync ([weakThis, result, state, completionCallback]
|
||||
{
|
||||
if (weakThis != nullptr)
|
||||
completionCallback (result, state);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void cancelRunningJobs()
|
||||
{
|
||||
jobPool.removeAllJobs (true, 500);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct AccountEnquiryBase
|
||||
{
|
||||
virtual ~AccountEnquiryBase() = default;
|
||||
|
||||
virtual bool isPOSTLikeRequest() const = 0;
|
||||
virtual String getEndpointURLSuffix() const = 0;
|
||||
virtual StringPairArray getParameterNamesAndValues() const = 0;
|
||||
virtual String getExtraHeaders() const = 0;
|
||||
virtual int getSuccessCode() const = 0;
|
||||
virtual String errorCodeToString (int) const = 0;
|
||||
virtual bool parseServerResponse (const String&, LicenseState&) = 0;
|
||||
};
|
||||
|
||||
struct UserLogin : public AccountEnquiryBase
|
||||
{
|
||||
UserLogin (const String& e, const String& p)
|
||||
: userEmail (e), userPassword (p)
|
||||
{
|
||||
}
|
||||
|
||||
bool isPOSTLikeRequest() const override { return true; }
|
||||
String getEndpointURLSuffix() const override { return "/authenticate/projucer"; }
|
||||
int getSuccessCode() const override { return 200; }
|
||||
|
||||
StringPairArray getParameterNamesAndValues() const override
|
||||
{
|
||||
StringPairArray namesAndValues;
|
||||
namesAndValues.set ("email", userEmail);
|
||||
namesAndValues.set ("password", userPassword);
|
||||
|
||||
return namesAndValues;
|
||||
}
|
||||
|
||||
String getExtraHeaders() const override
|
||||
{
|
||||
return "Content-Type: application/json";
|
||||
}
|
||||
|
||||
String errorCodeToString (int errorCode) const override
|
||||
{
|
||||
switch (errorCode)
|
||||
{
|
||||
case 400: return "Please enter your email and password to sign in.";
|
||||
case 401: return "Your email and password are incorrect.";
|
||||
case 451: return "Access denied.";
|
||||
default: return "Something went wrong, please try again.";
|
||||
}
|
||||
}
|
||||
|
||||
bool parseServerResponse (const String& serverResponse, LicenseState& licenseState) override
|
||||
{
|
||||
auto json = JSON::parse (serverResponse);
|
||||
|
||||
licenseState.authToken = json.getProperty ("token", {}).toString();
|
||||
licenseState.username = json.getProperty ("user", {}).getProperty ("username", {}).toString();
|
||||
|
||||
return (licenseState.authToken.isNotEmpty() && licenseState.username.isNotEmpty());
|
||||
}
|
||||
|
||||
String userEmail, userPassword;
|
||||
};
|
||||
|
||||
struct UserLicenseQuery : public AccountEnquiryBase
|
||||
{
|
||||
UserLicenseQuery (const String& authToken)
|
||||
: userAuthToken (authToken)
|
||||
{
|
||||
}
|
||||
|
||||
bool isPOSTLikeRequest() const override { return false; }
|
||||
String getEndpointURLSuffix() const override { return "/user/licences/projucer"; }
|
||||
int getSuccessCode() const override { return 200; }
|
||||
|
||||
StringPairArray getParameterNamesAndValues() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
String getExtraHeaders() const override
|
||||
{
|
||||
return "x-access-token: " + userAuthToken;
|
||||
}
|
||||
|
||||
String errorCodeToString (int errorCode) const override
|
||||
{
|
||||
switch (errorCode)
|
||||
{
|
||||
case 401: return "User not found or could not be verified.";
|
||||
default: return "User licenses info fetch failed (unknown error).";
|
||||
}
|
||||
}
|
||||
|
||||
bool parseServerResponse (const String& serverResponse, LicenseState& licenseState) override
|
||||
{
|
||||
auto json = JSON::parse (serverResponse);
|
||||
|
||||
if (auto* licensesJson = json.getArray())
|
||||
{
|
||||
std::vector<LicenseHelpers::LicenseVersionAndType> licenses;
|
||||
|
||||
for (auto& license : *licensesJson)
|
||||
{
|
||||
auto version = license.getProperty ("product_version", {}).toString().trim();
|
||||
auto type = license.getProperty ("licence_type", {}).toString();
|
||||
auto status = license.getProperty ("status", {}).toString();
|
||||
|
||||
if (status == "active" && type.isNotEmpty() && version.isNotEmpty())
|
||||
licenses.push_back ({ version.getIntValue(), LicenseHelpers::licenseTypeForString (type) });
|
||||
}
|
||||
|
||||
if (! licenses.empty())
|
||||
{
|
||||
auto bestLicense = LicenseHelpers::findBestLicense (std::move (licenses));
|
||||
|
||||
licenseState.version = bestLicense.first;
|
||||
licenseState.type = bestLicense.second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String userAuthToken;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static String postDataStringAsJSON (const StringPairArray& parameters)
|
||||
{
|
||||
DynamicObject::Ptr d (new DynamicObject());
|
||||
|
||||
for (auto& key : parameters.getAllKeys())
|
||||
d->setProperty (key, parameters[key]);
|
||||
|
||||
return JSON::toString (var (d.get()));
|
||||
}
|
||||
|
||||
static ErrorMessageAndType runTask (std::unique_ptr<AccountEnquiryBase> accountEnquiryTask, LicenseState& state)
|
||||
{
|
||||
const ErrorMessageAndType cancelledError ("Cancelled.", ErrorType::cancelled);
|
||||
const String endpointURL ("https://api.juce.com/api/v1");
|
||||
|
||||
URL url (endpointURL + accountEnquiryTask->getEndpointURLSuffix());
|
||||
|
||||
auto isPOST = accountEnquiryTask->isPOSTLikeRequest();
|
||||
|
||||
if (isPOST)
|
||||
url = url.withPOSTData (postDataStringAsJSON (accountEnquiryTask->getParameterNamesAndValues()));
|
||||
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
int statusCode = 0;
|
||||
auto urlStream = url.createInputStream (URL::InputStreamOptions (isPOST ? URL::ParameterHandling::inPostData
|
||||
: URL::ParameterHandling::inAddress)
|
||||
.withExtraHeaders (accountEnquiryTask->getExtraHeaders())
|
||||
.withConnectionTimeoutMs (5000)
|
||||
.withStatusCode (&statusCode));
|
||||
|
||||
if (urlStream == nullptr)
|
||||
return { "Failed to connect to the web server.", ErrorType::connectionError };
|
||||
|
||||
if (statusCode != accountEnquiryTask->getSuccessCode())
|
||||
return { accountEnquiryTask->errorCodeToString (statusCode), ErrorType::webResponseError };
|
||||
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
String response;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char buffer [8192] = "";
|
||||
auto num = urlStream->read (buffer, sizeof (buffer));
|
||||
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
if (num <= 0)
|
||||
break;
|
||||
|
||||
response += buffer;
|
||||
}
|
||||
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
if (! accountEnquiryTask->parseServerResponse (response, state))
|
||||
return { "Failed to parse server response.", ErrorType::webResponseError };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ThreadPool jobPool { 1 };
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (LicenseQueryThread)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseQueryThread)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
namespace LicenseHelpers
|
||||
{
|
||||
inline LicenseState::Type licenseTypeForString (const String& licenseString)
|
||||
{
|
||||
if (licenseString == "juce-pro") return LicenseState::Type::pro;
|
||||
if (licenseString == "juce-indie") return LicenseState::Type::indie;
|
||||
if (licenseString == "juce-edu") return LicenseState::Type::educational;
|
||||
if (licenseString == "juce-personal") return LicenseState::Type::personal;
|
||||
|
||||
jassertfalse; // unknown type
|
||||
return LicenseState::Type::none;
|
||||
}
|
||||
|
||||
using LicenseVersionAndType = std::pair<int, LicenseState::Type>;
|
||||
|
||||
inline LicenseVersionAndType findBestLicense (std::vector<LicenseVersionAndType>&& licenses)
|
||||
{
|
||||
if (licenses.size() == 1)
|
||||
return licenses[0];
|
||||
|
||||
auto getValueForLicenceType = [] (LicenseState::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LicenseState::Type::pro: return 4;
|
||||
case LicenseState::Type::indie: return 3;
|
||||
case LicenseState::Type::educational: return 2;
|
||||
case LicenseState::Type::personal: return 1;
|
||||
case LicenseState::Type::gpl:
|
||||
case LicenseState::Type::none:
|
||||
default: return -1;
|
||||
}
|
||||
};
|
||||
|
||||
std::sort (licenses.begin(), licenses.end(),
|
||||
[getValueForLicenceType] (const LicenseVersionAndType& l1, const LicenseVersionAndType& l2)
|
||||
{
|
||||
if (l1.first > l2.first)
|
||||
return true;
|
||||
|
||||
if (l1.first == l2.first)
|
||||
return getValueForLicenceType (l1.second) > getValueForLicenceType (l2.second);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
auto findFirstLicense = [&licenses] (bool isPaid)
|
||||
{
|
||||
auto iter = std::find_if (licenses.begin(), licenses.end(),
|
||||
[isPaid] (const LicenseVersionAndType& l)
|
||||
{
|
||||
auto proOrIndie = (l.second == LicenseState::Type::pro || l.second == LicenseState::Type::indie);
|
||||
return isPaid ? proOrIndie : ! proOrIndie;
|
||||
});
|
||||
|
||||
return iter != licenses.end() ? *iter
|
||||
: LicenseVersionAndType();
|
||||
};
|
||||
|
||||
auto newestPaid = findFirstLicense (true);
|
||||
auto newestFree = findFirstLicense (false);
|
||||
|
||||
if (newestPaid.first >= projucerMajorVersion || newestPaid.first >= newestFree.first)
|
||||
return newestPaid;
|
||||
|
||||
return newestFree;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class LicenseQueryThread
|
||||
{
|
||||
public:
|
||||
enum class ErrorType
|
||||
{
|
||||
busy,
|
||||
cancelled,
|
||||
connectionError,
|
||||
webResponseError
|
||||
};
|
||||
|
||||
using ErrorMessageAndType = std::pair<String, ErrorType>;
|
||||
using LicenseQueryCallback = std::function<void (ErrorMessageAndType, LicenseState)>;
|
||||
|
||||
//==============================================================================
|
||||
LicenseQueryThread() = default;
|
||||
|
||||
void checkLicenseValidity (const LicenseState& state, LicenseQueryCallback completionCallback)
|
||||
{
|
||||
if (jobPool.getNumJobs() > 0)
|
||||
{
|
||||
completionCallback ({ {}, ErrorType::busy }, {});
|
||||
return;
|
||||
}
|
||||
|
||||
jobPool.addJob ([this, state, completionCallback]
|
||||
{
|
||||
auto updatedState = state;
|
||||
|
||||
auto result = runTask (std::make_unique<UserLicenseQuery> (state.authToken), updatedState);
|
||||
|
||||
WeakReference<LicenseQueryThread> weakThis (this);
|
||||
MessageManager::callAsync ([weakThis, result, updatedState, completionCallback]
|
||||
{
|
||||
if (weakThis != nullptr)
|
||||
completionCallback (result, updatedState);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void doSignIn (const String& email, const String& password, LicenseQueryCallback completionCallback)
|
||||
{
|
||||
cancelRunningJobs();
|
||||
|
||||
jobPool.addJob ([this, email, password, completionCallback]
|
||||
{
|
||||
LicenseState state;
|
||||
|
||||
auto result = runTask (std::make_unique<UserLogin> (email, password), state);
|
||||
|
||||
if (result == ErrorMessageAndType())
|
||||
result = runTask (std::make_unique<UserLicenseQuery> (state.authToken), state);
|
||||
|
||||
if (result != ErrorMessageAndType())
|
||||
state = {};
|
||||
|
||||
WeakReference<LicenseQueryThread> weakThis (this);
|
||||
MessageManager::callAsync ([weakThis, result, state, completionCallback]
|
||||
{
|
||||
if (weakThis != nullptr)
|
||||
completionCallback (result, state);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void cancelRunningJobs()
|
||||
{
|
||||
jobPool.removeAllJobs (true, 500);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct AccountEnquiryBase
|
||||
{
|
||||
virtual ~AccountEnquiryBase() = default;
|
||||
|
||||
virtual bool isPOSTLikeRequest() const = 0;
|
||||
virtual String getEndpointURLSuffix() const = 0;
|
||||
virtual StringPairArray getParameterNamesAndValues() const = 0;
|
||||
virtual String getExtraHeaders() const = 0;
|
||||
virtual int getSuccessCode() const = 0;
|
||||
virtual String errorCodeToString (int) const = 0;
|
||||
virtual bool parseServerResponse (const String&, LicenseState&) = 0;
|
||||
};
|
||||
|
||||
struct UserLogin : public AccountEnquiryBase
|
||||
{
|
||||
UserLogin (const String& e, const String& p)
|
||||
: userEmail (e), userPassword (p)
|
||||
{
|
||||
}
|
||||
|
||||
bool isPOSTLikeRequest() const override { return true; }
|
||||
String getEndpointURLSuffix() const override { return "/authenticate/projucer"; }
|
||||
int getSuccessCode() const override { return 200; }
|
||||
|
||||
StringPairArray getParameterNamesAndValues() const override
|
||||
{
|
||||
StringPairArray namesAndValues;
|
||||
namesAndValues.set ("email", userEmail);
|
||||
namesAndValues.set ("password", userPassword);
|
||||
|
||||
return namesAndValues;
|
||||
}
|
||||
|
||||
String getExtraHeaders() const override
|
||||
{
|
||||
return "Content-Type: application/json";
|
||||
}
|
||||
|
||||
String errorCodeToString (int errorCode) const override
|
||||
{
|
||||
switch (errorCode)
|
||||
{
|
||||
case 400: return "Please enter your email and password to sign in.";
|
||||
case 401: return "Your email and password are incorrect.";
|
||||
case 451: return "Access denied.";
|
||||
default: return "Something went wrong, please try again.";
|
||||
}
|
||||
}
|
||||
|
||||
bool parseServerResponse (const String& serverResponse, LicenseState& licenseState) override
|
||||
{
|
||||
auto json = JSON::parse (serverResponse);
|
||||
|
||||
licenseState.authToken = json.getProperty ("token", {}).toString();
|
||||
licenseState.username = json.getProperty ("user", {}).getProperty ("username", {}).toString();
|
||||
|
||||
return (licenseState.authToken.isNotEmpty() && licenseState.username.isNotEmpty());
|
||||
}
|
||||
|
||||
String userEmail, userPassword;
|
||||
};
|
||||
|
||||
struct UserLicenseQuery : public AccountEnquiryBase
|
||||
{
|
||||
UserLicenseQuery (const String& authToken)
|
||||
: userAuthToken (authToken)
|
||||
{
|
||||
}
|
||||
|
||||
bool isPOSTLikeRequest() const override { return false; }
|
||||
String getEndpointURLSuffix() const override { return "/user/licences/projucer"; }
|
||||
int getSuccessCode() const override { return 200; }
|
||||
|
||||
StringPairArray getParameterNamesAndValues() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
String getExtraHeaders() const override
|
||||
{
|
||||
return "x-access-token: " + userAuthToken;
|
||||
}
|
||||
|
||||
String errorCodeToString (int errorCode) const override
|
||||
{
|
||||
switch (errorCode)
|
||||
{
|
||||
case 401: return "User not found or could not be verified.";
|
||||
default: return "User licenses info fetch failed (unknown error).";
|
||||
}
|
||||
}
|
||||
|
||||
bool parseServerResponse (const String& serverResponse, LicenseState& licenseState) override
|
||||
{
|
||||
auto json = JSON::parse (serverResponse);
|
||||
|
||||
if (auto* licensesJson = json.getArray())
|
||||
{
|
||||
std::vector<LicenseHelpers::LicenseVersionAndType> licenses;
|
||||
|
||||
for (auto& license : *licensesJson)
|
||||
{
|
||||
auto version = license.getProperty ("product_version", {}).toString().trim();
|
||||
auto type = license.getProperty ("licence_type", {}).toString();
|
||||
auto status = license.getProperty ("status", {}).toString();
|
||||
|
||||
if (status == "active" && type.isNotEmpty() && version.isNotEmpty())
|
||||
licenses.push_back ({ version.getIntValue(), LicenseHelpers::licenseTypeForString (type) });
|
||||
}
|
||||
|
||||
if (! licenses.empty())
|
||||
{
|
||||
auto bestLicense = LicenseHelpers::findBestLicense (std::move (licenses));
|
||||
|
||||
licenseState.version = bestLicense.first;
|
||||
licenseState.type = bestLicense.second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String userAuthToken;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static String postDataStringAsJSON (const StringPairArray& parameters)
|
||||
{
|
||||
DynamicObject::Ptr d (new DynamicObject());
|
||||
|
||||
for (auto& key : parameters.getAllKeys())
|
||||
d->setProperty (key, parameters[key]);
|
||||
|
||||
return JSON::toString (var (d.get()));
|
||||
}
|
||||
|
||||
static ErrorMessageAndType runTask (std::unique_ptr<AccountEnquiryBase> accountEnquiryTask, LicenseState& state)
|
||||
{
|
||||
const ErrorMessageAndType cancelledError ("Cancelled.", ErrorType::cancelled);
|
||||
const String endpointURL ("https://api.juce.com/api/v1");
|
||||
|
||||
URL url (endpointURL + accountEnquiryTask->getEndpointURLSuffix());
|
||||
|
||||
auto isPOST = accountEnquiryTask->isPOSTLikeRequest();
|
||||
|
||||
if (isPOST)
|
||||
url = url.withPOSTData (postDataStringAsJSON (accountEnquiryTask->getParameterNamesAndValues()));
|
||||
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
int statusCode = 0;
|
||||
auto urlStream = url.createInputStream (URL::InputStreamOptions (isPOST ? URL::ParameterHandling::inPostData
|
||||
: URL::ParameterHandling::inAddress)
|
||||
.withExtraHeaders (accountEnquiryTask->getExtraHeaders())
|
||||
.withConnectionTimeoutMs (5000)
|
||||
.withStatusCode (&statusCode));
|
||||
|
||||
if (urlStream == nullptr)
|
||||
return { "Failed to connect to the web server.", ErrorType::connectionError };
|
||||
|
||||
if (statusCode != accountEnquiryTask->getSuccessCode())
|
||||
return { accountEnquiryTask->errorCodeToString (statusCode), ErrorType::webResponseError };
|
||||
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
String response;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char buffer [8192] = "";
|
||||
auto num = urlStream->read (buffer, sizeof (buffer));
|
||||
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
if (num <= 0)
|
||||
break;
|
||||
|
||||
response += buffer;
|
||||
}
|
||||
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
if (! accountEnquiryTask->parseServerResponse (response, state))
|
||||
return { "Failed to parse server response.", ErrorType::webResponseError };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ThreadPool jobPool { 1 };
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (LicenseQueryThread)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseQueryThread)
|
||||
};
|
||||
|
||||
@@ -1,91 +1,91 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct LicenseState
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
none,
|
||||
gpl,
|
||||
personal,
|
||||
educational,
|
||||
indie,
|
||||
pro
|
||||
};
|
||||
|
||||
LicenseState() = default;
|
||||
|
||||
LicenseState (Type t, int v, String user, String token)
|
||||
: type (t), version (v), username (user), authToken (token)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator== (const LicenseState& other) const noexcept
|
||||
{
|
||||
return type == other.type
|
||||
&& version == other.version
|
||||
&& username == other.username
|
||||
&& authToken == other.authToken;
|
||||
}
|
||||
|
||||
bool operator != (const LicenseState& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
bool isSignedIn() const noexcept { return isGPL() || (version > 0 && username.isNotEmpty()); }
|
||||
bool isOldLicense() const noexcept { return isSignedIn() && version < projucerMajorVersion; }
|
||||
bool isGPL() const noexcept { return type == Type::gpl; }
|
||||
|
||||
bool canUnlockFullFeatures() const noexcept
|
||||
{
|
||||
return isGPL() || (isSignedIn() && ! isOldLicense() && (type == Type::indie || type == Type::pro));
|
||||
}
|
||||
|
||||
String getLicenseTypeString() const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Type::none: return "No license";
|
||||
case Type::gpl: return "GPL";
|
||||
case Type::personal: return "Personal";
|
||||
case Type::educational: return "Educational";
|
||||
case Type::indie: return "Indie";
|
||||
case Type::pro: return "Pro";
|
||||
default: break;
|
||||
};
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
Type type = Type::none;
|
||||
int version = -1;
|
||||
String username, authToken;
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct LicenseState
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
none,
|
||||
gpl,
|
||||
personal,
|
||||
educational,
|
||||
indie,
|
||||
pro
|
||||
};
|
||||
|
||||
LicenseState() = default;
|
||||
|
||||
LicenseState (Type t, int v, String user, String token)
|
||||
: type (t), version (v), username (user), authToken (token)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator== (const LicenseState& other) const noexcept
|
||||
{
|
||||
return type == other.type
|
||||
&& version == other.version
|
||||
&& username == other.username
|
||||
&& authToken == other.authToken;
|
||||
}
|
||||
|
||||
bool operator != (const LicenseState& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
bool isSignedIn() const noexcept { return isGPL() || (version > 0 && username.isNotEmpty()); }
|
||||
bool isOldLicense() const noexcept { return isSignedIn() && version < projucerMajorVersion; }
|
||||
bool isGPL() const noexcept { return type == Type::gpl; }
|
||||
|
||||
bool canUnlockFullFeatures() const noexcept
|
||||
{
|
||||
return isGPL() || (isSignedIn() && ! isOldLicense() && (type == Type::indie || type == Type::pro));
|
||||
}
|
||||
|
||||
String getLicenseTypeString() const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Type::none: return "No license";
|
||||
case Type::gpl: return "GPL";
|
||||
case Type::personal: return "Personal";
|
||||
case Type::educational: return "Educational";
|
||||
case Type::indie: return "Indie";
|
||||
case Type::pro: return "Pro";
|
||||
default: break;
|
||||
};
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
Type type = Type::none;
|
||||
int version = -1;
|
||||
String username, authToken;
|
||||
};
|
||||
|
||||
@@ -1,283 +1,283 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../Project/UI/jucer_UserAvatarComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class LoginFormComponent : public Component
|
||||
{
|
||||
public:
|
||||
LoginFormComponent (MainWindow& window)
|
||||
: mainWindow (window)
|
||||
{
|
||||
setTitle ("Login");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addAndMakeVisible (emailBox);
|
||||
emailBox.setTextToShowWhenEmpty ("Email", Colours::black.withAlpha (0.2f));
|
||||
emailBox.setJustification (Justification::centredLeft);
|
||||
emailBox.onReturnKey = [this] { submitDetails(); };
|
||||
emailBox.setTitle ("Email");
|
||||
|
||||
addAndMakeVisible (passwordBox);
|
||||
passwordBox.setTextToShowWhenEmpty ("Password", Colours::black.withAlpha (0.2f));
|
||||
passwordBox.setPasswordCharacter ((juce_wchar) 0x2022);
|
||||
passwordBox.setJustification (Justification::centredLeft);
|
||||
passwordBox.onReturnKey = [this] { submitDetails(); };
|
||||
passwordBox.setTitle ("Password");
|
||||
|
||||
addAndMakeVisible (logInButton);
|
||||
logInButton.onClick = [this] { submitDetails(); };
|
||||
|
||||
addAndMakeVisible (enableGPLButton);
|
||||
enableGPLButton.onClick = [this]
|
||||
{
|
||||
ProjucerApplication::getApp().getLicenseController().setState (LicenseController::getGPLState());
|
||||
mainWindow.hideLoginFormOverlay();
|
||||
};
|
||||
|
||||
addAndMakeVisible (userAvatar);
|
||||
|
||||
addAndMakeVisible (createAccountLabel);
|
||||
createAccountLabel.setFont (Font (14.0f, Font::underlined));
|
||||
createAccountLabel.addMouseListener (this, false);
|
||||
createAccountLabel.setMouseCursor (MouseCursor::PointingHandCursor);
|
||||
|
||||
addAndMakeVisible (errorMessageLabel);
|
||||
errorMessageLabel.setMinimumHorizontalScale (1.0f);
|
||||
errorMessageLabel.setFont (12.0f);
|
||||
errorMessageLabel.setColour (Label::textColourId, Colours::red);
|
||||
errorMessageLabel.setVisible (false);
|
||||
|
||||
dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false);
|
||||
addAndMakeVisible (dismissButton);
|
||||
dismissButton.onClick = [this] { mainWindow.hideLoginFormOverlay(); };
|
||||
dismissButton.setTitle ("Dismiss");
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
setOpaque (true);
|
||||
|
||||
lookAndFeelChanged();
|
||||
|
||||
setSize (300, 350);
|
||||
}
|
||||
|
||||
~LoginFormComponent() override
|
||||
{
|
||||
ProjucerApplication::getApp().getLicenseController().cancelSignIn();
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (20);
|
||||
auto spacing = bounds.getHeight() / 20;
|
||||
|
||||
userAvatar.setBounds (bounds.removeFromTop (iconHeight).reduced ((bounds.getWidth() / 2) - (iconHeight / 2), 0));
|
||||
|
||||
errorMessageLabel.setBounds (bounds.removeFromTop (spacing));
|
||||
bounds.removeFromTop (spacing / 2);
|
||||
|
||||
auto textEditorHeight = bounds.getHeight() / 5;
|
||||
|
||||
emailBox.setBounds (bounds.removeFromTop (textEditorHeight));
|
||||
bounds.removeFromTop (spacing);
|
||||
|
||||
passwordBox.setBounds (bounds.removeFromTop (textEditorHeight));
|
||||
bounds.removeFromTop (spacing * 2);
|
||||
|
||||
emailBox.setFont (Font ((float) textEditorHeight / 2.5f));
|
||||
passwordBox.setFont (Font ((float) textEditorHeight / 2.5f));
|
||||
|
||||
logInButton.setBounds (bounds.removeFromTop (textEditorHeight));
|
||||
|
||||
auto slice = bounds.removeFromTop (textEditorHeight);
|
||||
createAccountLabel.setBounds (slice.removeFromLeft (createAccountLabel.getFont().getStringWidth (createAccountLabel.getText()) + 5));
|
||||
slice.removeFromLeft (15);
|
||||
enableGPLButton.setBounds (slice.reduced (0, 5));
|
||||
|
||||
dismissButton.setBounds (getLocalBounds().reduced (10).removeFromTop (20).removeFromRight (20));
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId).contrasting (0.1f));
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& event) override
|
||||
{
|
||||
if (event.eventComponent == &createAccountLabel)
|
||||
URL ("https://juce.com/verification/register").launchInDefaultBrowser();
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
enableGPLButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
|
||||
}
|
||||
|
||||
private:
|
||||
class ProgressButton : public TextButton,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
ProgressButton (const String& buttonName)
|
||||
: TextButton (buttonName), text (buttonName)
|
||||
{
|
||||
}
|
||||
|
||||
void setBusy (bool shouldBeBusy)
|
||||
{
|
||||
isInProgress = shouldBeBusy;
|
||||
|
||||
if (isInProgress)
|
||||
{
|
||||
setEnabled (false);
|
||||
setButtonText ({});
|
||||
startTimerHz (30);
|
||||
}
|
||||
else
|
||||
{
|
||||
setEnabled (true);
|
||||
setButtonText (text);
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
TextButton::paint (g);
|
||||
|
||||
if (isInProgress)
|
||||
{
|
||||
auto size = getHeight() - 10;
|
||||
auto halfSize = size / 2;
|
||||
|
||||
getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white,
|
||||
(getWidth() / 2) - halfSize, (getHeight() / 2) - halfSize,
|
||||
size, size);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void timerCallback() override
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
String text;
|
||||
bool isInProgress = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void updateLoginButtonStates (bool isLoggingIn)
|
||||
{
|
||||
logInButton.setBusy (isLoggingIn);
|
||||
|
||||
emailBox.setEnabled (! isLoggingIn);
|
||||
passwordBox.setEnabled (! isLoggingIn);
|
||||
}
|
||||
|
||||
void submitDetails()
|
||||
{
|
||||
auto loginFormError = checkLoginFormsAreValid();
|
||||
|
||||
if (loginFormError.isNotEmpty())
|
||||
{
|
||||
showErrorMessage (loginFormError);
|
||||
return;
|
||||
}
|
||||
|
||||
updateLoginButtonStates (true);
|
||||
|
||||
auto completionCallback = [weakThis = SafePointer<LoginFormComponent> { this }] (const String& errorMessage)
|
||||
{
|
||||
if (weakThis == nullptr)
|
||||
return;
|
||||
|
||||
weakThis->updateLoginButtonStates (false);
|
||||
|
||||
if (errorMessage.isNotEmpty())
|
||||
{
|
||||
weakThis->showErrorMessage (errorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
weakThis->hideErrorMessage();
|
||||
weakThis->mainWindow.hideLoginFormOverlay();
|
||||
ProjucerApplication::getApp().getCommandManager().commandStatusChanged();
|
||||
}
|
||||
};
|
||||
|
||||
ProjucerApplication::getApp().getLicenseController().signIn (emailBox.getText(), passwordBox.getText(),
|
||||
std::move (completionCallback));
|
||||
}
|
||||
|
||||
String checkLoginFormsAreValid() const
|
||||
{
|
||||
auto email = emailBox.getText();
|
||||
|
||||
if (email.isEmpty() || email.indexOfChar ('@') < 0)
|
||||
return "Please enter a valid email.";
|
||||
|
||||
auto password = passwordBox.getText();
|
||||
|
||||
if (password.isEmpty() || password.length() < 8)
|
||||
return "Please enter a valid password.";
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void showErrorMessage (const String& errorMessage)
|
||||
{
|
||||
errorMessageLabel.setText (errorMessage, dontSendNotification);
|
||||
errorMessageLabel.setVisible (true);
|
||||
}
|
||||
|
||||
void hideErrorMessage()
|
||||
{
|
||||
errorMessageLabel.setText ({}, dontSendNotification);
|
||||
errorMessageLabel.setVisible (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int iconHeight = 50;
|
||||
|
||||
MainWindow& mainWindow;
|
||||
|
||||
TextEditor emailBox, passwordBox;
|
||||
ProgressButton logInButton { "Sign In" };
|
||||
TextButton enableGPLButton { "Enable GPL Mode" };
|
||||
ShapeButton dismissButton { {},
|
||||
findColour (treeIconColourId),
|
||||
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)),
|
||||
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) };
|
||||
UserAvatarComponent userAvatar { false };
|
||||
Label createAccountLabel { {}, "Create an account" },
|
||||
errorMessageLabel { {}, {} };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LoginFormComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../Project/UI/jucer_UserAvatarComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class LoginFormComponent : public Component
|
||||
{
|
||||
public:
|
||||
LoginFormComponent (MainWindow& window)
|
||||
: mainWindow (window)
|
||||
{
|
||||
setTitle ("Login");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addAndMakeVisible (emailBox);
|
||||
emailBox.setTextToShowWhenEmpty ("Email", Colours::black.withAlpha (0.2f));
|
||||
emailBox.setJustification (Justification::centredLeft);
|
||||
emailBox.onReturnKey = [this] { submitDetails(); };
|
||||
emailBox.setTitle ("Email");
|
||||
|
||||
addAndMakeVisible (passwordBox);
|
||||
passwordBox.setTextToShowWhenEmpty ("Password", Colours::black.withAlpha (0.2f));
|
||||
passwordBox.setPasswordCharacter ((juce_wchar) 0x2022);
|
||||
passwordBox.setJustification (Justification::centredLeft);
|
||||
passwordBox.onReturnKey = [this] { submitDetails(); };
|
||||
passwordBox.setTitle ("Password");
|
||||
|
||||
addAndMakeVisible (logInButton);
|
||||
logInButton.onClick = [this] { submitDetails(); };
|
||||
|
||||
addAndMakeVisible (enableGPLButton);
|
||||
enableGPLButton.onClick = [this]
|
||||
{
|
||||
ProjucerApplication::getApp().getLicenseController().setState (LicenseController::getGPLState());
|
||||
mainWindow.hideLoginFormOverlay();
|
||||
};
|
||||
|
||||
addAndMakeVisible (userAvatar);
|
||||
|
||||
addAndMakeVisible (createAccountLabel);
|
||||
createAccountLabel.setFont (Font (14.0f, Font::underlined));
|
||||
createAccountLabel.addMouseListener (this, false);
|
||||
createAccountLabel.setMouseCursor (MouseCursor::PointingHandCursor);
|
||||
|
||||
addAndMakeVisible (errorMessageLabel);
|
||||
errorMessageLabel.setMinimumHorizontalScale (1.0f);
|
||||
errorMessageLabel.setFont (12.0f);
|
||||
errorMessageLabel.setColour (Label::textColourId, Colours::red);
|
||||
errorMessageLabel.setVisible (false);
|
||||
|
||||
dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false);
|
||||
addAndMakeVisible (dismissButton);
|
||||
dismissButton.onClick = [this] { mainWindow.hideLoginFormOverlay(); };
|
||||
dismissButton.setTitle ("Dismiss");
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
setOpaque (true);
|
||||
|
||||
lookAndFeelChanged();
|
||||
|
||||
setSize (300, 350);
|
||||
}
|
||||
|
||||
~LoginFormComponent() override
|
||||
{
|
||||
ProjucerApplication::getApp().getLicenseController().cancelSignIn();
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (20);
|
||||
auto spacing = bounds.getHeight() / 20;
|
||||
|
||||
userAvatar.setBounds (bounds.removeFromTop (iconHeight).reduced ((bounds.getWidth() / 2) - (iconHeight / 2), 0));
|
||||
|
||||
errorMessageLabel.setBounds (bounds.removeFromTop (spacing));
|
||||
bounds.removeFromTop (spacing / 2);
|
||||
|
||||
auto textEditorHeight = bounds.getHeight() / 5;
|
||||
|
||||
emailBox.setBounds (bounds.removeFromTop (textEditorHeight));
|
||||
bounds.removeFromTop (spacing);
|
||||
|
||||
passwordBox.setBounds (bounds.removeFromTop (textEditorHeight));
|
||||
bounds.removeFromTop (spacing * 2);
|
||||
|
||||
emailBox.setFont (Font ((float) textEditorHeight / 2.5f));
|
||||
passwordBox.setFont (Font ((float) textEditorHeight / 2.5f));
|
||||
|
||||
logInButton.setBounds (bounds.removeFromTop (textEditorHeight));
|
||||
|
||||
auto slice = bounds.removeFromTop (textEditorHeight);
|
||||
createAccountLabel.setBounds (slice.removeFromLeft (createAccountLabel.getFont().getStringWidth (createAccountLabel.getText()) + 5));
|
||||
slice.removeFromLeft (15);
|
||||
enableGPLButton.setBounds (slice.reduced (0, 5));
|
||||
|
||||
dismissButton.setBounds (getLocalBounds().reduced (10).removeFromTop (20).removeFromRight (20));
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId).contrasting (0.1f));
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& event) override
|
||||
{
|
||||
if (event.eventComponent == &createAccountLabel)
|
||||
URL ("https://juce.com/verification/register").launchInDefaultBrowser();
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
enableGPLButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
|
||||
}
|
||||
|
||||
private:
|
||||
class ProgressButton : public TextButton,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
ProgressButton (const String& buttonName)
|
||||
: TextButton (buttonName), text (buttonName)
|
||||
{
|
||||
}
|
||||
|
||||
void setBusy (bool shouldBeBusy)
|
||||
{
|
||||
isInProgress = shouldBeBusy;
|
||||
|
||||
if (isInProgress)
|
||||
{
|
||||
setEnabled (false);
|
||||
setButtonText ({});
|
||||
startTimerHz (30);
|
||||
}
|
||||
else
|
||||
{
|
||||
setEnabled (true);
|
||||
setButtonText (text);
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
TextButton::paint (g);
|
||||
|
||||
if (isInProgress)
|
||||
{
|
||||
auto size = getHeight() - 10;
|
||||
auto halfSize = size / 2;
|
||||
|
||||
getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white,
|
||||
(getWidth() / 2) - halfSize, (getHeight() / 2) - halfSize,
|
||||
size, size);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void timerCallback() override
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
String text;
|
||||
bool isInProgress = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void updateLoginButtonStates (bool isLoggingIn)
|
||||
{
|
||||
logInButton.setBusy (isLoggingIn);
|
||||
|
||||
emailBox.setEnabled (! isLoggingIn);
|
||||
passwordBox.setEnabled (! isLoggingIn);
|
||||
}
|
||||
|
||||
void submitDetails()
|
||||
{
|
||||
auto loginFormError = checkLoginFormsAreValid();
|
||||
|
||||
if (loginFormError.isNotEmpty())
|
||||
{
|
||||
showErrorMessage (loginFormError);
|
||||
return;
|
||||
}
|
||||
|
||||
updateLoginButtonStates (true);
|
||||
|
||||
auto completionCallback = [weakThis = SafePointer<LoginFormComponent> { this }] (const String& errorMessage)
|
||||
{
|
||||
if (weakThis == nullptr)
|
||||
return;
|
||||
|
||||
weakThis->updateLoginButtonStates (false);
|
||||
|
||||
if (errorMessage.isNotEmpty())
|
||||
{
|
||||
weakThis->showErrorMessage (errorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
weakThis->hideErrorMessage();
|
||||
weakThis->mainWindow.hideLoginFormOverlay();
|
||||
ProjucerApplication::getApp().getCommandManager().commandStatusChanged();
|
||||
}
|
||||
};
|
||||
|
||||
ProjucerApplication::getApp().getLicenseController().signIn (emailBox.getText(), passwordBox.getText(),
|
||||
std::move (completionCallback));
|
||||
}
|
||||
|
||||
String checkLoginFormsAreValid() const
|
||||
{
|
||||
auto email = emailBox.getText();
|
||||
|
||||
if (email.isEmpty() || email.indexOfChar ('@') < 0)
|
||||
return "Please enter a valid email.";
|
||||
|
||||
auto password = passwordBox.getText();
|
||||
|
||||
if (password.isEmpty() || password.length() < 8)
|
||||
return "Please enter a valid password.";
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void showErrorMessage (const String& errorMessage)
|
||||
{
|
||||
errorMessageLabel.setText (errorMessage, dontSendNotification);
|
||||
errorMessageLabel.setVisible (true);
|
||||
}
|
||||
|
||||
void hideErrorMessage()
|
||||
{
|
||||
errorMessageLabel.setText ({}, dontSendNotification);
|
||||
errorMessageLabel.setVisible (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int iconHeight = 50;
|
||||
|
||||
MainWindow& mainWindow;
|
||||
|
||||
TextEditor emailBox, passwordBox;
|
||||
ProgressButton logInButton { "Sign In" };
|
||||
TextButton enableGPLButton { "Enable GPL Mode" };
|
||||
ShapeButton dismissButton { {},
|
||||
findColour (treeIconColourId),
|
||||
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)),
|
||||
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) };
|
||||
UserAvatarComponent userAvatar { false };
|
||||
Label createAccountLabel { {}, "Create an account" },
|
||||
errorMessageLabel { {}, {} };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LoginFormComponent)
|
||||
};
|
||||
|
||||
@@ -1,103 +1,103 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class AboutWindowComponent : public Component
|
||||
{
|
||||
public:
|
||||
AboutWindowComponent()
|
||||
{
|
||||
addAndMakeVisible (titleLabel);
|
||||
titleLabel.setJustificationType (Justification::centred);
|
||||
titleLabel.setFont (Font (35.0f, Font::FontStyleFlags::bold));
|
||||
|
||||
auto buildDate = Time::getCompilationDate();
|
||||
addAndMakeVisible (versionLabel);
|
||||
versionLabel.setText ("JUCE v" + ProjucerApplication::getApp().getApplicationVersion()
|
||||
+ "\nBuild date: " + String (buildDate.getDayOfMonth())
|
||||
+ " " + Time::getMonthName (buildDate.getMonth(), true)
|
||||
+ " " + String (buildDate.getYear()),
|
||||
dontSendNotification);
|
||||
|
||||
versionLabel.setJustificationType (Justification::centred);
|
||||
addAndMakeVisible (copyrightLabel);
|
||||
copyrightLabel.setJustificationType (Justification::centred);
|
||||
|
||||
addAndMakeVisible (aboutButton);
|
||||
aboutButton.setTooltip ( {} );
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
bounds.removeFromBottom (20);
|
||||
|
||||
auto leftSlice = bounds.removeFromLeft (150);
|
||||
auto centreSlice = bounds.withTrimmedRight (150);
|
||||
|
||||
juceLogoBounds = leftSlice.removeFromTop (150).toFloat();
|
||||
juceLogoBounds.setWidth (juceLogoBounds.getWidth() + 100);
|
||||
juceLogoBounds.setHeight (juceLogoBounds.getHeight() + 100);
|
||||
|
||||
auto titleHeight = 40;
|
||||
|
||||
centreSlice.removeFromTop ((centreSlice.getHeight() / 2) - (titleHeight / 2));
|
||||
|
||||
titleLabel.setBounds (centreSlice.removeFromTop (titleHeight));
|
||||
|
||||
centreSlice.removeFromTop (10);
|
||||
versionLabel.setBounds (centreSlice.removeFromTop (40));
|
||||
|
||||
centreSlice.removeFromTop (10);
|
||||
aboutButton.setBounds (centreSlice.removeFromTop (20));
|
||||
|
||||
copyrightLabel.setBounds (getLocalBounds().removeFromBottom (50));
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
if (juceLogo != nullptr)
|
||||
juceLogo->drawWithin (g, juceLogoBounds.translated (-75, -75), RectanglePlacement::centred, 1.0);
|
||||
}
|
||||
|
||||
private:
|
||||
Label titleLabel { "title", "PROJUCER" },
|
||||
versionLabel { "version" },
|
||||
copyrightLabel { "copyright", String (CharPointer_UTF8 ("\xc2\xa9")) + String (" 2020 Raw Material Software Limited") };
|
||||
|
||||
HyperlinkButton aboutButton { "About Us", URL ("https://juce.com") };
|
||||
|
||||
Rectangle<float> juceLogoBounds;
|
||||
|
||||
std::unique_ptr<Drawable> juceLogo { Drawable::createFromImageData (BinaryData::juce_icon_png,
|
||||
BinaryData::juce_icon_pngSize) };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AboutWindowComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class AboutWindowComponent : public Component
|
||||
{
|
||||
public:
|
||||
AboutWindowComponent()
|
||||
{
|
||||
addAndMakeVisible (titleLabel);
|
||||
titleLabel.setJustificationType (Justification::centred);
|
||||
titleLabel.setFont (Font (35.0f, Font::FontStyleFlags::bold));
|
||||
|
||||
auto buildDate = Time::getCompilationDate();
|
||||
addAndMakeVisible (versionLabel);
|
||||
versionLabel.setText ("JUCE v" + ProjucerApplication::getApp().getApplicationVersion()
|
||||
+ "\nBuild date: " + String (buildDate.getDayOfMonth())
|
||||
+ " " + Time::getMonthName (buildDate.getMonth(), true)
|
||||
+ " " + String (buildDate.getYear()),
|
||||
dontSendNotification);
|
||||
|
||||
versionLabel.setJustificationType (Justification::centred);
|
||||
addAndMakeVisible (copyrightLabel);
|
||||
copyrightLabel.setJustificationType (Justification::centred);
|
||||
|
||||
addAndMakeVisible (aboutButton);
|
||||
aboutButton.setTooltip ( {} );
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
bounds.removeFromBottom (20);
|
||||
|
||||
auto leftSlice = bounds.removeFromLeft (150);
|
||||
auto centreSlice = bounds.withTrimmedRight (150);
|
||||
|
||||
juceLogoBounds = leftSlice.removeFromTop (150).toFloat();
|
||||
juceLogoBounds.setWidth (juceLogoBounds.getWidth() + 100);
|
||||
juceLogoBounds.setHeight (juceLogoBounds.getHeight() + 100);
|
||||
|
||||
auto titleHeight = 40;
|
||||
|
||||
centreSlice.removeFromTop ((centreSlice.getHeight() / 2) - (titleHeight / 2));
|
||||
|
||||
titleLabel.setBounds (centreSlice.removeFromTop (titleHeight));
|
||||
|
||||
centreSlice.removeFromTop (10);
|
||||
versionLabel.setBounds (centreSlice.removeFromTop (40));
|
||||
|
||||
centreSlice.removeFromTop (10);
|
||||
aboutButton.setBounds (centreSlice.removeFromTop (20));
|
||||
|
||||
copyrightLabel.setBounds (getLocalBounds().removeFromBottom (50));
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
if (juceLogo != nullptr)
|
||||
juceLogo->drawWithin (g, juceLogoBounds.translated (-75, -75), RectanglePlacement::centred, 1.0);
|
||||
}
|
||||
|
||||
private:
|
||||
Label titleLabel { "title", "PROJUCER" },
|
||||
versionLabel { "version" },
|
||||
copyrightLabel { "copyright", String (CharPointer_UTF8 ("\xc2\xa9")) + String (" 2020 Raw Material Software Limited") };
|
||||
|
||||
HyperlinkButton aboutButton { "About Us", URL ("https://juce.com") };
|
||||
|
||||
Rectangle<float> juceLogoBounds;
|
||||
|
||||
std::unique_ptr<Drawable> juceLogo { Drawable::createFromImageData (BinaryData::juce_icon_png,
|
||||
BinaryData::juce_icon_pngSize) };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AboutWindowComponent)
|
||||
};
|
||||
|
||||
@@ -1,347 +1,347 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/UI/PropertyComponents/jucer_ColourPropertyComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class EditorColourSchemeWindowComponent : public Component
|
||||
{
|
||||
public:
|
||||
EditorColourSchemeWindowComponent()
|
||||
{
|
||||
if (getAppSettings().monospacedFontNames.size() == 0)
|
||||
changeContent (new AppearanceEditor::FontScanPanel());
|
||||
else
|
||||
changeContent (new AppearanceEditor::EditorPanel());
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
content->setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
void changeContent (Component* newContent)
|
||||
{
|
||||
content.reset (newContent);
|
||||
addAndMakeVisible (newContent);
|
||||
content->setBounds (getLocalBounds().reduced (10));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Component> content;
|
||||
|
||||
//==============================================================================
|
||||
struct AppearanceEditor
|
||||
{
|
||||
struct FontScanPanel : public Component,
|
||||
private Timer
|
||||
{
|
||||
FontScanPanel()
|
||||
{
|
||||
fontsToScan = Font::findAllTypefaceNames();
|
||||
startTimer (1);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
g.setFont (14.0f);
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2);
|
||||
|
||||
const auto size = 30;
|
||||
getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
repaint();
|
||||
|
||||
if (fontsToScan.size() == 0)
|
||||
{
|
||||
getAppSettings().monospacedFontNames = fontsFound;
|
||||
|
||||
if (auto* owner = findParentComponentOfClass<EditorColourSchemeWindowComponent>())
|
||||
owner->changeContent (new EditorPanel());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isMonospacedTypeface (fontsToScan[0]))
|
||||
fontsFound.add (fontsToScan[0]);
|
||||
|
||||
fontsToScan.remove (0);
|
||||
}
|
||||
}
|
||||
|
||||
// A rather hacky trick to select only the fixed-pitch fonts..
|
||||
// This is unfortunately a bit slow, but will work on all platforms.
|
||||
static bool isMonospacedTypeface (const String& name)
|
||||
{
|
||||
const Font font (name, 20.0f, Font::plain);
|
||||
|
||||
const auto width = font.getStringWidth ("....");
|
||||
|
||||
return width == font.getStringWidth ("WWWW")
|
||||
&& width == font.getStringWidth ("0000")
|
||||
&& width == font.getStringWidth ("1111")
|
||||
&& width == font.getStringWidth ("iiii");
|
||||
}
|
||||
|
||||
StringArray fontsToScan, fontsFound;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct EditorPanel : public Component
|
||||
{
|
||||
EditorPanel()
|
||||
: loadButton ("Load Scheme..."),
|
||||
saveButton ("Save Scheme...")
|
||||
{
|
||||
rebuildProperties();
|
||||
addAndMakeVisible (panel);
|
||||
|
||||
addAndMakeVisible (loadButton);
|
||||
addAndMakeVisible (saveButton);
|
||||
|
||||
loadButton.onClick = [this] { loadScheme(); };
|
||||
saveButton.onClick = [this] { saveScheme (false); };
|
||||
|
||||
lookAndFeelChanged();
|
||||
|
||||
saveSchemeState();
|
||||
}
|
||||
|
||||
~EditorPanel() override
|
||||
{
|
||||
if (hasSchemeBeenModifiedSinceSave())
|
||||
saveScheme (true);
|
||||
}
|
||||
|
||||
void rebuildProperties()
|
||||
{
|
||||
auto& scheme = getAppSettings().appearance;
|
||||
|
||||
Array<PropertyComponent*> props;
|
||||
auto fontValue = scheme.getCodeFontValue();
|
||||
props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue));
|
||||
props.add (FontSizeValueSource::createProperty ("Font Size", fontValue));
|
||||
|
||||
const auto colourNames = scheme.getColourNames();
|
||||
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
props.add (new ColourPropertyComponent (nullptr, colourNames[i],
|
||||
scheme.getColourValue (colourNames[i]),
|
||||
Colours::white, false));
|
||||
|
||||
panel.clear();
|
||||
panel.addProperties (props);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (10, 2));
|
||||
loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 1));
|
||||
saveButton.setBounds (r.reduced (10, 1));
|
||||
}
|
||||
|
||||
private:
|
||||
PropertyPanel panel;
|
||||
TextButton loadButton, saveButton;
|
||||
|
||||
Font codeFont;
|
||||
Array<var> colourValues;
|
||||
|
||||
void saveScheme (bool isExit)
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Select a file in which to save this colour-scheme...",
|
||||
getAppSettings().appearance.getSchemesFolder()
|
||||
.getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()),
|
||||
AppearanceSettings::getSchemeFileWildCard());
|
||||
auto chooserFlags = FileBrowserComponent::saveMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| FileBrowserComponent::warnAboutOverwriting;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this, isExit] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
{
|
||||
if (isExit)
|
||||
restorePreviousScheme();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix()));
|
||||
getAppSettings().appearance.writeToFile (file);
|
||||
getAppSettings().appearance.refreshPresetSchemeList();
|
||||
|
||||
saveSchemeState();
|
||||
ProjucerApplication::getApp().selectEditorColourSchemeWithName (file.getFileNameWithoutExtension());
|
||||
});
|
||||
}
|
||||
|
||||
void loadScheme()
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Please select a colour-scheme file to load...",
|
||||
getAppSettings().appearance.getSchemesFolder(),
|
||||
AppearanceSettings::getSchemeFileWildCard());
|
||||
auto chooserFlags = FileBrowserComponent::openMode
|
||||
| FileBrowserComponent::canSelectFiles;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
if (getAppSettings().appearance.readFromFile (fc.getResult()))
|
||||
{
|
||||
rebuildProperties();
|
||||
saveSchemeState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
loadButton.setColour (TextButton::buttonColourId,
|
||||
findColour (secondaryButtonBackgroundColourId));
|
||||
}
|
||||
|
||||
void saveSchemeState()
|
||||
{
|
||||
auto& appearance = getAppSettings().appearance;
|
||||
const auto colourNames = appearance.getColourNames();
|
||||
|
||||
codeFont = appearance.getCodeFont();
|
||||
|
||||
colourValues.clear();
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
colourValues.add (appearance.getColourValue (colourNames[i]).getValue());
|
||||
}
|
||||
|
||||
bool hasSchemeBeenModifiedSinceSave()
|
||||
{
|
||||
auto& appearance = getAppSettings().appearance;
|
||||
const auto colourNames = appearance.getColourNames();
|
||||
|
||||
if (codeFont != appearance.getCodeFont())
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
if (colourValues[i] != appearance.getColourValue (colourNames[i]).getValue())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void restorePreviousScheme()
|
||||
{
|
||||
auto& appearance = getAppSettings().appearance;
|
||||
const auto colourNames = appearance.getColourNames();
|
||||
|
||||
appearance.getCodeFontValue().setValue (codeFont.toString());
|
||||
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
appearance.getColourValue (colourNames[i]).setValue (colourValues[i]);
|
||||
}
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (EditorPanel)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FontNameValueSource : public ValueSourceFilter
|
||||
{
|
||||
FontNameValueSource (const Value& source) : ValueSourceFilter (source) {}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return Font::fromString (sourceValue.toString()).getTypefaceName();
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
auto font = Font::fromString (sourceValue.toString());
|
||||
font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName()
|
||||
: newValue.toString());
|
||||
sourceValue = font.toString();
|
||||
}
|
||||
|
||||
static ChoicePropertyComponent* createProperty (const String& title, const Value& value)
|
||||
{
|
||||
auto fontNames = getAppSettings().monospacedFontNames;
|
||||
|
||||
Array<var> values;
|
||||
values.add (Font::getDefaultMonospacedFontName());
|
||||
values.add (var());
|
||||
|
||||
for (int i = 0; i < fontNames.size(); ++i)
|
||||
values.add (fontNames[i]);
|
||||
|
||||
StringArray names;
|
||||
names.add ("<Default Monospaced>");
|
||||
names.add (String());
|
||||
names.addArray (getAppSettings().monospacedFontNames);
|
||||
|
||||
return new ChoicePropertyComponent (Value (new FontNameValueSource (value)),
|
||||
title, names, values);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FontSizeValueSource : public ValueSourceFilter
|
||||
{
|
||||
FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return Font::fromString (sourceValue.toString()).getHeight();
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString();
|
||||
}
|
||||
|
||||
static PropertyComponent* createProperty (const String& title, const Value& value)
|
||||
{
|
||||
return new SliderPropertyComponent (Value (new FontSizeValueSource (value)),
|
||||
title, 5.0, 40.0, 0.1, 0.5);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorColourSchemeWindowComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/UI/PropertyComponents/jucer_ColourPropertyComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class EditorColourSchemeWindowComponent : public Component
|
||||
{
|
||||
public:
|
||||
EditorColourSchemeWindowComponent()
|
||||
{
|
||||
if (getAppSettings().monospacedFontNames.size() == 0)
|
||||
changeContent (new AppearanceEditor::FontScanPanel());
|
||||
else
|
||||
changeContent (new AppearanceEditor::EditorPanel());
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
content->setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
void changeContent (Component* newContent)
|
||||
{
|
||||
content.reset (newContent);
|
||||
addAndMakeVisible (newContent);
|
||||
content->setBounds (getLocalBounds().reduced (10));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Component> content;
|
||||
|
||||
//==============================================================================
|
||||
struct AppearanceEditor
|
||||
{
|
||||
struct FontScanPanel : public Component,
|
||||
private Timer
|
||||
{
|
||||
FontScanPanel()
|
||||
{
|
||||
fontsToScan = Font::findAllTypefaceNames();
|
||||
startTimer (1);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
g.setFont (14.0f);
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2);
|
||||
|
||||
const auto size = 30;
|
||||
getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
repaint();
|
||||
|
||||
if (fontsToScan.size() == 0)
|
||||
{
|
||||
getAppSettings().monospacedFontNames = fontsFound;
|
||||
|
||||
if (auto* owner = findParentComponentOfClass<EditorColourSchemeWindowComponent>())
|
||||
owner->changeContent (new EditorPanel());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isMonospacedTypeface (fontsToScan[0]))
|
||||
fontsFound.add (fontsToScan[0]);
|
||||
|
||||
fontsToScan.remove (0);
|
||||
}
|
||||
}
|
||||
|
||||
// A rather hacky trick to select only the fixed-pitch fonts..
|
||||
// This is unfortunately a bit slow, but will work on all platforms.
|
||||
static bool isMonospacedTypeface (const String& name)
|
||||
{
|
||||
const Font font (name, 20.0f, Font::plain);
|
||||
|
||||
const auto width = font.getStringWidth ("....");
|
||||
|
||||
return width == font.getStringWidth ("WWWW")
|
||||
&& width == font.getStringWidth ("0000")
|
||||
&& width == font.getStringWidth ("1111")
|
||||
&& width == font.getStringWidth ("iiii");
|
||||
}
|
||||
|
||||
StringArray fontsToScan, fontsFound;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct EditorPanel : public Component
|
||||
{
|
||||
EditorPanel()
|
||||
: loadButton ("Load Scheme..."),
|
||||
saveButton ("Save Scheme...")
|
||||
{
|
||||
rebuildProperties();
|
||||
addAndMakeVisible (panel);
|
||||
|
||||
addAndMakeVisible (loadButton);
|
||||
addAndMakeVisible (saveButton);
|
||||
|
||||
loadButton.onClick = [this] { loadScheme(); };
|
||||
saveButton.onClick = [this] { saveScheme (false); };
|
||||
|
||||
lookAndFeelChanged();
|
||||
|
||||
saveSchemeState();
|
||||
}
|
||||
|
||||
~EditorPanel() override
|
||||
{
|
||||
if (hasSchemeBeenModifiedSinceSave())
|
||||
saveScheme (true);
|
||||
}
|
||||
|
||||
void rebuildProperties()
|
||||
{
|
||||
auto& scheme = getAppSettings().appearance;
|
||||
|
||||
Array<PropertyComponent*> props;
|
||||
auto fontValue = scheme.getCodeFontValue();
|
||||
props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue));
|
||||
props.add (FontSizeValueSource::createProperty ("Font Size", fontValue));
|
||||
|
||||
const auto colourNames = scheme.getColourNames();
|
||||
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
props.add (new ColourPropertyComponent (nullptr, colourNames[i],
|
||||
scheme.getColourValue (colourNames[i]),
|
||||
Colours::white, false));
|
||||
|
||||
panel.clear();
|
||||
panel.addProperties (props);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (10, 2));
|
||||
loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 1));
|
||||
saveButton.setBounds (r.reduced (10, 1));
|
||||
}
|
||||
|
||||
private:
|
||||
PropertyPanel panel;
|
||||
TextButton loadButton, saveButton;
|
||||
|
||||
Font codeFont;
|
||||
Array<var> colourValues;
|
||||
|
||||
void saveScheme (bool isExit)
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Select a file in which to save this colour-scheme...",
|
||||
getAppSettings().appearance.getSchemesFolder()
|
||||
.getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()),
|
||||
AppearanceSettings::getSchemeFileWildCard());
|
||||
auto chooserFlags = FileBrowserComponent::saveMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| FileBrowserComponent::warnAboutOverwriting;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this, isExit] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
{
|
||||
if (isExit)
|
||||
restorePreviousScheme();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix()));
|
||||
getAppSettings().appearance.writeToFile (file);
|
||||
getAppSettings().appearance.refreshPresetSchemeList();
|
||||
|
||||
saveSchemeState();
|
||||
ProjucerApplication::getApp().selectEditorColourSchemeWithName (file.getFileNameWithoutExtension());
|
||||
});
|
||||
}
|
||||
|
||||
void loadScheme()
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Please select a colour-scheme file to load...",
|
||||
getAppSettings().appearance.getSchemesFolder(),
|
||||
AppearanceSettings::getSchemeFileWildCard());
|
||||
auto chooserFlags = FileBrowserComponent::openMode
|
||||
| FileBrowserComponent::canSelectFiles;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
if (getAppSettings().appearance.readFromFile (fc.getResult()))
|
||||
{
|
||||
rebuildProperties();
|
||||
saveSchemeState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
loadButton.setColour (TextButton::buttonColourId,
|
||||
findColour (secondaryButtonBackgroundColourId));
|
||||
}
|
||||
|
||||
void saveSchemeState()
|
||||
{
|
||||
auto& appearance = getAppSettings().appearance;
|
||||
const auto colourNames = appearance.getColourNames();
|
||||
|
||||
codeFont = appearance.getCodeFont();
|
||||
|
||||
colourValues.clear();
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
colourValues.add (appearance.getColourValue (colourNames[i]).getValue());
|
||||
}
|
||||
|
||||
bool hasSchemeBeenModifiedSinceSave()
|
||||
{
|
||||
auto& appearance = getAppSettings().appearance;
|
||||
const auto colourNames = appearance.getColourNames();
|
||||
|
||||
if (codeFont != appearance.getCodeFont())
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
if (colourValues[i] != appearance.getColourValue (colourNames[i]).getValue())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void restorePreviousScheme()
|
||||
{
|
||||
auto& appearance = getAppSettings().appearance;
|
||||
const auto colourNames = appearance.getColourNames();
|
||||
|
||||
appearance.getCodeFontValue().setValue (codeFont.toString());
|
||||
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
appearance.getColourValue (colourNames[i]).setValue (colourValues[i]);
|
||||
}
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (EditorPanel)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FontNameValueSource : public ValueSourceFilter
|
||||
{
|
||||
FontNameValueSource (const Value& source) : ValueSourceFilter (source) {}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return Font::fromString (sourceValue.toString()).getTypefaceName();
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
auto font = Font::fromString (sourceValue.toString());
|
||||
font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName()
|
||||
: newValue.toString());
|
||||
sourceValue = font.toString();
|
||||
}
|
||||
|
||||
static ChoicePropertyComponent* createProperty (const String& title, const Value& value)
|
||||
{
|
||||
auto fontNames = getAppSettings().monospacedFontNames;
|
||||
|
||||
Array<var> values;
|
||||
values.add (Font::getDefaultMonospacedFontName());
|
||||
values.add (var());
|
||||
|
||||
for (int i = 0; i < fontNames.size(); ++i)
|
||||
values.add (fontNames[i]);
|
||||
|
||||
StringArray names;
|
||||
names.add ("<Default Monospaced>");
|
||||
names.add (String());
|
||||
names.addArray (getAppSettings().monospacedFontNames);
|
||||
|
||||
return new ChoicePropertyComponent (Value (new FontNameValueSource (value)),
|
||||
title, names, values);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FontSizeValueSource : public ValueSourceFilter
|
||||
{
|
||||
FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return Font::fromString (sourceValue.toString()).getHeight();
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString();
|
||||
}
|
||||
|
||||
static PropertyComponent* createProperty (const String& title, const Value& value)
|
||||
{
|
||||
return new SliderPropertyComponent (Value (new FontSizeValueSource (value)),
|
||||
title, 5.0, 40.0, 0.1, 0.5);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorColourSchemeWindowComponent)
|
||||
};
|
||||
|
||||
@@ -1,89 +1,89 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct FloatingToolWindow : public DialogWindow
|
||||
{
|
||||
FloatingToolWindow (const String& title,
|
||||
const String& windowPosPropertyName,
|
||||
Component* content,
|
||||
std::unique_ptr<Component>& ownerPointer,
|
||||
bool shouldBeResizable,
|
||||
int defaultW, int defaultH,
|
||||
int minW, int minH,
|
||||
int maxW, int maxH)
|
||||
: DialogWindow (title, content->findColour (secondaryBackgroundColourId), true, true),
|
||||
windowPosProperty (windowPosPropertyName),
|
||||
owner (ownerPointer)
|
||||
{
|
||||
setUsingNativeTitleBar (true);
|
||||
setResizable (shouldBeResizable, shouldBeResizable);
|
||||
setResizeLimits (minW, minH, maxW, maxH);
|
||||
setContentOwned (content, false);
|
||||
|
||||
String windowState;
|
||||
if (windowPosProperty.isNotEmpty())
|
||||
windowState = getGlobalProperties().getValue (windowPosProperty);
|
||||
|
||||
if (windowState.isNotEmpty())
|
||||
restoreWindowStateFromString (windowState);
|
||||
else
|
||||
centreAroundComponent (Component::getCurrentlyFocusedComponent(), defaultW, defaultH);
|
||||
|
||||
setVisible (true);
|
||||
owner.reset (this);
|
||||
}
|
||||
|
||||
~FloatingToolWindow() override
|
||||
{
|
||||
if (windowPosProperty.isNotEmpty())
|
||||
getGlobalProperties().setValue (windowPosProperty, getWindowStateAsString());
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
owner.reset();
|
||||
}
|
||||
|
||||
bool escapeKeyPressed() override
|
||||
{
|
||||
closeButtonPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
private:
|
||||
String windowPosProperty;
|
||||
std::unique_ptr<Component>& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FloatingToolWindow)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct FloatingToolWindow : public DialogWindow
|
||||
{
|
||||
FloatingToolWindow (const String& title,
|
||||
const String& windowPosPropertyName,
|
||||
Component* content,
|
||||
std::unique_ptr<Component>& ownerPointer,
|
||||
bool shouldBeResizable,
|
||||
int defaultW, int defaultH,
|
||||
int minW, int minH,
|
||||
int maxW, int maxH)
|
||||
: DialogWindow (title, content->findColour (secondaryBackgroundColourId), true, true),
|
||||
windowPosProperty (windowPosPropertyName),
|
||||
owner (ownerPointer)
|
||||
{
|
||||
setUsingNativeTitleBar (true);
|
||||
setResizable (shouldBeResizable, shouldBeResizable);
|
||||
setResizeLimits (minW, minH, maxW, maxH);
|
||||
setContentOwned (content, false);
|
||||
|
||||
String windowState;
|
||||
if (windowPosProperty.isNotEmpty())
|
||||
windowState = getGlobalProperties().getValue (windowPosProperty);
|
||||
|
||||
if (windowState.isNotEmpty())
|
||||
restoreWindowStateFromString (windowState);
|
||||
else
|
||||
centreAroundComponent (Component::getCurrentlyFocusedComponent(), defaultW, defaultH);
|
||||
|
||||
setVisible (true);
|
||||
owner.reset (this);
|
||||
}
|
||||
|
||||
~FloatingToolWindow() override
|
||||
{
|
||||
if (windowPosProperty.isNotEmpty())
|
||||
getGlobalProperties().setValue (windowPosProperty, getWindowStateAsString());
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
owner.reset();
|
||||
}
|
||||
|
||||
bool escapeKeyPressed() override
|
||||
{
|
||||
closeButtonPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
private:
|
||||
String windowPosProperty;
|
||||
std::unique_ptr<Component>& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FloatingToolWindow)
|
||||
};
|
||||
|
||||
@@ -1,318 +1,315 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/UI/PropertyComponents/jucer_LabelPropertyComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class GlobalPathsWindowComponent : public Component,
|
||||
private Timer,
|
||||
private Value::Listener,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
GlobalPathsWindowComponent()
|
||||
{
|
||||
addChildComponent (rescanJUCEPathButton);
|
||||
rescanJUCEPathButton.onClick = [this]
|
||||
{
|
||||
ProjucerApplication::getApp().rescanJUCEPathModules();
|
||||
lastJUCEModulePath = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get();
|
||||
};
|
||||
|
||||
addChildComponent (rescanUserPathButton);
|
||||
rescanUserPathButton.onClick = [this]
|
||||
{
|
||||
ProjucerApplication::getApp().rescanUserPathModules();
|
||||
lastUserModulePath = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get();
|
||||
};
|
||||
|
||||
addChildComponent (warnAboutJUCEPathButton);
|
||||
warnAboutJUCEPathButton.setToggleState (ProjucerApplication::getApp().shouldPromptUserAboutIncorrectJUCEPath(),
|
||||
dontSendNotification);
|
||||
warnAboutJUCEPathButton.onClick = [this]
|
||||
{
|
||||
ProjucerApplication::getApp().setShouldPromptUserAboutIncorrectJUCEPath (warnAboutJUCEPathButton.getToggleState());
|
||||
};
|
||||
|
||||
getGlobalProperties().addChangeListener (this);
|
||||
|
||||
addAndMakeVisible (resetToDefaultsButton);
|
||||
resetToDefaultsButton.onClick = [this] { resetCurrentOSPathsToDefaults(); };
|
||||
|
||||
addAndMakeVisible (propertyViewport);
|
||||
propertyViewport.setViewedComponent (&propertyGroup, false);
|
||||
|
||||
auto os = TargetOS::getThisOS();
|
||||
|
||||
if (os == TargetOS::osx) selectedOSValue = "osx";
|
||||
else if (os == TargetOS::windows) selectedOSValue = "windows";
|
||||
else if (os == TargetOS::linux) selectedOSValue = "linux";
|
||||
|
||||
selectedOSValue.addListener (this);
|
||||
|
||||
buildProps();
|
||||
|
||||
lastJUCEModulePath = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get();
|
||||
lastUserModulePath = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get();
|
||||
}
|
||||
|
||||
~GlobalPathsWindowComponent() override
|
||||
{
|
||||
getGlobalProperties().removeChangeListener (this);
|
||||
|
||||
auto juceValue = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS());
|
||||
auto userValue = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS());
|
||||
|
||||
if (juceValue.get() != lastJUCEModulePath) ProjucerApplication::getApp().rescanJUCEPathModules();
|
||||
if (userValue.get() != lastUserModulePath) ProjucerApplication::getApp().rescanUserPathModules();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void paintOverChildren (Graphics& g) override
|
||||
{
|
||||
g.setColour (findColour (defaultHighlightColourId).withAlpha (flashAlpha));
|
||||
g.fillRect (boundsToHighlight);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto b = getLocalBounds().reduced (10);
|
||||
|
||||
auto bottomBounds = b.removeFromBottom (80);
|
||||
auto buttonBounds = bottomBounds.removeFromBottom (50);
|
||||
|
||||
rescanJUCEPathButton.setBounds (buttonBounds.removeFromLeft (150).reduced (5, 10));
|
||||
rescanUserPathButton.setBounds (buttonBounds.removeFromLeft (150).reduced (5, 10));
|
||||
|
||||
resetToDefaultsButton.setBounds (buttonBounds.removeFromRight (150).reduced (5, 10));
|
||||
warnAboutJUCEPathButton.setBounds (bottomBounds.reduced (0, 5));
|
||||
warnAboutJUCEPathButton.changeWidthToFitText();
|
||||
|
||||
propertyGroup.updateSize (0, 0, getWidth() - 20 - propertyViewport.getScrollBarThickness());
|
||||
propertyViewport.setBounds (b);
|
||||
}
|
||||
|
||||
void highlightJUCEPath()
|
||||
{
|
||||
if (isTimerRunning() || ! isSelectedOSThisOS())
|
||||
return;
|
||||
|
||||
const auto findJucePathPropertyComponent = [this]() -> PropertyComponent*
|
||||
{
|
||||
for (const auto& prop : propertyGroup.getProperties())
|
||||
if (prop->getName() == "Path to JUCE")
|
||||
return prop.get();
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
if (auto* propComponent = findJucePathPropertyComponent())
|
||||
{
|
||||
boundsToHighlight = getLocalArea (nullptr, propComponent->getScreenBounds());
|
||||
flashAlpha = 0.0f;
|
||||
hasFlashed = false;
|
||||
|
||||
startTimer (25);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void timerCallback() override
|
||||
{
|
||||
flashAlpha += (hasFlashed ? -0.05f : 0.05f);
|
||||
|
||||
if (flashAlpha > 0.75f)
|
||||
{
|
||||
hasFlashed = true;
|
||||
}
|
||||
else if (flashAlpha < 0.0f)
|
||||
{
|
||||
flashAlpha = 0.0f;
|
||||
boundsToHighlight = {};
|
||||
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void valueChanged (Value&) override
|
||||
{
|
||||
buildProps();
|
||||
resized();
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
warnAboutJUCEPathButton.setToggleState (ProjucerApplication::getApp().shouldPromptUserAboutIncorrectJUCEPath(),
|
||||
dontSendNotification);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isSelectedOSThisOS() { return TargetOS::getThisOS() == getSelectedOS(); }
|
||||
|
||||
TargetOS::OS getSelectedOS() const
|
||||
{
|
||||
auto val = selectedOSValue.getValue();
|
||||
|
||||
if (val == "osx") return TargetOS::osx;
|
||||
else if (val == "windows") return TargetOS::windows;
|
||||
else if (val == "linux") return TargetOS::linux;
|
||||
|
||||
jassertfalse;
|
||||
return TargetOS::unknown;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void buildProps()
|
||||
{
|
||||
updateValues();
|
||||
|
||||
PropertyListBuilder builder;
|
||||
auto isThisOS = isSelectedOSThisOS();
|
||||
|
||||
builder.add (new ChoicePropertyComponent (selectedOSValue, "OS", { "OSX", "Windows", "Linux" }, { "osx", "windows", "linux" }),
|
||||
"Use this dropdown to set the global paths for different OSes. "
|
||||
"\nN.B. These paths are stored locally and will only be used when "
|
||||
"saving a project on this machine. Other machines will have their own "
|
||||
"locally stored paths.");
|
||||
|
||||
builder.add (new LabelPropertyComponent ("JUCE"), {});
|
||||
|
||||
builder.add (new FilePathPropertyComponent (jucePathValue, "Path to JUCE", true, isThisOS),
|
||||
"This should be the path to the top-level directory of your JUCE folder. "
|
||||
"This path will be used when searching for the JUCE examples and DemoRunner application.");
|
||||
|
||||
builder.add (new FilePathPropertyComponent (juceModulePathValue, "JUCE Modules", true, isThisOS),
|
||||
String ("This should be the path to the folder containing the JUCE modules that you wish to use, typically the \"modules\" directory of your JUCE folder.")
|
||||
+ (isThisOS ? " Use the button below to re-scan a new path." : ""));
|
||||
builder.add (new FilePathPropertyComponent (userModulePathValue, "User Modules", true, isThisOS),
|
||||
String ("A path to a folder containing any custom modules that you wish to use.")
|
||||
+ (isThisOS ? " Use the button below to re-scan new paths." : ""));
|
||||
|
||||
builder.add (new LabelPropertyComponent ("SDKs"), {});
|
||||
|
||||
builder.add (new FilePathPropertyComponent (vstPathValue, "VST (Legacy) SDK", true, isThisOS),
|
||||
"If you are building a legacy VST plug-in then this path should point to a VST2 SDK. "
|
||||
"The VST2 SDK can be obtained from the vstsdk3610_11_06_2018_build_37 (or older) VST3 SDK or JUCE version 5.3.2. "
|
||||
"You also need a VST2 license from Steinberg to distribute VST2 plug-ins.");
|
||||
|
||||
if (getSelectedOS() != TargetOS::linux)
|
||||
{
|
||||
builder.add (new FilePathPropertyComponent (aaxPathValue, "AAX SDK", true, isThisOS),
|
||||
"If you are building AAX plug-ins, this should be the path to the AAX SDK folder.");
|
||||
builder.add (new FilePathPropertyComponent (rtasPathValue, "RTAS SDK (deprecated)", true, isThisOS),
|
||||
"If you are building RTAS plug-ins, this should be the path to the RTAS SDK folder.");
|
||||
}
|
||||
|
||||
builder.add (new FilePathPropertyComponent (androidSDKPathValue, "Android SDK", true, isThisOS),
|
||||
"This path will be used when writing the local.properties file of an Android project and should point to the Android SDK folder.");
|
||||
|
||||
if (isThisOS)
|
||||
{
|
||||
builder.add (new LabelPropertyComponent ("Other"), {});
|
||||
|
||||
#if JUCE_MAC
|
||||
String exeLabel ("app");
|
||||
#elif JUCE_WINDOWS
|
||||
String exeLabel ("executable");
|
||||
#else
|
||||
String exeLabel ("startup script");
|
||||
#endif
|
||||
|
||||
builder.add (new FilePathPropertyComponent (clionExePathValue, "CLion " + exeLabel, false, isThisOS),
|
||||
"This path will be used for the \"Save Project and Open in IDE...\" option of the CLion exporter.");
|
||||
builder.add (new FilePathPropertyComponent (androidStudioExePathValue, "Android Studio " + exeLabel, false, isThisOS),
|
||||
"This path will be used for the \"Save Project and Open in IDE...\" option of the Android Studio exporter.");
|
||||
}
|
||||
|
||||
rescanJUCEPathButton.setVisible (isThisOS);
|
||||
rescanUserPathButton.setVisible (isThisOS);
|
||||
warnAboutJUCEPathButton.setVisible (isThisOS);
|
||||
|
||||
propertyGroup.setProperties (builder);
|
||||
}
|
||||
|
||||
void updateValues()
|
||||
{
|
||||
auto& settings = getAppSettings();
|
||||
auto os = getSelectedOS();
|
||||
|
||||
jucePathValue = settings.getStoredPath (Ids::jucePath, os);
|
||||
juceModulePathValue = settings.getStoredPath (Ids::defaultJuceModulePath, os);
|
||||
userModulePathValue = settings.getStoredPath (Ids::defaultUserModulePath, os);
|
||||
vstPathValue = settings.getStoredPath (Ids::vstLegacyPath, os);
|
||||
rtasPathValue = settings.getStoredPath (Ids::rtasPath, os);
|
||||
aaxPathValue = settings.getStoredPath (Ids::aaxPath, os);
|
||||
androidSDKPathValue = settings.getStoredPath (Ids::androidSDKPath, os);
|
||||
clionExePathValue = settings.getStoredPath (Ids::clionExePath, os);
|
||||
androidStudioExePathValue = settings.getStoredPath (Ids::androidStudioExePath, os);
|
||||
}
|
||||
|
||||
void resetCurrentOSPathsToDefaults()
|
||||
{
|
||||
jucePathValue .resetToDefault();
|
||||
juceModulePathValue .resetToDefault();
|
||||
userModulePathValue .resetToDefault();
|
||||
vstPathValue .resetToDefault();
|
||||
rtasPathValue .resetToDefault();
|
||||
aaxPathValue .resetToDefault();
|
||||
androidSDKPathValue .resetToDefault();
|
||||
clionExePathValue .resetToDefault();
|
||||
androidStudioExePathValue.resetToDefault();
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Value selectedOSValue;
|
||||
|
||||
ValueWithDefault jucePathValue, juceModulePathValue, userModulePathValue,
|
||||
vstPathValue, rtasPathValue, aaxPathValue, androidSDKPathValue, clionExePathValue, androidStudioExePathValue;
|
||||
|
||||
Viewport propertyViewport;
|
||||
PropertyGroupComponent propertyGroup { "Global Paths", { getIcons().openFolder, Colours::transparentBlack } };
|
||||
|
||||
ToggleButton warnAboutJUCEPathButton { "Warn about incorrect JUCE path" };
|
||||
TextButton rescanJUCEPathButton { "Re-scan JUCE Modules" },
|
||||
rescanUserPathButton { "Re-scan User Modules" },
|
||||
resetToDefaultsButton { "Reset to Defaults" };
|
||||
|
||||
Rectangle<int> boundsToHighlight;
|
||||
float flashAlpha = 0.0f;
|
||||
bool hasFlashed = false;
|
||||
|
||||
var lastJUCEModulePath, lastUserModulePath;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlobalPathsWindowComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/UI/PropertyComponents/jucer_LabelPropertyComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class GlobalPathsWindowComponent : public Component,
|
||||
private Timer,
|
||||
private Value::Listener,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
GlobalPathsWindowComponent()
|
||||
{
|
||||
addChildComponent (rescanJUCEPathButton);
|
||||
rescanJUCEPathButton.onClick = [this]
|
||||
{
|
||||
ProjucerApplication::getApp().rescanJUCEPathModules();
|
||||
lastJUCEModulePath = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get();
|
||||
};
|
||||
|
||||
addChildComponent (rescanUserPathButton);
|
||||
rescanUserPathButton.onClick = [this]
|
||||
{
|
||||
ProjucerApplication::getApp().rescanUserPathModules();
|
||||
lastUserModulePath = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get();
|
||||
};
|
||||
|
||||
addChildComponent (warnAboutJUCEPathButton);
|
||||
warnAboutJUCEPathButton.setToggleState (ProjucerApplication::getApp().shouldPromptUserAboutIncorrectJUCEPath(),
|
||||
dontSendNotification);
|
||||
warnAboutJUCEPathButton.onClick = [this]
|
||||
{
|
||||
ProjucerApplication::getApp().setShouldPromptUserAboutIncorrectJUCEPath (warnAboutJUCEPathButton.getToggleState());
|
||||
};
|
||||
|
||||
getGlobalProperties().addChangeListener (this);
|
||||
|
||||
addAndMakeVisible (resetToDefaultsButton);
|
||||
resetToDefaultsButton.onClick = [this] { resetCurrentOSPathsToDefaults(); };
|
||||
|
||||
addAndMakeVisible (propertyViewport);
|
||||
propertyViewport.setViewedComponent (&propertyGroup, false);
|
||||
|
||||
auto os = TargetOS::getThisOS();
|
||||
|
||||
if (os == TargetOS::osx) selectedOSValue = "osx";
|
||||
else if (os == TargetOS::windows) selectedOSValue = "windows";
|
||||
else if (os == TargetOS::linux) selectedOSValue = "linux";
|
||||
|
||||
selectedOSValue.addListener (this);
|
||||
|
||||
buildProps();
|
||||
|
||||
lastJUCEModulePath = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get();
|
||||
lastUserModulePath = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get();
|
||||
}
|
||||
|
||||
~GlobalPathsWindowComponent() override
|
||||
{
|
||||
getGlobalProperties().removeChangeListener (this);
|
||||
|
||||
auto juceValue = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS());
|
||||
auto userValue = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS());
|
||||
|
||||
if (juceValue.get() != lastJUCEModulePath) ProjucerApplication::getApp().rescanJUCEPathModules();
|
||||
if (userValue.get() != lastUserModulePath) ProjucerApplication::getApp().rescanUserPathModules();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void paintOverChildren (Graphics& g) override
|
||||
{
|
||||
g.setColour (findColour (defaultHighlightColourId).withAlpha (flashAlpha));
|
||||
g.fillRect (boundsToHighlight);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto b = getLocalBounds().reduced (10);
|
||||
|
||||
auto bottomBounds = b.removeFromBottom (80);
|
||||
auto buttonBounds = bottomBounds.removeFromBottom (50);
|
||||
|
||||
rescanJUCEPathButton.setBounds (buttonBounds.removeFromLeft (150).reduced (5, 10));
|
||||
rescanUserPathButton.setBounds (buttonBounds.removeFromLeft (150).reduced (5, 10));
|
||||
|
||||
resetToDefaultsButton.setBounds (buttonBounds.removeFromRight (150).reduced (5, 10));
|
||||
warnAboutJUCEPathButton.setBounds (bottomBounds.reduced (0, 5));
|
||||
warnAboutJUCEPathButton.changeWidthToFitText();
|
||||
|
||||
propertyGroup.updateSize (0, 0, getWidth() - 20 - propertyViewport.getScrollBarThickness());
|
||||
propertyViewport.setBounds (b);
|
||||
}
|
||||
|
||||
void highlightJUCEPath()
|
||||
{
|
||||
if (isTimerRunning() || ! isSelectedOSThisOS())
|
||||
return;
|
||||
|
||||
const auto findJucePathPropertyComponent = [this]() -> PropertyComponent*
|
||||
{
|
||||
for (const auto& prop : propertyGroup.getProperties())
|
||||
if (prop->getName() == "Path to JUCE")
|
||||
return prop.get();
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
if (auto* propComponent = findJucePathPropertyComponent())
|
||||
{
|
||||
boundsToHighlight = getLocalArea (nullptr, propComponent->getScreenBounds());
|
||||
flashAlpha = 0.0f;
|
||||
hasFlashed = false;
|
||||
|
||||
startTimer (25);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void timerCallback() override
|
||||
{
|
||||
flashAlpha += (hasFlashed ? -0.05f : 0.05f);
|
||||
|
||||
if (flashAlpha > 0.75f)
|
||||
{
|
||||
hasFlashed = true;
|
||||
}
|
||||
else if (flashAlpha < 0.0f)
|
||||
{
|
||||
flashAlpha = 0.0f;
|
||||
boundsToHighlight = {};
|
||||
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void valueChanged (Value&) override
|
||||
{
|
||||
buildProps();
|
||||
resized();
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
warnAboutJUCEPathButton.setToggleState (ProjucerApplication::getApp().shouldPromptUserAboutIncorrectJUCEPath(),
|
||||
dontSendNotification);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isSelectedOSThisOS() { return TargetOS::getThisOS() == getSelectedOS(); }
|
||||
|
||||
TargetOS::OS getSelectedOS() const
|
||||
{
|
||||
auto val = selectedOSValue.getValue();
|
||||
|
||||
if (val == "osx") return TargetOS::osx;
|
||||
else if (val == "windows") return TargetOS::windows;
|
||||
else if (val == "linux") return TargetOS::linux;
|
||||
|
||||
jassertfalse;
|
||||
return TargetOS::unknown;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void buildProps()
|
||||
{
|
||||
updateValues();
|
||||
|
||||
PropertyListBuilder builder;
|
||||
auto isThisOS = isSelectedOSThisOS();
|
||||
|
||||
builder.add (new ChoicePropertyComponent (selectedOSValue, "OS", { "OSX", "Windows", "Linux" }, { "osx", "windows", "linux" }),
|
||||
"Use this dropdown to set the global paths for different OSes. "
|
||||
"\nN.B. These paths are stored locally and will only be used when "
|
||||
"saving a project on this machine. Other machines will have their own "
|
||||
"locally stored paths.");
|
||||
|
||||
builder.add (new LabelPropertyComponent ("JUCE"), {});
|
||||
|
||||
builder.add (new FilePathPropertyComponent (jucePathValue, "Path to JUCE", true, isThisOS),
|
||||
"This should be the path to the top-level directory of your JUCE folder. "
|
||||
"This path will be used when searching for the JUCE examples and DemoRunner application.");
|
||||
|
||||
builder.add (new FilePathPropertyComponent (juceModulePathValue, "JUCE Modules", true, isThisOS),
|
||||
String ("This should be the path to the folder containing the JUCE modules that you wish to use, typically the \"modules\" directory of your JUCE folder.")
|
||||
+ (isThisOS ? " Use the button below to re-scan a new path." : ""));
|
||||
builder.add (new FilePathPropertyComponent (userModulePathValue, "User Modules", true, isThisOS),
|
||||
String ("A path to a folder containing any custom modules that you wish to use.")
|
||||
+ (isThisOS ? " Use the button below to re-scan new paths." : ""));
|
||||
|
||||
builder.add (new LabelPropertyComponent ("SDKs"), {});
|
||||
|
||||
builder.add (new FilePathPropertyComponent (vstPathValue, "VST (Legacy) SDK", true, isThisOS),
|
||||
"If you are building a legacy VST plug-in then this path should point to a VST2 SDK. "
|
||||
"The VST2 SDK can be obtained from the vstsdk3610_11_06_2018_build_37 (or older) VST3 SDK or JUCE version 5.3.2. "
|
||||
"You also need a VST2 license from Steinberg to distribute VST2 plug-ins.");
|
||||
builder.add (new FilePathPropertyComponent (araPathValue, "ARA SDK", true, isThisOS),
|
||||
"If you are building ARA enabled plug-ins, this should be the path to the ARA SDK folder.");
|
||||
|
||||
if (getSelectedOS() != TargetOS::linux)
|
||||
{
|
||||
builder.add (new FilePathPropertyComponent (aaxPathValue, "AAX SDK", true, isThisOS),
|
||||
"If you are building AAX plug-ins, this should be the path to the AAX SDK folder.");
|
||||
}
|
||||
|
||||
builder.add (new FilePathPropertyComponent (androidSDKPathValue, "Android SDK", true, isThisOS),
|
||||
"This path will be used when writing the local.properties file of an Android project and should point to the Android SDK folder.");
|
||||
|
||||
if (isThisOS)
|
||||
{
|
||||
builder.add (new LabelPropertyComponent ("Other"), {});
|
||||
|
||||
#if JUCE_MAC
|
||||
String exeLabel ("app");
|
||||
#elif JUCE_WINDOWS
|
||||
String exeLabel ("executable");
|
||||
#else
|
||||
String exeLabel ("startup script");
|
||||
#endif
|
||||
|
||||
builder.add (new FilePathPropertyComponent (androidStudioExePathValue, "Android Studio " + exeLabel, false, isThisOS),
|
||||
"This path will be used for the \"Save Project and Open in IDE...\" option of the Android Studio exporter.");
|
||||
}
|
||||
|
||||
rescanJUCEPathButton.setVisible (isThisOS);
|
||||
rescanUserPathButton.setVisible (isThisOS);
|
||||
warnAboutJUCEPathButton.setVisible (isThisOS);
|
||||
|
||||
propertyGroup.setProperties (builder);
|
||||
}
|
||||
|
||||
void updateValues()
|
||||
{
|
||||
auto& settings = getAppSettings();
|
||||
auto os = getSelectedOS();
|
||||
|
||||
jucePathValue = settings.getStoredPath (Ids::jucePath, os);
|
||||
juceModulePathValue = settings.getStoredPath (Ids::defaultJuceModulePath, os);
|
||||
userModulePathValue = settings.getStoredPath (Ids::defaultUserModulePath, os);
|
||||
vstPathValue = settings.getStoredPath (Ids::vstLegacyPath, os);
|
||||
aaxPathValue = settings.getStoredPath (Ids::aaxPath, os);
|
||||
araPathValue = settings.getStoredPath (Ids::araPath, os);
|
||||
androidSDKPathValue = settings.getStoredPath (Ids::androidSDKPath, os);
|
||||
androidStudioExePathValue = settings.getStoredPath (Ids::androidStudioExePath, os);
|
||||
}
|
||||
|
||||
void resetCurrentOSPathsToDefaults()
|
||||
{
|
||||
jucePathValue .resetToDefault();
|
||||
juceModulePathValue .resetToDefault();
|
||||
userModulePathValue .resetToDefault();
|
||||
vstPathValue .resetToDefault();
|
||||
aaxPathValue .resetToDefault();
|
||||
araPathValue .resetToDefault();
|
||||
androidSDKPathValue .resetToDefault();
|
||||
androidStudioExePathValue.resetToDefault();
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Value selectedOSValue;
|
||||
|
||||
ValueTreePropertyWithDefault jucePathValue, juceModulePathValue, userModulePathValue,
|
||||
vstPathValue, aaxPathValue, araPathValue, androidSDKPathValue,
|
||||
androidStudioExePathValue;
|
||||
|
||||
Viewport propertyViewport;
|
||||
PropertyGroupComponent propertyGroup { "Global Paths", { getIcons().openFolder, Colours::transparentBlack } };
|
||||
|
||||
ToggleButton warnAboutJUCEPathButton { "Warn about incorrect JUCE path" };
|
||||
TextButton rescanJUCEPathButton { "Re-scan JUCE Modules" },
|
||||
rescanUserPathButton { "Re-scan User Modules" },
|
||||
resetToDefaultsButton { "Reset to Defaults" };
|
||||
|
||||
Rectangle<int> boundsToHighlight;
|
||||
float flashAlpha = 0.0f;
|
||||
bool hasFlashed = false;
|
||||
|
||||
var lastJUCEModulePath, lastUserModulePath;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlobalPathsWindowComponent)
|
||||
};
|
||||
|
||||
@@ -1,347 +1,347 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
static String getWidthLimitedStringFromVarArray (const var& varArray) noexcept
|
||||
{
|
||||
if (! varArray.isArray())
|
||||
return {};
|
||||
|
||||
int numLines = 1;
|
||||
const int lineWidth = 100;
|
||||
const String indent (" ");
|
||||
|
||||
String str;
|
||||
if (auto* arr = varArray.getArray())
|
||||
{
|
||||
for (auto& v : *arr)
|
||||
{
|
||||
if ((str.length() + v.toString().length()) > (lineWidth * numLines))
|
||||
{
|
||||
str += newLine;
|
||||
str += indent;
|
||||
|
||||
++numLines;
|
||||
}
|
||||
|
||||
str += v.toString() + (arr->indexOf (v) != arr->size() - 1 ? ", " : "");
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class PIPCreatorWindowComponent : public Component,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
PIPCreatorWindowComponent()
|
||||
{
|
||||
lf.reset (new PIPCreatorLookAndFeel());
|
||||
setLookAndFeel (lf.get());
|
||||
|
||||
addAndMakeVisible (propertyViewport);
|
||||
propertyViewport.setViewedComponent (&propertyGroup, false);
|
||||
buildProps();
|
||||
|
||||
addAndMakeVisible (createButton);
|
||||
createButton.onClick = [this]
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Save PIP File",
|
||||
File::getSpecialLocation (File::SpecialLocationType::userDesktopDirectory)
|
||||
.getChildFile (nameValue.get().toString() + ".h"));
|
||||
auto browserFlags = FileBrowserComponent::saveMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| FileBrowserComponent::warnAboutOverwriting;
|
||||
|
||||
chooser->launchAsync (browserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
const auto result = fc.getResult();
|
||||
|
||||
if (result != File{})
|
||||
createPIPFile (result);
|
||||
});
|
||||
};
|
||||
|
||||
pipTree.addListener (this);
|
||||
}
|
||||
|
||||
~PIPCreatorWindowComponent() override
|
||||
{
|
||||
setLookAndFeel (nullptr);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
createButton.setBounds (bounds.removeFromBottom (50).reduced (100, 10));
|
||||
|
||||
propertyGroup.updateSize (0, 0, getWidth() - propertyViewport.getScrollBarThickness());
|
||||
propertyViewport.setBounds (bounds);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct PIPCreatorLookAndFeel : public ProjucerLookAndFeel
|
||||
{
|
||||
PIPCreatorLookAndFeel() {}
|
||||
|
||||
Rectangle<int> getPropertyComponentContentPosition (PropertyComponent& component)
|
||||
{
|
||||
auto textW = jmin (200, component.getWidth() / 3);
|
||||
return { textW, 0, component.getWidth() - textW, component.getHeight() - 1 };
|
||||
}
|
||||
};
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
lf->setColourScheme (ProjucerApplication::getApp().lookAndFeel.getCurrentColourScheme());
|
||||
lf->setupColours();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void buildProps()
|
||||
{
|
||||
PropertyListBuilder builder;
|
||||
|
||||
builder.add (new TextPropertyComponent (nameValue, "Name", 256, false),
|
||||
"The name of your JUCE project.");
|
||||
builder.add (new TextPropertyComponent (versionValue, "Version", 16, false),
|
||||
"This will be used for the \"Project Version\" field in the Projucer.");
|
||||
builder.add (new TextPropertyComponent (vendorValue, "Vendor", 2048, false),
|
||||
"This will be used for the \"Company Name\" field in the Projucer.");
|
||||
builder.add (new TextPropertyComponent (websiteValue, "Website", 2048, false),
|
||||
"This will be used for the \"Company Website\" field in the Projucer");
|
||||
builder.add (new TextPropertyComponent (descriptionValue, "Description", 2048, true),
|
||||
"A short description of your JUCE project.");
|
||||
|
||||
{
|
||||
Array<var> moduleVars;
|
||||
for (auto& m : getJUCEModules())
|
||||
moduleVars.add (m);
|
||||
|
||||
builder.add (new MultiChoicePropertyComponent (dependenciesValue, "Dependencies",
|
||||
getJUCEModules(), moduleVars),
|
||||
"The JUCE modules that should be added to your project.");
|
||||
}
|
||||
|
||||
{
|
||||
Array<var> exporterVars;
|
||||
StringArray exporterNames;
|
||||
|
||||
for (auto& exporterTypeInfo : ProjectExporter::getExporterTypeInfos())
|
||||
{
|
||||
exporterVars.add (exporterTypeInfo.identifier.toString());
|
||||
exporterNames.add (exporterTypeInfo.displayName);
|
||||
}
|
||||
|
||||
builder.add (new MultiChoicePropertyComponent (exportersValue, "Exporters", exporterNames, exporterVars),
|
||||
"The exporters that should be added to your project.");
|
||||
}
|
||||
|
||||
builder.add (new TextPropertyComponent (moduleFlagsValue, "Module Flags", 2048, true),
|
||||
"Use this to set one, or many, of the JUCE module flags");
|
||||
builder.add (new TextPropertyComponent (definesValue, "Defines", 2048, true),
|
||||
"This sets some global preprocessor definitions for your project. Used to populate the \"Preprocessor Definitions\" field in the Projucer.");
|
||||
builder.add (new ChoicePropertyComponent (typeValue, "Type",
|
||||
{ "Component", "Plugin", "Console Application" },
|
||||
{ "Component", "AudioProcessor", "Console" }),
|
||||
"The project type.");
|
||||
|
||||
builder.add (new TextPropertyComponent (mainClassValue, "Main Class", 2048, false),
|
||||
"The name of the main class that should be instantiated. "
|
||||
"There can only be one main class and it must have a default constructor. "
|
||||
"Depending on the type, this may need to inherit from a specific JUCE class");
|
||||
|
||||
builder.add (new ChoicePropertyComponent (useLocalCopyValue, "Use Local Copy"),
|
||||
"Enable this to specify that the PIP file should be copied to the generated project directory instead of just referred to.");
|
||||
|
||||
propertyGroup.setProperties (builder);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier& identifier) override
|
||||
{
|
||||
if (identifier == Ids::type)
|
||||
{
|
||||
auto type = typeValue.get().toString();
|
||||
|
||||
if (type == "Component")
|
||||
{
|
||||
nameValue.setDefault ("MyComponentPIP");
|
||||
dependenciesValue.setDefault (getModulesRequiredForComponent());
|
||||
mainClassValue.setDefault ("MyComponent");
|
||||
}
|
||||
else if (type == "AudioProcessor")
|
||||
{
|
||||
nameValue.setDefault ("MyPluginPIP");
|
||||
dependenciesValue.setDefault (getModulesRequiredForAudioProcessor());
|
||||
mainClassValue.setDefault ("MyPlugin");
|
||||
}
|
||||
else if (type == "Console")
|
||||
{
|
||||
nameValue.setDefault ("MyConsolePIP");
|
||||
dependenciesValue.setDefault (getModulesRequiredForConsole());
|
||||
mainClassValue.setDefault ({});
|
||||
}
|
||||
|
||||
MessageManager::callAsync ([this]
|
||||
{
|
||||
buildProps();
|
||||
resized();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String getFormattedMetadataString() const noexcept
|
||||
{
|
||||
StringArray metadata;
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
if (nameValue.get().toString().isNotEmpty()) section.add (" name: " + nameValue.get().toString());
|
||||
if (versionValue.get().toString().isNotEmpty()) section.add (" version: " + versionValue.get().toString());
|
||||
if (vendorValue.get().toString().isNotEmpty()) section.add (" vendor: " + vendorValue.get().toString());
|
||||
if (websiteValue.get().toString().isNotEmpty()) section.add (" website: " + websiteValue.get().toString());
|
||||
if (descriptionValue.get().toString().isNotEmpty()) section.add (" description: " + descriptionValue.get().toString());
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
auto dependenciesString = getWidthLimitedStringFromVarArray (dependenciesValue.get());
|
||||
if (dependenciesString.isNotEmpty()) section.add (" dependencies: " + dependenciesString);
|
||||
|
||||
auto exportersString = getWidthLimitedStringFromVarArray (exportersValue.get());
|
||||
if (exportersString.isNotEmpty()) section.add (" exporters: " + exportersString);
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
if (moduleFlagsValue.get().toString().isNotEmpty()) section.add (" moduleFlags: " + moduleFlagsValue.get().toString());
|
||||
if (definesValue.get().toString().isNotEmpty()) section.add (" defines: " + definesValue.get().toString());
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
if (typeValue.get().toString().isNotEmpty()) section.add (" type: " + typeValue.get().toString());
|
||||
if (mainClassValue.get().toString().isNotEmpty()) section.add (" mainClass: " + mainClassValue.get().toString());
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
if (useLocalCopyValue.get()) section.add (" useLocalCopy: " + useLocalCopyValue.get().toString());
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
return metadata.joinIntoString (String (getPreferredLineFeed()) + getPreferredLineFeed());
|
||||
}
|
||||
|
||||
void createPIPFile (File fileToSave)
|
||||
{
|
||||
String fileTemplate (BinaryData::jucer_PIPTemplate_h);
|
||||
fileTemplate = fileTemplate.replace ("%%pip_metadata%%", getFormattedMetadataString());
|
||||
|
||||
auto type = typeValue.get().toString();
|
||||
|
||||
if (type == "Component")
|
||||
{
|
||||
String componentCode (BinaryData::jucer_ContentCompSimpleTemplate_h);
|
||||
componentCode = componentCode.substring (componentCode.indexOf ("class %%content_component_class%%"))
|
||||
.replace ("%%content_component_class%%", mainClassValue.get().toString());
|
||||
|
||||
fileTemplate = fileTemplate.replace ("%%pip_code%%", componentCode);
|
||||
}
|
||||
else if (type == "AudioProcessor")
|
||||
{
|
||||
String audioProcessorCode (BinaryData::jucer_PIPAudioProcessorTemplate_h);
|
||||
audioProcessorCode = audioProcessorCode.replace ("%%class_name%%", mainClassValue.get().toString())
|
||||
.replace ("%%name%%", nameValue.get().toString());
|
||||
|
||||
fileTemplate = fileTemplate.replace ("%%pip_code%%", audioProcessorCode);
|
||||
}
|
||||
else if (type == "Console")
|
||||
{
|
||||
String consoleCode (BinaryData::jucer_MainConsoleAppTemplate_cpp);
|
||||
consoleCode = consoleCode.substring (consoleCode.indexOf ("int main (int argc, char* argv[])"));
|
||||
|
||||
fileTemplate = fileTemplate.replace ("%%pip_code%%", consoleCode);
|
||||
}
|
||||
|
||||
if (fileToSave.create())
|
||||
fileToSave.replaceWithText (fileTemplate);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ValueTree pipTree { "PIPSettings" };
|
||||
ValueWithDefault nameValue { pipTree, Ids::name, nullptr, "MyComponentPIP" },
|
||||
versionValue { pipTree, Ids::version, nullptr },
|
||||
vendorValue { pipTree, Ids::vendor, nullptr },
|
||||
websiteValue { pipTree, Ids::website, nullptr },
|
||||
descriptionValue { pipTree, Ids::description, nullptr },
|
||||
dependenciesValue { pipTree, Ids::dependencies_, nullptr, getModulesRequiredForComponent(), "," },
|
||||
exportersValue { pipTree, Ids::exporters, nullptr, StringArray (ProjectExporter::getCurrentPlatformExporterTypeInfo().identifier.toString()), "," },
|
||||
moduleFlagsValue { pipTree, Ids::moduleFlags, nullptr, "JUCE_STRICT_REFCOUNTEDPOINTER=1" },
|
||||
definesValue { pipTree, Ids::defines, nullptr },
|
||||
typeValue { pipTree, Ids::type, nullptr, "Component" },
|
||||
mainClassValue { pipTree, Ids::mainClass, nullptr, "MyComponent" },
|
||||
useLocalCopyValue { pipTree, Ids::useLocalCopy, nullptr, false };
|
||||
|
||||
std::unique_ptr<PIPCreatorLookAndFeel> lf;
|
||||
|
||||
Viewport propertyViewport;
|
||||
PropertyGroupComponent propertyGroup { "PIP Creator", {} };
|
||||
|
||||
TextButton createButton { "Create PIP" };
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PIPCreatorWindowComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
static String getWidthLimitedStringFromVarArray (const var& varArray) noexcept
|
||||
{
|
||||
if (! varArray.isArray())
|
||||
return {};
|
||||
|
||||
int numLines = 1;
|
||||
const int lineWidth = 100;
|
||||
const String indent (" ");
|
||||
|
||||
String str;
|
||||
if (auto* arr = varArray.getArray())
|
||||
{
|
||||
for (auto& v : *arr)
|
||||
{
|
||||
if ((str.length() + v.toString().length()) > (lineWidth * numLines))
|
||||
{
|
||||
str += newLine;
|
||||
str += indent;
|
||||
|
||||
++numLines;
|
||||
}
|
||||
|
||||
str += v.toString() + (arr->indexOf (v) != arr->size() - 1 ? ", " : "");
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class PIPCreatorWindowComponent : public Component,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
PIPCreatorWindowComponent()
|
||||
{
|
||||
lf.reset (new PIPCreatorLookAndFeel());
|
||||
setLookAndFeel (lf.get());
|
||||
|
||||
addAndMakeVisible (propertyViewport);
|
||||
propertyViewport.setViewedComponent (&propertyGroup, false);
|
||||
buildProps();
|
||||
|
||||
addAndMakeVisible (createButton);
|
||||
createButton.onClick = [this]
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Save PIP File",
|
||||
File::getSpecialLocation (File::SpecialLocationType::userDesktopDirectory)
|
||||
.getChildFile (nameValue.get().toString() + ".h"));
|
||||
auto browserFlags = FileBrowserComponent::saveMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| FileBrowserComponent::warnAboutOverwriting;
|
||||
|
||||
chooser->launchAsync (browserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
const auto result = fc.getResult();
|
||||
|
||||
if (result != File{})
|
||||
createPIPFile (result);
|
||||
});
|
||||
};
|
||||
|
||||
pipTree.addListener (this);
|
||||
}
|
||||
|
||||
~PIPCreatorWindowComponent() override
|
||||
{
|
||||
setLookAndFeel (nullptr);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
createButton.setBounds (bounds.removeFromBottom (50).reduced (100, 10));
|
||||
|
||||
propertyGroup.updateSize (0, 0, getWidth() - propertyViewport.getScrollBarThickness());
|
||||
propertyViewport.setBounds (bounds);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct PIPCreatorLookAndFeel : public ProjucerLookAndFeel
|
||||
{
|
||||
PIPCreatorLookAndFeel() {}
|
||||
|
||||
Rectangle<int> getPropertyComponentContentPosition (PropertyComponent& component)
|
||||
{
|
||||
auto textW = jmin (200, component.getWidth() / 3);
|
||||
return { textW, 0, component.getWidth() - textW, component.getHeight() - 1 };
|
||||
}
|
||||
};
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
lf->setColourScheme (ProjucerApplication::getApp().lookAndFeel.getCurrentColourScheme());
|
||||
lf->setupColours();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void buildProps()
|
||||
{
|
||||
PropertyListBuilder builder;
|
||||
|
||||
builder.add (new TextPropertyComponent (nameValue, "Name", 256, false),
|
||||
"The name of your JUCE project.");
|
||||
builder.add (new TextPropertyComponent (versionValue, "Version", 16, false),
|
||||
"This will be used for the \"Project Version\" field in the Projucer.");
|
||||
builder.add (new TextPropertyComponent (vendorValue, "Vendor", 2048, false),
|
||||
"This will be used for the \"Company Name\" field in the Projucer.");
|
||||
builder.add (new TextPropertyComponent (websiteValue, "Website", 2048, false),
|
||||
"This will be used for the \"Company Website\" field in the Projucer");
|
||||
builder.add (new TextPropertyComponent (descriptionValue, "Description", 2048, true),
|
||||
"A short description of your JUCE project.");
|
||||
|
||||
{
|
||||
Array<var> moduleVars;
|
||||
for (auto& m : getJUCEModules())
|
||||
moduleVars.add (m);
|
||||
|
||||
builder.add (new MultiChoicePropertyComponent (dependenciesValue, "Dependencies",
|
||||
getJUCEModules(), moduleVars),
|
||||
"The JUCE modules that should be added to your project.");
|
||||
}
|
||||
|
||||
{
|
||||
Array<var> exporterVars;
|
||||
StringArray exporterNames;
|
||||
|
||||
for (auto& exporterTypeInfo : ProjectExporter::getExporterTypeInfos())
|
||||
{
|
||||
exporterVars.add (exporterTypeInfo.identifier.toString());
|
||||
exporterNames.add (exporterTypeInfo.displayName);
|
||||
}
|
||||
|
||||
builder.add (new MultiChoicePropertyComponent (exportersValue, "Exporters", exporterNames, exporterVars),
|
||||
"The exporters that should be added to your project.");
|
||||
}
|
||||
|
||||
builder.add (new TextPropertyComponent (moduleFlagsValue, "Module Flags", 2048, true),
|
||||
"Use this to set one, or many, of the JUCE module flags");
|
||||
builder.add (new TextPropertyComponent (definesValue, "Defines", 2048, true),
|
||||
"This sets some global preprocessor definitions for your project. Used to populate the \"Preprocessor Definitions\" field in the Projucer.");
|
||||
builder.add (new ChoicePropertyComponent (typeValue, "Type",
|
||||
{ "Component", "Plugin", "Console Application" },
|
||||
{ "Component", "AudioProcessor", "Console" }),
|
||||
"The project type.");
|
||||
|
||||
builder.add (new TextPropertyComponent (mainClassValue, "Main Class", 2048, false),
|
||||
"The name of the main class that should be instantiated. "
|
||||
"There can only be one main class and it must have a default constructor. "
|
||||
"Depending on the type, this may need to inherit from a specific JUCE class");
|
||||
|
||||
builder.add (new ChoicePropertyComponent (useLocalCopyValue, "Use Local Copy"),
|
||||
"Enable this to specify that the PIP file should be copied to the generated project directory instead of just referred to.");
|
||||
|
||||
propertyGroup.setProperties (builder);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier& identifier) override
|
||||
{
|
||||
if (identifier == Ids::type)
|
||||
{
|
||||
auto type = typeValue.get().toString();
|
||||
|
||||
if (type == "Component")
|
||||
{
|
||||
nameValue.setDefault ("MyComponentPIP");
|
||||
dependenciesValue.setDefault (getModulesRequiredForComponent());
|
||||
mainClassValue.setDefault ("MyComponent");
|
||||
}
|
||||
else if (type == "AudioProcessor")
|
||||
{
|
||||
nameValue.setDefault ("MyPluginPIP");
|
||||
dependenciesValue.setDefault (getModulesRequiredForAudioProcessor());
|
||||
mainClassValue.setDefault ("MyPlugin");
|
||||
}
|
||||
else if (type == "Console")
|
||||
{
|
||||
nameValue.setDefault ("MyConsolePIP");
|
||||
dependenciesValue.setDefault (getModulesRequiredForConsole());
|
||||
mainClassValue.setDefault ({});
|
||||
}
|
||||
|
||||
MessageManager::callAsync ([this]
|
||||
{
|
||||
buildProps();
|
||||
resized();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String getFormattedMetadataString() const noexcept
|
||||
{
|
||||
StringArray metadata;
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
if (nameValue.get().toString().isNotEmpty()) section.add (" name: " + nameValue.get().toString());
|
||||
if (versionValue.get().toString().isNotEmpty()) section.add (" version: " + versionValue.get().toString());
|
||||
if (vendorValue.get().toString().isNotEmpty()) section.add (" vendor: " + vendorValue.get().toString());
|
||||
if (websiteValue.get().toString().isNotEmpty()) section.add (" website: " + websiteValue.get().toString());
|
||||
if (descriptionValue.get().toString().isNotEmpty()) section.add (" description: " + descriptionValue.get().toString());
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
auto dependenciesString = getWidthLimitedStringFromVarArray (dependenciesValue.get());
|
||||
if (dependenciesString.isNotEmpty()) section.add (" dependencies: " + dependenciesString);
|
||||
|
||||
auto exportersString = getWidthLimitedStringFromVarArray (exportersValue.get());
|
||||
if (exportersString.isNotEmpty()) section.add (" exporters: " + exportersString);
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
if (moduleFlagsValue.get().toString().isNotEmpty()) section.add (" moduleFlags: " + moduleFlagsValue.get().toString());
|
||||
if (definesValue.get().toString().isNotEmpty()) section.add (" defines: " + definesValue.get().toString());
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
if (typeValue.get().toString().isNotEmpty()) section.add (" type: " + typeValue.get().toString());
|
||||
if (mainClassValue.get().toString().isNotEmpty()) section.add (" mainClass: " + mainClassValue.get().toString());
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
{
|
||||
StringArray section;
|
||||
|
||||
if (useLocalCopyValue.get()) section.add (" useLocalCopy: " + useLocalCopyValue.get().toString());
|
||||
|
||||
if (! section.isEmpty())
|
||||
metadata.add (section.joinIntoString (getPreferredLineFeed()));
|
||||
}
|
||||
|
||||
return metadata.joinIntoString (String (getPreferredLineFeed()) + getPreferredLineFeed());
|
||||
}
|
||||
|
||||
void createPIPFile (File fileToSave)
|
||||
{
|
||||
String fileTemplate (BinaryData::jucer_PIPTemplate_h);
|
||||
fileTemplate = fileTemplate.replace ("%%pip_metadata%%", getFormattedMetadataString());
|
||||
|
||||
auto type = typeValue.get().toString();
|
||||
|
||||
if (type == "Component")
|
||||
{
|
||||
String componentCode (BinaryData::jucer_ContentCompSimpleTemplate_h);
|
||||
componentCode = componentCode.substring (componentCode.indexOf ("class %%content_component_class%%"))
|
||||
.replace ("%%content_component_class%%", mainClassValue.get().toString());
|
||||
|
||||
fileTemplate = fileTemplate.replace ("%%pip_code%%", componentCode);
|
||||
}
|
||||
else if (type == "AudioProcessor")
|
||||
{
|
||||
String audioProcessorCode (BinaryData::jucer_PIPAudioProcessorTemplate_h);
|
||||
audioProcessorCode = audioProcessorCode.replace ("%%class_name%%", mainClassValue.get().toString())
|
||||
.replace ("%%name%%", nameValue.get().toString());
|
||||
|
||||
fileTemplate = fileTemplate.replace ("%%pip_code%%", audioProcessorCode);
|
||||
}
|
||||
else if (type == "Console")
|
||||
{
|
||||
String consoleCode (BinaryData::jucer_MainConsoleAppTemplate_cpp);
|
||||
consoleCode = consoleCode.substring (consoleCode.indexOf ("int main (int argc, char* argv[])"));
|
||||
|
||||
fileTemplate = fileTemplate.replace ("%%pip_code%%", consoleCode);
|
||||
}
|
||||
|
||||
if (fileToSave.create())
|
||||
fileToSave.replaceWithText (fileTemplate);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ValueTree pipTree { "PIPSettings" };
|
||||
ValueTreePropertyWithDefault nameValue { pipTree, Ids::name, nullptr, "MyComponentPIP" },
|
||||
versionValue { pipTree, Ids::version, nullptr },
|
||||
vendorValue { pipTree, Ids::vendor, nullptr },
|
||||
websiteValue { pipTree, Ids::website, nullptr },
|
||||
descriptionValue { pipTree, Ids::description, nullptr },
|
||||
dependenciesValue { pipTree, Ids::dependencies_, nullptr, getModulesRequiredForComponent(), "," },
|
||||
exportersValue { pipTree, Ids::exporters, nullptr, StringArray (ProjectExporter::getCurrentPlatformExporterTypeInfo().identifier.toString()), "," },
|
||||
moduleFlagsValue { pipTree, Ids::moduleFlags, nullptr, "JUCE_STRICT_REFCOUNTEDPOINTER=1" },
|
||||
definesValue { pipTree, Ids::defines, nullptr },
|
||||
typeValue { pipTree, Ids::type, nullptr, "Component" },
|
||||
mainClassValue { pipTree, Ids::mainClass, nullptr, "MyComponent" },
|
||||
useLocalCopyValue { pipTree, Ids::useLocalCopy, nullptr, false };
|
||||
|
||||
std::unique_ptr<PIPCreatorLookAndFeel> lf;
|
||||
|
||||
Viewport propertyViewport;
|
||||
PropertyGroupComponent propertyGroup { "PIP Creator", {} };
|
||||
|
||||
TextButton createButton { "Create PIP" };
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PIPCreatorWindowComponent)
|
||||
};
|
||||
|
||||
@@ -1,218 +1,218 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class SVGPathDataComponent : public Component,
|
||||
public FileDragAndDropTarget
|
||||
|
||||
{
|
||||
public:
|
||||
SVGPathDataComponent()
|
||||
{
|
||||
desc.setJustificationType (Justification::centred);
|
||||
addAndMakeVisible (desc);
|
||||
|
||||
userText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
userText.setMultiLine (true, true);
|
||||
userText.setReturnKeyStartsNewLine (true);
|
||||
addAndMakeVisible (userText);
|
||||
userText.onTextChange = [this] { update(); };
|
||||
userText.onEscapeKey = [this] { getTopLevelComponent()->exitModalState (0); };
|
||||
|
||||
resultText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
resultText.setMultiLine (true, true);
|
||||
resultText.setReadOnly (true);
|
||||
resultText.setSelectAllWhenFocused (true);
|
||||
addAndMakeVisible (resultText);
|
||||
|
||||
userText.setText (getLastText());
|
||||
|
||||
addAndMakeVisible (copyButton);
|
||||
copyButton.onClick = [this] { SystemClipboard::copyTextToClipboard (resultText.getText()); };
|
||||
|
||||
addAndMakeVisible (closeSubPathButton);
|
||||
closeSubPathButton.onClick = [this] { update(); };
|
||||
closeSubPathButton.setToggleState (true, NotificationType::dontSendNotification);
|
||||
|
||||
addAndMakeVisible (fillPathButton);
|
||||
fillPathButton.onClick = [this] { update(); };
|
||||
fillPathButton.setToggleState (true, NotificationType::dontSendNotification);
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
getLastText() = userText.getText();
|
||||
auto text = getLastText().trim().unquoted().trim();
|
||||
|
||||
path = Drawable::parseSVGPath (text);
|
||||
|
||||
if (path.isEmpty())
|
||||
path = pathFromPoints (text);
|
||||
|
||||
String result = "No path generated.. Not a valid SVG path string?";
|
||||
|
||||
if (! path.isEmpty())
|
||||
{
|
||||
MemoryOutputStream data;
|
||||
path.writePathToStream (data);
|
||||
|
||||
MemoryOutputStream out;
|
||||
|
||||
out << "static const unsigned char pathData[] = ";
|
||||
build_tools::writeDataAsCppLiteral (data.getMemoryBlock(), out, false, true);
|
||||
out << newLine
|
||||
<< newLine
|
||||
<< "Path path;" << newLine
|
||||
<< "path.loadPathFromData (pathData, sizeof (pathData));" << newLine;
|
||||
|
||||
result = out.toString();
|
||||
}
|
||||
|
||||
resultText.setText (result, false);
|
||||
repaint (previewPathArea);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds().reduced (8);
|
||||
|
||||
auto bottomSection = r.removeFromBottom (30);
|
||||
copyButton.setBounds (bottomSection.removeFromLeft (50));
|
||||
bottomSection.removeFromLeft (25);
|
||||
fillPathButton.setBounds (bottomSection.removeFromLeft (bottomSection.getWidth() / 2));
|
||||
closeSubPathButton.setBounds (bottomSection);
|
||||
|
||||
r.removeFromBottom (5);
|
||||
desc.setBounds (r.removeFromTop (44));
|
||||
r.removeFromTop (8);
|
||||
userText.setBounds (r.removeFromTop (r.getHeight() / 2));
|
||||
r.removeFromTop (8);
|
||||
previewPathArea = r.removeFromRight (r.getHeight());
|
||||
resultText.setBounds (r);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (dragOver)
|
||||
{
|
||||
g.setColour (findColour (secondaryBackgroundColourId).brighter());
|
||||
g.fillAll();
|
||||
}
|
||||
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
path.applyTransform (path.getTransformToScaleToFit (previewPathArea.reduced (4).toFloat(), true));
|
||||
|
||||
if (fillPathButton.getToggleState())
|
||||
g.fillPath (path);
|
||||
else
|
||||
g.strokePath (path, PathStrokeType (2.0f));
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
userText.applyFontToAllText (userText.getFont());
|
||||
resultText.applyFontToAllText (resultText.getFont());
|
||||
}
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& files) override
|
||||
{
|
||||
return files.size() == 1
|
||||
&& File (files[0]).hasFileExtension ("svg");
|
||||
}
|
||||
|
||||
void fileDragEnter (const StringArray&, int, int) override
|
||||
{
|
||||
dragOver = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void fileDragExit (const StringArray&) override
|
||||
{
|
||||
dragOver = false;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void filesDropped (const StringArray& files, int, int) override
|
||||
{
|
||||
dragOver = false;
|
||||
repaint();
|
||||
|
||||
if (auto element = parseXML (File (files[0])))
|
||||
{
|
||||
if (auto* ePath = element->getChildByName ("path"))
|
||||
userText.setText (ePath->getStringAttribute ("d"), true);
|
||||
else if (auto* ePolygon = element->getChildByName ("polygon"))
|
||||
userText.setText (ePolygon->getStringAttribute ("points"), true);
|
||||
}
|
||||
}
|
||||
|
||||
Path pathFromPoints (String pointsText)
|
||||
{
|
||||
auto points = StringArray::fromTokens (pointsText, " ,", "");
|
||||
points.removeEmptyStrings();
|
||||
|
||||
jassert (points.size() % 2 == 0);
|
||||
|
||||
Path p;
|
||||
|
||||
for (int i = 0; i < points.size() / 2; i++)
|
||||
{
|
||||
auto x = points[i * 2].getFloatValue();
|
||||
auto y = points[i * 2 + 1].getFloatValue();
|
||||
|
||||
if (i == 0)
|
||||
p.startNewSubPath ({ x, y });
|
||||
else
|
||||
p.lineTo ({ x, y });
|
||||
}
|
||||
|
||||
if (closeSubPathButton.getToggleState())
|
||||
p.closeSubPath();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private:
|
||||
Label desc { {}, "Paste an SVG path string into the top box, and it'll be converted to some C++ "
|
||||
"code that will load it as a Path object.." };
|
||||
TextButton copyButton { "Copy" };
|
||||
TextEditor userText, resultText;
|
||||
|
||||
ToggleButton closeSubPathButton { "Close sub-path" };
|
||||
ToggleButton fillPathButton { "Fill path" };
|
||||
|
||||
Rectangle<int> previewPathArea;
|
||||
Path path;
|
||||
bool dragOver = false;
|
||||
|
||||
String& getLastText()
|
||||
{
|
||||
static String t;
|
||||
return t;
|
||||
}
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class SVGPathDataComponent : public Component,
|
||||
public FileDragAndDropTarget
|
||||
|
||||
{
|
||||
public:
|
||||
SVGPathDataComponent()
|
||||
{
|
||||
desc.setJustificationType (Justification::centred);
|
||||
addAndMakeVisible (desc);
|
||||
|
||||
userText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
userText.setMultiLine (true, true);
|
||||
userText.setReturnKeyStartsNewLine (true);
|
||||
addAndMakeVisible (userText);
|
||||
userText.onTextChange = [this] { update(); };
|
||||
userText.onEscapeKey = [this] { getTopLevelComponent()->exitModalState (0); };
|
||||
|
||||
resultText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
resultText.setMultiLine (true, true);
|
||||
resultText.setReadOnly (true);
|
||||
resultText.setSelectAllWhenFocused (true);
|
||||
addAndMakeVisible (resultText);
|
||||
|
||||
userText.setText (getLastText());
|
||||
|
||||
addAndMakeVisible (copyButton);
|
||||
copyButton.onClick = [this] { SystemClipboard::copyTextToClipboard (resultText.getText()); };
|
||||
|
||||
addAndMakeVisible (closeSubPathButton);
|
||||
closeSubPathButton.onClick = [this] { update(); };
|
||||
closeSubPathButton.setToggleState (true, NotificationType::dontSendNotification);
|
||||
|
||||
addAndMakeVisible (fillPathButton);
|
||||
fillPathButton.onClick = [this] { update(); };
|
||||
fillPathButton.setToggleState (true, NotificationType::dontSendNotification);
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
getLastText() = userText.getText();
|
||||
auto text = getLastText().trim().unquoted().trim();
|
||||
|
||||
path = Drawable::parseSVGPath (text);
|
||||
|
||||
if (path.isEmpty())
|
||||
path = pathFromPoints (text);
|
||||
|
||||
String result = "No path generated.. Not a valid SVG path string?";
|
||||
|
||||
if (! path.isEmpty())
|
||||
{
|
||||
MemoryOutputStream data;
|
||||
path.writePathToStream (data);
|
||||
|
||||
MemoryOutputStream out;
|
||||
|
||||
out << "static const unsigned char pathData[] = ";
|
||||
build_tools::writeDataAsCppLiteral (data.getMemoryBlock(), out, false, true);
|
||||
out << newLine
|
||||
<< newLine
|
||||
<< "Path path;" << newLine
|
||||
<< "path.loadPathFromData (pathData, sizeof (pathData));" << newLine;
|
||||
|
||||
result = out.toString();
|
||||
}
|
||||
|
||||
resultText.setText (result, false);
|
||||
repaint (previewPathArea);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds().reduced (8);
|
||||
|
||||
auto bottomSection = r.removeFromBottom (30);
|
||||
copyButton.setBounds (bottomSection.removeFromLeft (50));
|
||||
bottomSection.removeFromLeft (25);
|
||||
fillPathButton.setBounds (bottomSection.removeFromLeft (bottomSection.getWidth() / 2));
|
||||
closeSubPathButton.setBounds (bottomSection);
|
||||
|
||||
r.removeFromBottom (5);
|
||||
desc.setBounds (r.removeFromTop (44));
|
||||
r.removeFromTop (8);
|
||||
userText.setBounds (r.removeFromTop (r.getHeight() / 2));
|
||||
r.removeFromTop (8);
|
||||
previewPathArea = r.removeFromRight (r.getHeight());
|
||||
resultText.setBounds (r);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (dragOver)
|
||||
{
|
||||
g.setColour (findColour (secondaryBackgroundColourId).brighter());
|
||||
g.fillAll();
|
||||
}
|
||||
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
path.applyTransform (path.getTransformToScaleToFit (previewPathArea.reduced (4).toFloat(), true));
|
||||
|
||||
if (fillPathButton.getToggleState())
|
||||
g.fillPath (path);
|
||||
else
|
||||
g.strokePath (path, PathStrokeType (2.0f));
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
userText.applyFontToAllText (userText.getFont());
|
||||
resultText.applyFontToAllText (resultText.getFont());
|
||||
}
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& files) override
|
||||
{
|
||||
return files.size() == 1
|
||||
&& File (files[0]).hasFileExtension ("svg");
|
||||
}
|
||||
|
||||
void fileDragEnter (const StringArray&, int, int) override
|
||||
{
|
||||
dragOver = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void fileDragExit (const StringArray&) override
|
||||
{
|
||||
dragOver = false;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void filesDropped (const StringArray& files, int, int) override
|
||||
{
|
||||
dragOver = false;
|
||||
repaint();
|
||||
|
||||
if (auto element = parseXML (File (files[0])))
|
||||
{
|
||||
if (auto* ePath = element->getChildByName ("path"))
|
||||
userText.setText (ePath->getStringAttribute ("d"), true);
|
||||
else if (auto* ePolygon = element->getChildByName ("polygon"))
|
||||
userText.setText (ePolygon->getStringAttribute ("points"), true);
|
||||
}
|
||||
}
|
||||
|
||||
Path pathFromPoints (String pointsText)
|
||||
{
|
||||
auto points = StringArray::fromTokens (pointsText, " ,", "");
|
||||
points.removeEmptyStrings();
|
||||
|
||||
jassert (points.size() % 2 == 0);
|
||||
|
||||
Path p;
|
||||
|
||||
for (int i = 0; i < points.size() / 2; i++)
|
||||
{
|
||||
auto x = points[i * 2].getFloatValue();
|
||||
auto y = points[i * 2 + 1].getFloatValue();
|
||||
|
||||
if (i == 0)
|
||||
p.startNewSubPath ({ x, y });
|
||||
else
|
||||
p.lineTo ({ x, y });
|
||||
}
|
||||
|
||||
if (closeSubPathButton.getToggleState())
|
||||
p.closeSubPath();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private:
|
||||
Label desc { {}, "Paste an SVG path string into the top box, and it'll be converted to some C++ "
|
||||
"code that will load it as a Path object.." };
|
||||
TextButton copyButton { "Copy" };
|
||||
TextEditor userText, resultText;
|
||||
|
||||
ToggleButton closeSubPathButton { "Close sub-path" };
|
||||
ToggleButton fillPathButton { "Fill path" };
|
||||
|
||||
Rectangle<int> previewPathArea;
|
||||
Path path;
|
||||
bool dragOver = false;
|
||||
|
||||
String& getLastText()
|
||||
{
|
||||
static String t;
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,198 +1,198 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/Helpers/jucer_TranslationHelpers.h"
|
||||
|
||||
//==============================================================================
|
||||
class TranslationToolComponent : public Component
|
||||
{
|
||||
public:
|
||||
TranslationToolComponent()
|
||||
: editorOriginal (documentOriginal, nullptr),
|
||||
editorPre (documentPre, nullptr),
|
||||
editorPost (documentPost, nullptr),
|
||||
editorResult (documentResult, nullptr)
|
||||
{
|
||||
instructionsLabel.setText (
|
||||
"This utility converts translation files to/from a format that can be passed to automatic translation tools."
|
||||
"\n\n"
|
||||
"First, choose whether to scan the current project for all TRANS() macros, or "
|
||||
"pick an existing translation file to load:", dontSendNotification);
|
||||
addAndMakeVisible (instructionsLabel);
|
||||
|
||||
label1.setText ("..then copy-and-paste this annotated text into Google Translate or some other translator:", dontSendNotification);
|
||||
addAndMakeVisible (label1);
|
||||
|
||||
label2.setText ("...then, take the translated result and paste it into the box below:", dontSendNotification);
|
||||
addAndMakeVisible (label2);
|
||||
|
||||
label3.setText ("Finally, click the 'Generate' button, and a translation file will be created below. "
|
||||
"Remember to update its language code at the top!", dontSendNotification);
|
||||
addAndMakeVisible (label3);
|
||||
|
||||
label4.setText ("If you load an existing file the already translated strings will be removed. Ensure this box is empty to create a fresh translation", dontSendNotification);
|
||||
addAndMakeVisible (label4);
|
||||
|
||||
addAndMakeVisible (editorOriginal);
|
||||
addAndMakeVisible (editorPre);
|
||||
addAndMakeVisible (editorPost);
|
||||
addAndMakeVisible (editorResult);
|
||||
|
||||
addAndMakeVisible (generateButton);
|
||||
generateButton.onClick = [this] { generate(); };
|
||||
|
||||
addAndMakeVisible (scanProjectButton);
|
||||
scanProjectButton.onClick = [this] { scanProject(); };
|
||||
|
||||
addAndMakeVisible (scanFolderButton);
|
||||
scanFolderButton.onClick = [this] { scanFolder(); };
|
||||
|
||||
addAndMakeVisible (loadTranslationButton);
|
||||
loadTranslationButton.onClick = [this] { loadFile(); };
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
const int m = 6;
|
||||
const int textH = 44;
|
||||
const int extraH = (7 * textH);
|
||||
const int editorH = (getHeight() - extraH) / 4;
|
||||
const int numButtons = 3;
|
||||
|
||||
Rectangle<int> r (getLocalBounds().withTrimmedBottom (m));
|
||||
const int buttonWidth = r.getWidth() / numButtons;
|
||||
|
||||
instructionsLabel.setBounds (r.removeFromTop (textH * 2).reduced (m));
|
||||
r.removeFromTop (m);
|
||||
Rectangle<int> r2 (r.removeFromTop (textH - (2 * m)));
|
||||
scanProjectButton .setBounds (r2.removeFromLeft (buttonWidth).reduced (m, 0));
|
||||
scanFolderButton .setBounds (r2.removeFromLeft (buttonWidth).reduced (m, 0));
|
||||
loadTranslationButton.setBounds (r2.reduced (m, 0));
|
||||
|
||||
label1 .setBounds (r.removeFromTop (textH) .reduced (m));
|
||||
editorPre.setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
label2 .setBounds (r.removeFromTop (textH) .reduced (m));
|
||||
editorPost.setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
r2 = r.removeFromTop (textH);
|
||||
generateButton.setBounds (r2.removeFromRight (152).reduced (m));
|
||||
label3 .setBounds (r2.reduced (m));
|
||||
editorResult .setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
label4 .setBounds (r.removeFromTop (textH).reduced (m));
|
||||
editorOriginal.setBounds (r.reduced (m, 0));
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void generate()
|
||||
{
|
||||
StringArray preStrings (TranslationHelpers::breakApart (documentPre.getAllContent()));
|
||||
StringArray postStrings (TranslationHelpers::breakApart (documentPost.getAllContent()));
|
||||
|
||||
if (postStrings.size() != preStrings.size())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
TRANS("Error"),
|
||||
TRANS("The pre- and post-translation text doesn't match!\n\n"
|
||||
"Perhaps it got mangled by the translator?"));
|
||||
return;
|
||||
}
|
||||
|
||||
const LocalisedStrings originalTranslation (documentOriginal.getAllContent(), false);
|
||||
documentResult.replaceAllContent (TranslationHelpers::createFinishedTranslationFile (preStrings, postStrings, originalTranslation));
|
||||
}
|
||||
|
||||
void scanProject()
|
||||
{
|
||||
if (Project* project = ProjucerApplication::getApp().mainWindowList.getFrontmostProject())
|
||||
setPreTranslationText (TranslationHelpers::getPreTranslationText (*project));
|
||||
else
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Translation Tool",
|
||||
"This will only work when you have a project open!");
|
||||
}
|
||||
|
||||
void scanFolder()
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Choose the root folder to search for the TRANS macros",
|
||||
File(), "*");
|
||||
auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
StringArray strings;
|
||||
TranslationHelpers::scanFolderForTranslations (strings, fc.getResult());
|
||||
setPreTranslationText (TranslationHelpers::mungeStrings(strings));
|
||||
});
|
||||
}
|
||||
|
||||
void loadFile()
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Choose a translation file to load", File(), "*");
|
||||
auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
const LocalisedStrings loadedStrings (fc.getResult(), false);
|
||||
documentOriginal.replaceAllContent (fc.getResult().loadFileAsString().trim());
|
||||
setPreTranslationText (TranslationHelpers::getPreTranslationText (loadedStrings));
|
||||
});
|
||||
}
|
||||
|
||||
void setPreTranslationText (const String& text)
|
||||
{
|
||||
documentPre.replaceAllContent (text);
|
||||
editorPre.grabKeyboardFocus();
|
||||
editorPre.selectAll();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CodeDocument documentOriginal, documentPre, documentPost, documentResult;
|
||||
CodeEditorComponent editorOriginal, editorPre, editorPost, editorResult;
|
||||
|
||||
Label label1, label2, label3, label4;
|
||||
Label instructionsLabel;
|
||||
|
||||
TextButton generateButton { TRANS("Generate") },
|
||||
scanProjectButton { "Scan project for TRANS macros" },
|
||||
scanFolderButton { "Scan folder for TRANS macros" },
|
||||
loadTranslationButton { "Load existing translation file..."};
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/Helpers/jucer_TranslationHelpers.h"
|
||||
|
||||
//==============================================================================
|
||||
class TranslationToolComponent : public Component
|
||||
{
|
||||
public:
|
||||
TranslationToolComponent()
|
||||
: editorOriginal (documentOriginal, nullptr),
|
||||
editorPre (documentPre, nullptr),
|
||||
editorPost (documentPost, nullptr),
|
||||
editorResult (documentResult, nullptr)
|
||||
{
|
||||
instructionsLabel.setText (
|
||||
"This utility converts translation files to/from a format that can be passed to automatic translation tools."
|
||||
"\n\n"
|
||||
"First, choose whether to scan the current project for all TRANS() macros, or "
|
||||
"pick an existing translation file to load:", dontSendNotification);
|
||||
addAndMakeVisible (instructionsLabel);
|
||||
|
||||
label1.setText ("..then copy-and-paste this annotated text into Google Translate or some other translator:", dontSendNotification);
|
||||
addAndMakeVisible (label1);
|
||||
|
||||
label2.setText ("...then, take the translated result and paste it into the box below:", dontSendNotification);
|
||||
addAndMakeVisible (label2);
|
||||
|
||||
label3.setText ("Finally, click the 'Generate' button, and a translation file will be created below. "
|
||||
"Remember to update its language code at the top!", dontSendNotification);
|
||||
addAndMakeVisible (label3);
|
||||
|
||||
label4.setText ("If you load an existing file the already translated strings will be removed. Ensure this box is empty to create a fresh translation", dontSendNotification);
|
||||
addAndMakeVisible (label4);
|
||||
|
||||
addAndMakeVisible (editorOriginal);
|
||||
addAndMakeVisible (editorPre);
|
||||
addAndMakeVisible (editorPost);
|
||||
addAndMakeVisible (editorResult);
|
||||
|
||||
addAndMakeVisible (generateButton);
|
||||
generateButton.onClick = [this] { generate(); };
|
||||
|
||||
addAndMakeVisible (scanProjectButton);
|
||||
scanProjectButton.onClick = [this] { scanProject(); };
|
||||
|
||||
addAndMakeVisible (scanFolderButton);
|
||||
scanFolderButton.onClick = [this] { scanFolder(); };
|
||||
|
||||
addAndMakeVisible (loadTranslationButton);
|
||||
loadTranslationButton.onClick = [this] { loadFile(); };
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
const int m = 6;
|
||||
const int textH = 44;
|
||||
const int extraH = (7 * textH);
|
||||
const int editorH = (getHeight() - extraH) / 4;
|
||||
const int numButtons = 3;
|
||||
|
||||
Rectangle<int> r (getLocalBounds().withTrimmedBottom (m));
|
||||
const int buttonWidth = r.getWidth() / numButtons;
|
||||
|
||||
instructionsLabel.setBounds (r.removeFromTop (textH * 2).reduced (m));
|
||||
r.removeFromTop (m);
|
||||
Rectangle<int> r2 (r.removeFromTop (textH - (2 * m)));
|
||||
scanProjectButton .setBounds (r2.removeFromLeft (buttonWidth).reduced (m, 0));
|
||||
scanFolderButton .setBounds (r2.removeFromLeft (buttonWidth).reduced (m, 0));
|
||||
loadTranslationButton.setBounds (r2.reduced (m, 0));
|
||||
|
||||
label1 .setBounds (r.removeFromTop (textH) .reduced (m));
|
||||
editorPre.setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
label2 .setBounds (r.removeFromTop (textH) .reduced (m));
|
||||
editorPost.setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
r2 = r.removeFromTop (textH);
|
||||
generateButton.setBounds (r2.removeFromRight (152).reduced (m));
|
||||
label3 .setBounds (r2.reduced (m));
|
||||
editorResult .setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
label4 .setBounds (r.removeFromTop (textH).reduced (m));
|
||||
editorOriginal.setBounds (r.reduced (m, 0));
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void generate()
|
||||
{
|
||||
StringArray preStrings (TranslationHelpers::breakApart (documentPre.getAllContent()));
|
||||
StringArray postStrings (TranslationHelpers::breakApart (documentPost.getAllContent()));
|
||||
|
||||
if (postStrings.size() != preStrings.size())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
TRANS("Error"),
|
||||
TRANS("The pre- and post-translation text doesn't match!\n\n"
|
||||
"Perhaps it got mangled by the translator?"));
|
||||
return;
|
||||
}
|
||||
|
||||
const LocalisedStrings originalTranslation (documentOriginal.getAllContent(), false);
|
||||
documentResult.replaceAllContent (TranslationHelpers::createFinishedTranslationFile (preStrings, postStrings, originalTranslation));
|
||||
}
|
||||
|
||||
void scanProject()
|
||||
{
|
||||
if (Project* project = ProjucerApplication::getApp().mainWindowList.getFrontmostProject())
|
||||
setPreTranslationText (TranslationHelpers::getPreTranslationText (*project));
|
||||
else
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Translation Tool",
|
||||
"This will only work when you have a project open!");
|
||||
}
|
||||
|
||||
void scanFolder()
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Choose the root folder to search for the TRANS macros",
|
||||
File(), "*");
|
||||
auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
StringArray strings;
|
||||
TranslationHelpers::scanFolderForTranslations (strings, fc.getResult());
|
||||
setPreTranslationText (TranslationHelpers::mungeStrings(strings));
|
||||
});
|
||||
}
|
||||
|
||||
void loadFile()
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Choose a translation file to load", File(), "*");
|
||||
auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
const LocalisedStrings loadedStrings (fc.getResult(), false);
|
||||
documentOriginal.replaceAllContent (fc.getResult().loadFileAsString().trim());
|
||||
setPreTranslationText (TranslationHelpers::getPreTranslationText (loadedStrings));
|
||||
});
|
||||
}
|
||||
|
||||
void setPreTranslationText (const String& text)
|
||||
{
|
||||
documentPre.replaceAllContent (text);
|
||||
editorPre.grabKeyboardFocus();
|
||||
editorPre.selectAll();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CodeDocument documentOriginal, documentPre, documentPost, documentResult;
|
||||
CodeEditorComponent editorOriginal, editorPre, editorPost, editorResult;
|
||||
|
||||
Label label1, label2, label3, label4;
|
||||
Label instructionsLabel;
|
||||
|
||||
TextButton generateButton { TRANS("Generate") },
|
||||
scanProjectButton { "Scan project for TRANS macros" },
|
||||
scanFolderButton { "Scan folder for TRANS macros" },
|
||||
loadTranslationButton { "Load existing translation file..."};
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
};
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class UTF8Component : public Component
|
||||
{
|
||||
public:
|
||||
UTF8Component()
|
||||
: desc (String(),
|
||||
"Type any string into the box, and it'll be shown below as a portable UTF-8 literal, "
|
||||
"ready to cut-and-paste into your source-code...")
|
||||
{
|
||||
desc.setJustificationType (Justification::centred);
|
||||
addAndMakeVisible (desc);
|
||||
|
||||
userText.setMultiLine (true, true);
|
||||
userText.setReturnKeyStartsNewLine (true);
|
||||
addAndMakeVisible (userText);
|
||||
userText.onTextChange = [this] { update(); };
|
||||
userText.onEscapeKey = [this] { getTopLevelComponent()->exitModalState (0); };
|
||||
|
||||
resultText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
resultText.setMultiLine (true, true);
|
||||
resultText.setReadOnly (true);
|
||||
resultText.setSelectAllWhenFocused (true);
|
||||
addAndMakeVisible (resultText);
|
||||
|
||||
userText.setText (getLastText());
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
getLastText() = userText.getText();
|
||||
resultText.setText (CodeHelpers::stringLiteral (getLastText(), 100), false);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds().reduced (8);
|
||||
desc.setBounds (r.removeFromTop (44));
|
||||
r.removeFromTop (8);
|
||||
userText.setBounds (r.removeFromTop (r.getHeight() / 2));
|
||||
r.removeFromTop (8);
|
||||
resultText.setBounds (r);
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
userText.applyFontToAllText (userText.getFont());
|
||||
resultText.applyFontToAllText (resultText.getFont());
|
||||
}
|
||||
|
||||
private:
|
||||
Label desc;
|
||||
TextEditor userText, resultText;
|
||||
|
||||
String& getLastText()
|
||||
{
|
||||
static String t;
|
||||
return t;
|
||||
}
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class UTF8Component : public Component
|
||||
{
|
||||
public:
|
||||
UTF8Component()
|
||||
: desc (String(),
|
||||
"Type any string into the box, and it'll be shown below as a portable UTF-8 literal, "
|
||||
"ready to cut-and-paste into your source-code...")
|
||||
{
|
||||
desc.setJustificationType (Justification::centred);
|
||||
addAndMakeVisible (desc);
|
||||
|
||||
userText.setMultiLine (true, true);
|
||||
userText.setReturnKeyStartsNewLine (true);
|
||||
addAndMakeVisible (userText);
|
||||
userText.onTextChange = [this] { update(); };
|
||||
userText.onEscapeKey = [this] { getTopLevelComponent()->exitModalState (0); };
|
||||
|
||||
resultText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
resultText.setMultiLine (true, true);
|
||||
resultText.setReadOnly (true);
|
||||
resultText.setSelectAllWhenFocused (true);
|
||||
addAndMakeVisible (resultText);
|
||||
|
||||
userText.setText (getLastText());
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
getLastText() = userText.getText();
|
||||
resultText.setText (CodeHelpers::stringLiteral (getLastText(), 100), false);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds().reduced (8);
|
||||
desc.setBounds (r.removeFromTop (44));
|
||||
r.removeFromTop (8);
|
||||
userText.setBounds (r.removeFromTop (r.getHeight() / 2));
|
||||
r.removeFromTop (8);
|
||||
resultText.setBounds (r);
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
userText.applyFontToAllText (userText.getFont());
|
||||
resultText.applyFontToAllText (resultText.getFont());
|
||||
}
|
||||
|
||||
private:
|
||||
Label desc;
|
||||
TextEditor userText, resultText;
|
||||
|
||||
String& getLastText()
|
||||
{
|
||||
static String t;
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,224 +1,224 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UserAccount/jucer_LicenseController.h"
|
||||
#include "jucer_MainWindow.h"
|
||||
#include "../Project/Modules/jucer_Modules.h"
|
||||
#include "jucer_AutoUpdater.h"
|
||||
#include "../CodeEditor/jucer_SourceCodeEditor.h"
|
||||
#include "../Utility/UI/jucer_ProjucerLookAndFeel.h"
|
||||
|
||||
//==============================================================================
|
||||
class ProjucerApplication : public JUCEApplication,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
ProjucerApplication() = default;
|
||||
|
||||
static ProjucerApplication& getApp();
|
||||
static ApplicationCommandManager& getCommandManager();
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const String& commandLine) override;
|
||||
void shutdown() override;
|
||||
void systemRequestedQuit() override;
|
||||
void deleteLogger();
|
||||
|
||||
const String getApplicationName() override { return "Projucer"; }
|
||||
const String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||
|
||||
String getVersionDescription() const;
|
||||
bool moreThanOneInstanceAllowed() override { return true; } // this is handled manually in initialise()
|
||||
|
||||
void anotherInstanceStarted (const String& commandLine) override;
|
||||
|
||||
//==============================================================================
|
||||
MenuBarModel* getMenuModel();
|
||||
|
||||
void getAllCommands (Array<CommandID>&) override;
|
||||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo&) override;
|
||||
bool perform (const InvocationInfo&) override;
|
||||
|
||||
bool isGUIEditorEnabled() const;
|
||||
|
||||
//==============================================================================
|
||||
void openFile (const File&, std::function<void (bool)>);
|
||||
void showPathsWindow (bool highlightJUCEPath = false);
|
||||
PropertiesFile::Options getPropertyFileOptionsFor (const String& filename, bool isProjectSettings);
|
||||
void selectEditorColourSchemeWithName (const String& schemeName);
|
||||
|
||||
//==============================================================================
|
||||
void rescanJUCEPathModules();
|
||||
void rescanUserPathModules();
|
||||
|
||||
AvailableModulesList& getJUCEPathModulesList() { return jucePathModulesList; }
|
||||
AvailableModulesList& getUserPathsModulesList() { return userPathsModulesList; }
|
||||
|
||||
LicenseController& getLicenseController() { return *licenseController; }
|
||||
|
||||
bool isAutomaticVersionCheckingEnabled() const;
|
||||
void setAutomaticVersionCheckingEnabled (bool shouldBeEnabled);
|
||||
|
||||
bool shouldPromptUserAboutIncorrectJUCEPath() const;
|
||||
void setShouldPromptUserAboutIncorrectJUCEPath (bool shouldPrompt);
|
||||
|
||||
static File getJUCEExamplesDirectoryPathFromGlobal() noexcept;
|
||||
static Array<File> getSortedExampleDirectories() noexcept;
|
||||
static Array<File> getSortedExampleFilesInDirectory (const File&) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
ProjucerLookAndFeel lookAndFeel;
|
||||
|
||||
std::unique_ptr<StoredSettings> settings;
|
||||
std::unique_ptr<Icons> icons;
|
||||
|
||||
struct MainMenuModel;
|
||||
std::unique_ptr<MainMenuModel> menuModel;
|
||||
|
||||
MainWindowList mainWindowList;
|
||||
OpenDocumentManager openDocumentManager;
|
||||
std::unique_ptr<ApplicationCommandManager> commandManager;
|
||||
|
||||
bool isRunningCommandLine = false;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void handleAsyncUpdate() override;
|
||||
void doBasicApplicationSetup();
|
||||
|
||||
void initCommandManager();
|
||||
bool initialiseLogger (const char* filePrefix);
|
||||
void initialiseWindows (const String& commandLine);
|
||||
|
||||
void createNewProject();
|
||||
void createNewProjectFromClipboard();
|
||||
void createNewPIP();
|
||||
void askUserToOpenFile();
|
||||
void saveAllDocuments();
|
||||
void closeAllDocuments (OpenDocumentManager::SaveIfNeeded askUserToSave);
|
||||
void closeAllMainWindows (std::function<void (bool)>);
|
||||
void closeAllMainWindowsAndQuitIfNeeded();
|
||||
void clearRecentFiles();
|
||||
|
||||
StringArray getMenuNames();
|
||||
PopupMenu createMenu (const String& menuName);
|
||||
PopupMenu createFileMenu();
|
||||
PopupMenu createEditMenu();
|
||||
PopupMenu createViewMenu();
|
||||
void createColourSchemeItems (PopupMenu&);
|
||||
PopupMenu createWindowMenu();
|
||||
PopupMenu createDocumentMenu();
|
||||
PopupMenu createToolsMenu();
|
||||
PopupMenu createHelpMenu();
|
||||
PopupMenu createExtraAppleMenuItems();
|
||||
void handleMainMenuCommand (int menuItemID);
|
||||
PopupMenu createExamplesPopupMenu() noexcept;
|
||||
|
||||
void findAndLaunchExample (int);
|
||||
|
||||
void checkIfGlobalJUCEPathHasChanged();
|
||||
File tryToFindDemoRunnerExecutable();
|
||||
File tryToFindDemoRunnerProject();
|
||||
void launchDemoRunner();
|
||||
|
||||
void setColourScheme (int index, bool saveSetting);
|
||||
void setEditorColourScheme (int index, bool saveSetting);
|
||||
void updateEditorColourSchemeIfNeeded();
|
||||
|
||||
void showUTF8ToolWindow();
|
||||
void showSVGPathDataToolWindow();
|
||||
void showAboutWindow();
|
||||
void showEditorColourSchemeWindow();
|
||||
void showPIPCreatorWindow();
|
||||
|
||||
void launchForumBrowser();
|
||||
void launchModulesBrowser();
|
||||
void launchClassesBrowser();
|
||||
void launchTutorialsBrowser();
|
||||
|
||||
void doLoginOrLogout();
|
||||
void showLoginForm();
|
||||
|
||||
void enableOrDisableGUIEditor();
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
class AppleMenuRebuildListener : private MenuBarModel::Listener
|
||||
{
|
||||
public:
|
||||
AppleMenuRebuildListener()
|
||||
{
|
||||
if (auto* model = ProjucerApplication::getApp().getMenuModel())
|
||||
model->addListener (this);
|
||||
}
|
||||
|
||||
~AppleMenuRebuildListener() override
|
||||
{
|
||||
if (auto* model = ProjucerApplication::getApp().getMenuModel())
|
||||
model->removeListener (this);
|
||||
}
|
||||
|
||||
private:
|
||||
void menuBarItemsChanged (MenuBarModel*) override {}
|
||||
|
||||
void menuCommandInvoked (MenuBarModel*,
|
||||
const ApplicationCommandTarget::InvocationInfo& info) override
|
||||
{
|
||||
if (info.commandID == CommandIDs::enableNewVersionCheck)
|
||||
Timer::callAfterDelay (50, [] { ProjucerApplication::getApp().rebuildAppleMenu(); });
|
||||
}
|
||||
};
|
||||
|
||||
void rebuildAppleMenu();
|
||||
|
||||
std::unique_ptr<AppleMenuRebuildListener> appleMenuRebuildListener;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<LicenseController> licenseController;
|
||||
|
||||
std::unique_ptr<TooltipWindow> tooltipWindow;
|
||||
AvailableModulesList jucePathModulesList, userPathsModulesList;
|
||||
|
||||
std::unique_ptr<Component> utf8Window, svgPathWindow, aboutWindow, pathsWindow,
|
||||
editorColourSchemeWindow, pipCreatorWindow;
|
||||
|
||||
std::unique_ptr<FileLogger> logger;
|
||||
|
||||
int numExamples = 0;
|
||||
std::unique_ptr<AlertWindow> demoRunnerAlert;
|
||||
bool hasScannedForDemoRunnerExecutable = false, hasScannedForDemoRunnerProject = false;
|
||||
File lastJUCEPath, lastDemoRunnerExectuableFile, lastDemoRunnerProjectFile;
|
||||
|
||||
int selectedColourSchemeIndex = 0, selectedEditorColourSchemeIndex = 0;
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjucerApplication)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ProjucerApplication)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UserAccount/jucer_LicenseController.h"
|
||||
#include "jucer_MainWindow.h"
|
||||
#include "../Project/Modules/jucer_Modules.h"
|
||||
#include "jucer_AutoUpdater.h"
|
||||
#include "../CodeEditor/jucer_SourceCodeEditor.h"
|
||||
#include "../Utility/UI/jucer_ProjucerLookAndFeel.h"
|
||||
|
||||
//==============================================================================
|
||||
class ProjucerApplication : public JUCEApplication,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
ProjucerApplication() = default;
|
||||
|
||||
static ProjucerApplication& getApp();
|
||||
static ApplicationCommandManager& getCommandManager();
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const String& commandLine) override;
|
||||
void shutdown() override;
|
||||
void systemRequestedQuit() override;
|
||||
void deleteLogger();
|
||||
|
||||
const String getApplicationName() override { return "Projucer"; }
|
||||
const String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||
|
||||
String getVersionDescription() const;
|
||||
bool moreThanOneInstanceAllowed() override { return true; } // this is handled manually in initialise()
|
||||
|
||||
void anotherInstanceStarted (const String& commandLine) override;
|
||||
|
||||
//==============================================================================
|
||||
MenuBarModel* getMenuModel();
|
||||
|
||||
void getAllCommands (Array<CommandID>&) override;
|
||||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo&) override;
|
||||
bool perform (const InvocationInfo&) override;
|
||||
|
||||
bool isGUIEditorEnabled() const;
|
||||
|
||||
//==============================================================================
|
||||
void openFile (const File&, std::function<void (bool)>);
|
||||
void showPathsWindow (bool highlightJUCEPath = false);
|
||||
PropertiesFile::Options getPropertyFileOptionsFor (const String& filename, bool isProjectSettings);
|
||||
void selectEditorColourSchemeWithName (const String& schemeName);
|
||||
|
||||
//==============================================================================
|
||||
void rescanJUCEPathModules();
|
||||
void rescanUserPathModules();
|
||||
|
||||
AvailableModulesList& getJUCEPathModulesList() { return jucePathModulesList; }
|
||||
AvailableModulesList& getUserPathsModulesList() { return userPathsModulesList; }
|
||||
|
||||
LicenseController& getLicenseController() { return *licenseController; }
|
||||
|
||||
bool isAutomaticVersionCheckingEnabled() const;
|
||||
void setAutomaticVersionCheckingEnabled (bool shouldBeEnabled);
|
||||
|
||||
bool shouldPromptUserAboutIncorrectJUCEPath() const;
|
||||
void setShouldPromptUserAboutIncorrectJUCEPath (bool shouldPrompt);
|
||||
|
||||
static File getJUCEExamplesDirectoryPathFromGlobal() noexcept;
|
||||
static Array<File> getSortedExampleDirectories() noexcept;
|
||||
static Array<File> getSortedExampleFilesInDirectory (const File&) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
ProjucerLookAndFeel lookAndFeel;
|
||||
|
||||
std::unique_ptr<StoredSettings> settings;
|
||||
std::unique_ptr<Icons> icons;
|
||||
|
||||
struct MainMenuModel;
|
||||
std::unique_ptr<MainMenuModel> menuModel;
|
||||
|
||||
MainWindowList mainWindowList;
|
||||
OpenDocumentManager openDocumentManager;
|
||||
std::unique_ptr<ApplicationCommandManager> commandManager;
|
||||
|
||||
bool isRunningCommandLine = false;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void handleAsyncUpdate() override;
|
||||
void doBasicApplicationSetup();
|
||||
|
||||
void initCommandManager();
|
||||
bool initialiseLogger (const char* filePrefix);
|
||||
void initialiseWindows (const String& commandLine);
|
||||
|
||||
void createNewProject();
|
||||
void createNewProjectFromClipboard();
|
||||
void createNewPIP();
|
||||
void askUserToOpenFile();
|
||||
void saveAllDocuments();
|
||||
void closeAllDocuments (OpenDocumentManager::SaveIfNeeded askUserToSave);
|
||||
void closeAllMainWindows (std::function<void (bool)>);
|
||||
void closeAllMainWindowsAndQuitIfNeeded();
|
||||
void clearRecentFiles();
|
||||
|
||||
StringArray getMenuNames();
|
||||
PopupMenu createMenu (const String& menuName);
|
||||
PopupMenu createFileMenu();
|
||||
PopupMenu createEditMenu();
|
||||
PopupMenu createViewMenu();
|
||||
void createColourSchemeItems (PopupMenu&);
|
||||
PopupMenu createWindowMenu();
|
||||
PopupMenu createDocumentMenu();
|
||||
PopupMenu createToolsMenu();
|
||||
PopupMenu createHelpMenu();
|
||||
PopupMenu createExtraAppleMenuItems();
|
||||
void handleMainMenuCommand (int menuItemID);
|
||||
PopupMenu createExamplesPopupMenu() noexcept;
|
||||
|
||||
void findAndLaunchExample (int);
|
||||
|
||||
void checkIfGlobalJUCEPathHasChanged();
|
||||
File tryToFindDemoRunnerExecutable();
|
||||
File tryToFindDemoRunnerProject();
|
||||
void launchDemoRunner();
|
||||
|
||||
void setColourScheme (int index, bool saveSetting);
|
||||
void setEditorColourScheme (int index, bool saveSetting);
|
||||
void updateEditorColourSchemeIfNeeded();
|
||||
|
||||
void showUTF8ToolWindow();
|
||||
void showSVGPathDataToolWindow();
|
||||
void showAboutWindow();
|
||||
void showEditorColourSchemeWindow();
|
||||
void showPIPCreatorWindow();
|
||||
|
||||
void launchForumBrowser();
|
||||
void launchModulesBrowser();
|
||||
void launchClassesBrowser();
|
||||
void launchTutorialsBrowser();
|
||||
|
||||
void doLoginOrLogout();
|
||||
void showLoginForm();
|
||||
|
||||
void enableOrDisableGUIEditor();
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
class AppleMenuRebuildListener : private MenuBarModel::Listener
|
||||
{
|
||||
public:
|
||||
AppleMenuRebuildListener()
|
||||
{
|
||||
if (auto* model = ProjucerApplication::getApp().getMenuModel())
|
||||
model->addListener (this);
|
||||
}
|
||||
|
||||
~AppleMenuRebuildListener() override
|
||||
{
|
||||
if (auto* model = ProjucerApplication::getApp().getMenuModel())
|
||||
model->removeListener (this);
|
||||
}
|
||||
|
||||
private:
|
||||
void menuBarItemsChanged (MenuBarModel*) override {}
|
||||
|
||||
void menuCommandInvoked (MenuBarModel*,
|
||||
const ApplicationCommandTarget::InvocationInfo& info) override
|
||||
{
|
||||
if (info.commandID == CommandIDs::enableNewVersionCheck)
|
||||
Timer::callAfterDelay (50, [] { ProjucerApplication::getApp().rebuildAppleMenu(); });
|
||||
}
|
||||
};
|
||||
|
||||
void rebuildAppleMenu();
|
||||
|
||||
std::unique_ptr<AppleMenuRebuildListener> appleMenuRebuildListener;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<LicenseController> licenseController;
|
||||
|
||||
std::unique_ptr<TooltipWindow> tooltipWindow;
|
||||
AvailableModulesList jucePathModulesList, userPathsModulesList;
|
||||
|
||||
std::unique_ptr<Component> utf8Window, svgPathWindow, aboutWindow, pathsWindow,
|
||||
editorColourSchemeWindow, pipCreatorWindow;
|
||||
|
||||
std::unique_ptr<FileLogger> logger;
|
||||
|
||||
int numExamples = 0;
|
||||
std::unique_ptr<AlertWindow> demoRunnerAlert;
|
||||
bool hasScannedForDemoRunnerExecutable = false, hasScannedForDemoRunnerProject = false;
|
||||
File lastJUCEPath, lastDemoRunnerExectuableFile, lastDemoRunnerProjectFile;
|
||||
|
||||
int selectedColourSchemeIndex = 0, selectedEditorColourSchemeIndex = 0;
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjucerApplication)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ProjucerApplication)
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,62 +1,62 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Utility/Helpers/jucer_VersionInfo.h"
|
||||
|
||||
class DownloadAndInstallThread;
|
||||
|
||||
class LatestVersionCheckerAndUpdater : public DeletedAtShutdown,
|
||||
private Thread
|
||||
{
|
||||
public:
|
||||
LatestVersionCheckerAndUpdater();
|
||||
~LatestVersionCheckerAndUpdater() override;
|
||||
|
||||
void checkForNewVersion (bool isBackgroundCheck);
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (LatestVersionCheckerAndUpdater)
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void run() override;
|
||||
void askUserAboutNewVersion (const String&, const String&, const VersionInfo::Asset&);
|
||||
void askUserForLocationToDownload (const VersionInfo::Asset&);
|
||||
void downloadAndInstall (const VersionInfo::Asset&, const File&);
|
||||
|
||||
void showDialogWindow (const String&, const String&, const VersionInfo::Asset&);
|
||||
void addNotificationToOpenProjects (const VersionInfo::Asset&);
|
||||
|
||||
//==============================================================================
|
||||
bool backgroundCheck = false;
|
||||
|
||||
std::unique_ptr<DownloadAndInstallThread> installer;
|
||||
std::unique_ptr<Component> dialogWindow;
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (LatestVersionCheckerAndUpdater)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Utility/Helpers/jucer_VersionInfo.h"
|
||||
|
||||
class DownloadAndInstallThread;
|
||||
|
||||
class LatestVersionCheckerAndUpdater : public DeletedAtShutdown,
|
||||
private Thread
|
||||
{
|
||||
public:
|
||||
LatestVersionCheckerAndUpdater();
|
||||
~LatestVersionCheckerAndUpdater() override;
|
||||
|
||||
void checkForNewVersion (bool isBackgroundCheck);
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (LatestVersionCheckerAndUpdater)
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void run() override;
|
||||
void askUserAboutNewVersion (const String&, const String&, const VersionInfo::Asset&);
|
||||
void askUserForLocationToDownload (const VersionInfo::Asset&);
|
||||
void downloadAndInstall (const VersionInfo::Asset&, const File&);
|
||||
|
||||
void showDialogWindow (const String&, const String&, const VersionInfo::Asset&);
|
||||
void addNotificationToOpenProjects (const VersionInfo::Asset&);
|
||||
|
||||
//==============================================================================
|
||||
bool backgroundCheck = false;
|
||||
|
||||
std::unique_ptr<DownloadAndInstallThread> installer;
|
||||
std::unique_ptr<Component> dialogWindow;
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (LatestVersionCheckerAndUpdater)
|
||||
};
|
||||
|
||||
@@ -1,108 +1,108 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
A namespace to hold all the possible command IDs.
|
||||
*/
|
||||
namespace CommandIDs
|
||||
{
|
||||
enum
|
||||
{
|
||||
newProject = 0x300000,
|
||||
newProjectFromClipboard = 0x300001,
|
||||
newPIP = 0x300002,
|
||||
open = 0x300003,
|
||||
closeDocument = 0x300004,
|
||||
saveDocument = 0x300005,
|
||||
saveDocumentAs = 0x300006,
|
||||
|
||||
launchDemoRunner = 0x300007,
|
||||
|
||||
closeProject = 0x300010,
|
||||
saveProject = 0x300011,
|
||||
saveAll = 0x300012,
|
||||
openInIDE = 0x300013,
|
||||
saveAndOpenInIDE = 0x300014,
|
||||
createNewExporter = 0x300015,
|
||||
|
||||
showUTF8Tool = 0x300020,
|
||||
showGlobalPathsWindow = 0x300021,
|
||||
showTranslationTool = 0x300022,
|
||||
showSVGPathTool = 0x300023,
|
||||
showAboutWindow = 0x300024,
|
||||
checkForNewVersion = 0x300025,
|
||||
enableNewVersionCheck = 0x300026,
|
||||
enableGUIEditor = 0x300027,
|
||||
|
||||
showProjectSettings = 0x300030,
|
||||
showFileExplorerPanel = 0x300033,
|
||||
showModulesPanel = 0x300034,
|
||||
showExportersPanel = 0x300035,
|
||||
showExporterSettings = 0x300036,
|
||||
|
||||
closeWindow = 0x300040,
|
||||
closeAllWindows = 0x300041,
|
||||
closeAllDocuments = 0x300042,
|
||||
goToPreviousDoc = 0x300043,
|
||||
goToNextDoc = 0x300044,
|
||||
goToCounterpart = 0x300045,
|
||||
deleteSelectedItem = 0x300046,
|
||||
goToPreviousWindow = 0x300047,
|
||||
goToNextWindow = 0x300048,
|
||||
clearRecentFiles = 0x300049,
|
||||
|
||||
showFindPanel = 0x300050,
|
||||
findSelection = 0x300051,
|
||||
findNext = 0x300052,
|
||||
findPrevious = 0x300053,
|
||||
|
||||
enableSnapToGrid = 0x300070,
|
||||
zoomIn = 0x300071,
|
||||
zoomOut = 0x300072,
|
||||
zoomNormal = 0x300073,
|
||||
spaceBarDrag = 0x300074,
|
||||
|
||||
loginLogout = 0x300090,
|
||||
|
||||
showForum = 0x300100,
|
||||
showAPIModules = 0x300101,
|
||||
showAPIClasses = 0x300102,
|
||||
showTutorials = 0x300103,
|
||||
|
||||
addNewGUIFile = 0x300200,
|
||||
|
||||
lastCommandIDEntry
|
||||
};
|
||||
}
|
||||
|
||||
namespace CommandCategories
|
||||
{
|
||||
static const char* const general = "General";
|
||||
static const char* const editing = "Editing";
|
||||
static const char* const view = "View";
|
||||
static const char* const windows = "Windows";
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
A namespace to hold all the possible command IDs.
|
||||
*/
|
||||
namespace CommandIDs
|
||||
{
|
||||
enum
|
||||
{
|
||||
newProject = 0x300000,
|
||||
newProjectFromClipboard = 0x300001,
|
||||
newPIP = 0x300002,
|
||||
open = 0x300003,
|
||||
closeDocument = 0x300004,
|
||||
saveDocument = 0x300005,
|
||||
saveDocumentAs = 0x300006,
|
||||
|
||||
launchDemoRunner = 0x300007,
|
||||
|
||||
closeProject = 0x300010,
|
||||
saveProject = 0x300011,
|
||||
saveAll = 0x300012,
|
||||
openInIDE = 0x300013,
|
||||
saveAndOpenInIDE = 0x300014,
|
||||
createNewExporter = 0x300015,
|
||||
|
||||
showUTF8Tool = 0x300020,
|
||||
showGlobalPathsWindow = 0x300021,
|
||||
showTranslationTool = 0x300022,
|
||||
showSVGPathTool = 0x300023,
|
||||
showAboutWindow = 0x300024,
|
||||
checkForNewVersion = 0x300025,
|
||||
enableNewVersionCheck = 0x300026,
|
||||
enableGUIEditor = 0x300027,
|
||||
|
||||
showProjectSettings = 0x300030,
|
||||
showFileExplorerPanel = 0x300033,
|
||||
showModulesPanel = 0x300034,
|
||||
showExportersPanel = 0x300035,
|
||||
showExporterSettings = 0x300036,
|
||||
|
||||
closeWindow = 0x300040,
|
||||
closeAllWindows = 0x300041,
|
||||
closeAllDocuments = 0x300042,
|
||||
goToPreviousDoc = 0x300043,
|
||||
goToNextDoc = 0x300044,
|
||||
goToCounterpart = 0x300045,
|
||||
deleteSelectedItem = 0x300046,
|
||||
goToPreviousWindow = 0x300047,
|
||||
goToNextWindow = 0x300048,
|
||||
clearRecentFiles = 0x300049,
|
||||
|
||||
showFindPanel = 0x300050,
|
||||
findSelection = 0x300051,
|
||||
findNext = 0x300052,
|
||||
findPrevious = 0x300053,
|
||||
|
||||
enableSnapToGrid = 0x300070,
|
||||
zoomIn = 0x300071,
|
||||
zoomOut = 0x300072,
|
||||
zoomNormal = 0x300073,
|
||||
spaceBarDrag = 0x300074,
|
||||
|
||||
loginLogout = 0x300090,
|
||||
|
||||
showForum = 0x300100,
|
||||
showAPIModules = 0x300101,
|
||||
showAPIClasses = 0x300102,
|
||||
showTutorials = 0x300103,
|
||||
|
||||
addNewGUIFile = 0x300200,
|
||||
|
||||
lastCommandIDEntry
|
||||
};
|
||||
}
|
||||
|
||||
namespace CommandCategories
|
||||
{
|
||||
static const char* const general = "General";
|
||||
static const char* const editing = "Editing";
|
||||
static const char* const view = "View";
|
||||
static const char* const windows = "Windows";
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,30 +1,30 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int performCommandLine (const ArgumentList&);
|
||||
|
||||
enum { commandLineNotPerformed = 0x72346231 };
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int performCommandLine (const ArgumentList&);
|
||||
|
||||
enum { commandLineNotPerformed = 0x72346231 };
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// The GCC extensions define linux somewhere in the headers, so undef it here...
|
||||
#if JUCE_GCC
|
||||
#undef linux
|
||||
#endif
|
||||
|
||||
struct TargetOS
|
||||
{
|
||||
enum OS
|
||||
{
|
||||
windows = 0,
|
||||
osx,
|
||||
linux,
|
||||
unknown
|
||||
};
|
||||
|
||||
static OS getThisOS() noexcept
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
return windows;
|
||||
#elif JUCE_MAC
|
||||
return osx;
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
return linux;
|
||||
#else
|
||||
return unknown;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
typedef TargetOS::OS DependencyPathOS;
|
||||
|
||||
//==============================================================================
|
||||
#include "../Settings/jucer_StoredSettings.h"
|
||||
#include "../Utility/UI/jucer_Icons.h"
|
||||
#include "../Utility/Helpers/jucer_MiscUtilities.h"
|
||||
#include "../Utility/Helpers/jucer_CodeHelpers.h"
|
||||
#include "../Utility/Helpers/jucer_FileHelpers.h"
|
||||
#include "../Utility/Helpers/jucer_ValueSourceHelpers.h"
|
||||
#include "../Utility/Helpers/jucer_PresetIDs.h"
|
||||
#include "jucer_CommandIDs.h"
|
||||
|
||||
//==============================================================================
|
||||
const char* const projectItemDragType = "Project Items";
|
||||
const char* const drawableItemDragType = "Drawable Items";
|
||||
const char* const componentItemDragType = "Components";
|
||||
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x2340000,
|
||||
secondaryBackgroundColourId = 0x2340001,
|
||||
defaultTextColourId = 0x2340002,
|
||||
widgetTextColourId = 0x2340003,
|
||||
defaultButtonBackgroundColourId = 0x2340004,
|
||||
secondaryButtonBackgroundColourId = 0x2340005,
|
||||
userButtonBackgroundColourId = 0x2340006,
|
||||
defaultIconColourId = 0x2340007,
|
||||
treeIconColourId = 0x2340008,
|
||||
defaultHighlightColourId = 0x2340009,
|
||||
defaultHighlightedTextColourId = 0x234000a,
|
||||
codeEditorLineNumberColourId = 0x234000b,
|
||||
activeTabIconColourId = 0x234000c,
|
||||
inactiveTabBackgroundColourId = 0x234000d,
|
||||
inactiveTabIconColourId = 0x234000e,
|
||||
contentHeaderBackgroundColourId = 0x234000f,
|
||||
widgetBackgroundColourId = 0x2340010,
|
||||
secondaryWidgetBackgroundColourId = 0x2340011,
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int projucerMajorVersion = ProjectInfo::versionNumber >> 16;
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// The GCC extensions define linux somewhere in the headers, so undef it here...
|
||||
#if JUCE_GCC
|
||||
#undef linux
|
||||
#endif
|
||||
|
||||
struct TargetOS
|
||||
{
|
||||
enum OS
|
||||
{
|
||||
windows = 0,
|
||||
osx,
|
||||
linux,
|
||||
unknown
|
||||
};
|
||||
|
||||
static OS getThisOS() noexcept
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
return windows;
|
||||
#elif JUCE_MAC
|
||||
return osx;
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
return linux;
|
||||
#else
|
||||
return unknown;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
typedef TargetOS::OS DependencyPathOS;
|
||||
|
||||
//==============================================================================
|
||||
#include "../Settings/jucer_StoredSettings.h"
|
||||
#include "../Utility/UI/jucer_Icons.h"
|
||||
#include "../Utility/Helpers/jucer_MiscUtilities.h"
|
||||
#include "../Utility/Helpers/jucer_CodeHelpers.h"
|
||||
#include "../Utility/Helpers/jucer_FileHelpers.h"
|
||||
#include "../Utility/Helpers/jucer_ValueSourceHelpers.h"
|
||||
#include "../Utility/Helpers/jucer_PresetIDs.h"
|
||||
#include "jucer_CommandIDs.h"
|
||||
|
||||
//==============================================================================
|
||||
const char* const projectItemDragType = "Project Items";
|
||||
const char* const drawableItemDragType = "Drawable Items";
|
||||
const char* const componentItemDragType = "Components";
|
||||
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x2340000,
|
||||
secondaryBackgroundColourId = 0x2340001,
|
||||
defaultTextColourId = 0x2340002,
|
||||
widgetTextColourId = 0x2340003,
|
||||
defaultButtonBackgroundColourId = 0x2340004,
|
||||
secondaryButtonBackgroundColourId = 0x2340005,
|
||||
userButtonBackgroundColourId = 0x2340006,
|
||||
defaultIconColourId = 0x2340007,
|
||||
treeIconColourId = 0x2340008,
|
||||
defaultHighlightColourId = 0x2340009,
|
||||
defaultHighlightedTextColourId = 0x234000a,
|
||||
codeEditorLineNumberColourId = 0x234000b,
|
||||
activeTabIconColourId = 0x234000c,
|
||||
inactiveTabBackgroundColourId = 0x234000d,
|
||||
inactiveTabIconColourId = 0x234000e,
|
||||
contentHeaderBackgroundColourId = 0x234000f,
|
||||
widgetBackgroundColourId = 0x2340010,
|
||||
secondaryWidgetBackgroundColourId = 0x2340011,
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int projucerMajorVersion = ProjectInfo::versionNumber >> 16;
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "jucer_CommonHeaders.h"
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "jucer_CommonHeaders.h"
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_Headers.h"
|
||||
|
||||
#include "jucer_Application.h"
|
||||
#include "../CodeEditor/jucer_OpenDocumentManager.h"
|
||||
#include "../CodeEditor/jucer_SourceCodeEditor.h"
|
||||
#include "../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h"
|
||||
#include "../Project/UI/jucer_ProjectContentComponent.h"
|
||||
#include "../Project/UI/Sidebar/jucer_TreeItemTypes.h"
|
||||
#include "Windows/jucer_UTF8WindowComponent.h"
|
||||
#include "Windows/jucer_SVGPathDataWindowComponent.h"
|
||||
#include "Windows/jucer_AboutWindowComponent.h"
|
||||
#include "Windows/jucer_EditorColourSchemeWindowComponent.h"
|
||||
#include "Windows/jucer_GlobalPathsWindowComponent.h"
|
||||
#include "Windows/jucer_PIPCreatorWindowComponent.h"
|
||||
#include "Windows/jucer_FloatingToolWindow.h"
|
||||
|
||||
#include "jucer_CommandLine.h"
|
||||
|
||||
#include "../Project/UI/jucer_ProjectContentComponent.cpp"
|
||||
#include "jucer_Application.cpp"
|
||||
|
||||
|
||||
START_JUCE_APPLICATION (ProjucerApplication)
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_Headers.h"
|
||||
|
||||
#include "jucer_Application.h"
|
||||
#include "../CodeEditor/jucer_OpenDocumentManager.h"
|
||||
#include "../CodeEditor/jucer_SourceCodeEditor.h"
|
||||
#include "../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h"
|
||||
#include "../Project/UI/jucer_ProjectContentComponent.h"
|
||||
#include "../Project/UI/Sidebar/jucer_TreeItemTypes.h"
|
||||
#include "Windows/jucer_UTF8WindowComponent.h"
|
||||
#include "Windows/jucer_SVGPathDataWindowComponent.h"
|
||||
#include "Windows/jucer_AboutWindowComponent.h"
|
||||
#include "Windows/jucer_EditorColourSchemeWindowComponent.h"
|
||||
#include "Windows/jucer_GlobalPathsWindowComponent.h"
|
||||
#include "Windows/jucer_PIPCreatorWindowComponent.h"
|
||||
#include "Windows/jucer_FloatingToolWindow.h"
|
||||
|
||||
#include "jucer_CommandLine.h"
|
||||
|
||||
#include "../Project/UI/jucer_ProjectContentComponent.cpp"
|
||||
#include "jucer_Application.cpp"
|
||||
|
||||
|
||||
START_JUCE_APPLICATION (ProjucerApplication)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,146 +1,148 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Utility/PIPs/jucer_PIPGenerator.h"
|
||||
#include "../Project/jucer_Project.h"
|
||||
#include "../CodeEditor/jucer_OpenDocumentManager.h"
|
||||
|
||||
class ProjectContentComponent;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The big top-level window where everything happens.
|
||||
*/
|
||||
class MainWindow : public DocumentWindow,
|
||||
public ApplicationCommandTarget,
|
||||
public FileDragAndDropTarget,
|
||||
public DragAndDropContainer,
|
||||
private Value::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MainWindow();
|
||||
~MainWindow() override;
|
||||
|
||||
enum class OpenInIDE { no, yes };
|
||||
|
||||
//==============================================================================
|
||||
void closeButtonPressed() override;
|
||||
|
||||
//==============================================================================
|
||||
bool canOpenFile (const File& file) const;
|
||||
void openFile (const File& file, std::function<void (bool)> callback);
|
||||
|
||||
void setProject (std::unique_ptr<Project> newProject);
|
||||
Project* getProject() const { return currentProject.get(); }
|
||||
|
||||
void makeVisible();
|
||||
void restoreWindowPosition();
|
||||
void updateTitleBarIcon();
|
||||
void closeCurrentProject (OpenDocumentManager::SaveIfNeeded askToSave, std::function<void (bool)> callback);
|
||||
void moveProject (File newProjectFile, OpenInIDE openInIDE);
|
||||
|
||||
void showStartPage();
|
||||
|
||||
void showLoginFormOverlay();
|
||||
void hideLoginFormOverlay();
|
||||
bool isShowingLoginForm() const noexcept { return loginFormOpen; }
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& files) override;
|
||||
void filesDropped (const StringArray& filenames, int mouseX, int mouseY) override;
|
||||
|
||||
void activeWindowStatusChanged() override;
|
||||
|
||||
ProjectContentComponent* getProjectContentComponent() const;
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* getNextCommandTarget() override;
|
||||
void getAllCommands (Array <CommandID>& commands) override;
|
||||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override;
|
||||
bool perform (const InvocationInfo& info) override;
|
||||
|
||||
bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails,
|
||||
StringArray& files, bool& canMoveFiles) override;
|
||||
private:
|
||||
void valueChanged (Value&) override;
|
||||
|
||||
static const char* getProjectWindowPosName() { return "projectWindowPos"; }
|
||||
void createProjectContentCompIfNeeded();
|
||||
|
||||
void openPIP (const File&, std::function<void (bool)> callback);
|
||||
void setupTemporaryPIPProject (PIPGenerator&);
|
||||
|
||||
void initialiseProjectWindow();
|
||||
|
||||
std::unique_ptr<Project> currentProject;
|
||||
Value projectNameValue;
|
||||
|
||||
std::unique_ptr<Component> blurOverlayComponent;
|
||||
bool loginFormOpen = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MainWindowList
|
||||
{
|
||||
public:
|
||||
MainWindowList();
|
||||
|
||||
void forceCloseAllWindows();
|
||||
void askAllWindowsToClose (std::function<void (bool)> callback);
|
||||
void closeWindow (MainWindow*);
|
||||
|
||||
void goToSiblingWindow (MainWindow*, int delta);
|
||||
|
||||
void createWindowIfNoneAreOpen();
|
||||
void openDocument (OpenDocumentManager::Document*, bool grabFocus);
|
||||
void openFile (const File& file, std::function<void (bool)> callback, bool openInBackground = false);
|
||||
|
||||
MainWindow* createNewMainWindow();
|
||||
MainWindow* getFrontmostWindow (bool createIfNotFound = true);
|
||||
MainWindow* getOrCreateEmptyWindow();
|
||||
MainWindow* getMainWindowForFile (const File&);
|
||||
MainWindow* getMainWindowWithLoginFormOpen();
|
||||
|
||||
Project* getFrontmostProject();
|
||||
|
||||
void reopenLastProjects();
|
||||
void saveCurrentlyOpenProjectList();
|
||||
|
||||
void checkWindowBounds (MainWindow&);
|
||||
|
||||
void sendLookAndFeelChange();
|
||||
|
||||
OwnedArray<MainWindow> windows;
|
||||
|
||||
private:
|
||||
bool isInReopenLastProjects = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindowList)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (MainWindowList)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Utility/PIPs/jucer_PIPGenerator.h"
|
||||
#include "../Project/jucer_Project.h"
|
||||
#include "../CodeEditor/jucer_OpenDocumentManager.h"
|
||||
|
||||
class ProjectContentComponent;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The big top-level window where everything happens.
|
||||
*/
|
||||
class MainWindow : public DocumentWindow,
|
||||
public ApplicationCommandTarget,
|
||||
public FileDragAndDropTarget,
|
||||
public DragAndDropContainer,
|
||||
private Value::Listener,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MainWindow();
|
||||
~MainWindow() override;
|
||||
|
||||
enum class OpenInIDE { no, yes };
|
||||
|
||||
//==============================================================================
|
||||
void closeButtonPressed() override;
|
||||
|
||||
//==============================================================================
|
||||
bool canOpenFile (const File& file) const;
|
||||
void openFile (const File& file, std::function<void (bool)> callback);
|
||||
|
||||
void setProject (std::unique_ptr<Project> newProject);
|
||||
Project* getProject() const { return currentProject.get(); }
|
||||
|
||||
void makeVisible();
|
||||
void restoreWindowPosition();
|
||||
void updateTitleBarIcon();
|
||||
void closeCurrentProject (OpenDocumentManager::SaveIfNeeded askToSave, std::function<void (bool)> callback);
|
||||
void moveProject (File newProjectFile, OpenInIDE openInIDE);
|
||||
|
||||
void showStartPage();
|
||||
|
||||
void showLoginFormOverlay();
|
||||
void hideLoginFormOverlay();
|
||||
bool isShowingLoginForm() const noexcept { return loginFormOpen; }
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& files) override;
|
||||
void filesDropped (const StringArray& filenames, int mouseX, int mouseY) override;
|
||||
|
||||
void activeWindowStatusChanged() override;
|
||||
|
||||
ProjectContentComponent* getProjectContentComponent() const;
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* getNextCommandTarget() override;
|
||||
void getAllCommands (Array <CommandID>& commands) override;
|
||||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override;
|
||||
bool perform (const InvocationInfo& info) override;
|
||||
|
||||
bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails,
|
||||
StringArray& files, bool& canMoveFiles) override;
|
||||
private:
|
||||
void valueChanged (Value&) override;
|
||||
void changeListenerCallback (ChangeBroadcaster* source) override;
|
||||
|
||||
static const char* getProjectWindowPosName() { return "projectWindowPos"; }
|
||||
void createProjectContentCompIfNeeded();
|
||||
|
||||
void openPIP (const File&, std::function<void (bool)> callback);
|
||||
void setupTemporaryPIPProject (PIPGenerator&);
|
||||
|
||||
void initialiseProjectWindow();
|
||||
|
||||
std::unique_ptr<Project> currentProject;
|
||||
Value projectNameValue;
|
||||
|
||||
std::unique_ptr<Component> blurOverlayComponent;
|
||||
bool loginFormOpen = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MainWindowList
|
||||
{
|
||||
public:
|
||||
MainWindowList();
|
||||
|
||||
void forceCloseAllWindows();
|
||||
void askAllWindowsToClose (std::function<void (bool)> callback);
|
||||
void closeWindow (MainWindow*);
|
||||
|
||||
void goToSiblingWindow (MainWindow*, int delta);
|
||||
|
||||
void createWindowIfNoneAreOpen();
|
||||
void openDocument (OpenDocumentManager::Document*, bool grabFocus);
|
||||
void openFile (const File& file, std::function<void (bool)> callback, bool openInBackground = false);
|
||||
|
||||
MainWindow* createNewMainWindow();
|
||||
MainWindow* getFrontmostWindow (bool createIfNotFound = true);
|
||||
MainWindow* getOrCreateEmptyWindow();
|
||||
MainWindow* getMainWindowForFile (const File&);
|
||||
MainWindow* getMainWindowWithLoginFormOpen();
|
||||
|
||||
Project* getFrontmostProject();
|
||||
|
||||
void reopenLastProjects();
|
||||
void saveCurrentlyOpenProjectList();
|
||||
|
||||
void checkWindowBounds (MainWindow&);
|
||||
|
||||
void sendLookAndFeelChange();
|
||||
|
||||
OwnedArray<MainWindow> windows;
|
||||
|
||||
private:
|
||||
bool isInReopenLastProjects = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindowList)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (MainWindowList)
|
||||
};
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128 128">
|
||||
<defs>
|
||||
<linearGradient id="linear-gradient" x1="40.69" y1="-676.56" x2="83.48" y2="-676.56" gradientTransform="matrix(1, 0, 0, -1, 0, -648.86)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#ed358c"/>
|
||||
<stop offset="0.16" stop-color="#e9388c"/>
|
||||
<stop offset="0.3" stop-color="#de418c"/>
|
||||
<stop offset="0.43" stop-color="#cc508c"/>
|
||||
<stop offset="0.57" stop-color="#b2658d"/>
|
||||
<stop offset="0.7" stop-color="#90808d"/>
|
||||
<stop offset="0.83" stop-color="#67a18e"/>
|
||||
<stop offset="0.95" stop-color="#37c78f"/>
|
||||
<stop offset="1" stop-color="#22d88f"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-2" x1="32.58" y1="-665.27" x2="13.76" y2="-791.59" gradientTransform="matrix(1, 0, 0, -1, 0, -648.86)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.09" stop-color="#22d88f"/>
|
||||
<stop offset="0.9" stop-color="#029de0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-3" x1="116.68" y1="-660.66" x2="-12.09" y2="-796.66" xlink:href="#linear-gradient-2"/>
|
||||
<linearGradient id="linear-gradient-4" x1="73.35" y1="-739.1" x2="122.29" y2="-746.06" xlink:href="#linear-gradient-2"/>
|
||||
</defs>
|
||||
<title>icon_CLion</title>
|
||||
<g>
|
||||
<polygon points="49.2 51.8 40.6 55.4 48.4 0 77.8 16.2 49.2 51.8" fill="url(#linear-gradient)"/>
|
||||
<polygon points="44.6 76.8 48.8 0 11.8 23.2 0 94 44.6 76.8" fill="url(#linear-gradient-2)"/>
|
||||
<polygon points="125.4 38.4 109 4.8 77.8 16.2 55 41.4 0 94 41.6 124.4 93.6 77.2 125.4 38.4" fill="url(#linear-gradient-3)"/>
|
||||
<polygon points="53.8 54.6 46.6 98.4 75.8 121 107.8 128 128 82.4 53.8 54.6" fill="url(#linear-gradient-4)"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="24" y="24" width="80" height="80"/>
|
||||
<rect x="31.6" y="89" width="30" height="5" fill="#fff"/>
|
||||
<path d="M31,51.2h0A16.83,16.83,0,0,1,48.2,34c6.2,0,10,2,13,5.2l-4.6,5.4c-2.6-2.4-5.2-3.8-8.4-3.8-5.6,0-9.6,4.6-9.6,10.4h0c0,5.6,4,10.4,9.6,10.4,3.8,0,6.2-1.6,8.8-3.8l4.6,4.6c-3.4,3.6-7.2,6-13.6,6A17,17,0,0,1,31,51.2" fill="#fff"/>
|
||||
<path d="M66.6,34.4H74v27H88.4v6.2H66.6V34.4Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,56 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::AnimatedAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
setFramesPerSecond (60); // This sets the frequency of the update calls.
|
||||
}
|
||||
|
||||
~%%content_component_class%%() override
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void update() override
|
||||
{
|
||||
// This function is called at the frequency specified by the setFramesPerSecond() call
|
||||
// in the constructor. You can use it to update counters, animate values, etc.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
// You can add your drawing code here!
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::AnimatedAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
setFramesPerSecond (60); // This sets the frequency of the update calls.
|
||||
}
|
||||
|
||||
~%%content_component_class%%() override
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void update() override
|
||||
{
|
||||
// This function is called at the frequency specified by the setFramesPerSecond() call
|
||||
// in the constructor. You can use it to update counters, animate values, etc.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
// You can add your drawing code here!
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%content_component_class%%::%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
setFramesPerSecond (60); // This sets the frequency of the update calls.
|
||||
}
|
||||
|
||||
%%content_component_class%%::~%%content_component_class%%()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::update()
|
||||
{
|
||||
// This function is called at the frequency specified by the setFramesPerSecond() call
|
||||
// in the constructor. You can use it to update counters, animate values, etc.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
// You can add your drawing code here!
|
||||
}
|
||||
|
||||
void %%content_component_class%%::resized()
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%content_component_class%%::%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
setFramesPerSecond (60); // This sets the frequency of the update calls.
|
||||
}
|
||||
|
||||
%%content_component_class%%::~%%content_component_class%%()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::update()
|
||||
{
|
||||
// This function is called at the frequency specified by the setFramesPerSecond() call
|
||||
// in the constructor. You can use it to update counters, animate values, etc.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
// You can add your drawing code here!
|
||||
}
|
||||
|
||||
void %%content_component_class%%::resized()
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::AnimatedAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%();
|
||||
~%%content_component_class%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void update() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::AnimatedAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%();
|
||||
~%%content_component_class%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void update() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::AudioAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
|
||||
// Some platforms require permissions to open input channels so request that here
|
||||
if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
|
||||
&& ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
|
||||
{
|
||||
juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
|
||||
[&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Specify the number of input and output channels that we want to open
|
||||
setAudioChannels (2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
~%%content_component_class%%() override
|
||||
{
|
||||
// This shuts down the audio device and clears the audio source.
|
||||
shutdownAudio();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
|
||||
{
|
||||
// This function will be called when the audio device is started, or when
|
||||
// its settings (i.e. sample rate, block size, etc) are changed.
|
||||
|
||||
// You can use this function to initialise any resources you might need,
|
||||
// but be careful - it will be called on the audio thread, not the GUI thread.
|
||||
|
||||
// For more details, see the help for AudioProcessor::prepareToPlay()
|
||||
}
|
||||
|
||||
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
|
||||
{
|
||||
// Your audio-processing code goes here!
|
||||
|
||||
// For more details, see the help for AudioProcessor::getNextAudioBlock()
|
||||
|
||||
// Right now we are not producing any data, in which case we need to clear the buffer
|
||||
// (to prevent the output of random noise)
|
||||
bufferToFill.clearActiveBufferRegion();
|
||||
}
|
||||
|
||||
void releaseResources() override
|
||||
{
|
||||
// This will be called when the audio device stops, or when it is being
|
||||
// restarted due to a setting change.
|
||||
|
||||
// For more details, see the help for AudioProcessor::releaseResources()
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
// You can add your drawing code here!
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::AudioAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
|
||||
// Some platforms require permissions to open input channels so request that here
|
||||
if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
|
||||
&& ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
|
||||
{
|
||||
juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
|
||||
[&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Specify the number of input and output channels that we want to open
|
||||
setAudioChannels (2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
~%%content_component_class%%() override
|
||||
{
|
||||
// This shuts down the audio device and clears the audio source.
|
||||
shutdownAudio();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
|
||||
{
|
||||
// This function will be called when the audio device is started, or when
|
||||
// its settings (i.e. sample rate, block size, etc) are changed.
|
||||
|
||||
// You can use this function to initialise any resources you might need,
|
||||
// but be careful - it will be called on the audio thread, not the GUI thread.
|
||||
|
||||
// For more details, see the help for AudioProcessor::prepareToPlay()
|
||||
}
|
||||
|
||||
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
|
||||
{
|
||||
// Your audio-processing code goes here!
|
||||
|
||||
// For more details, see the help for AudioProcessor::getNextAudioBlock()
|
||||
|
||||
// Right now we are not producing any data, in which case we need to clear the buffer
|
||||
// (to prevent the output of random noise)
|
||||
bufferToFill.clearActiveBufferRegion();
|
||||
}
|
||||
|
||||
void releaseResources() override
|
||||
{
|
||||
// This will be called when the audio device stops, or when it is being
|
||||
// restarted due to a setting change.
|
||||
|
||||
// For more details, see the help for AudioProcessor::releaseResources()
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
// You can add your drawing code here!
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%content_component_class%%::%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
|
||||
// Some platforms require permissions to open input channels so request that here
|
||||
if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
|
||||
&& ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
|
||||
{
|
||||
juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
|
||||
[&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Specify the number of input and output channels that we want to open
|
||||
setAudioChannels (2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
%%content_component_class%%::~%%content_component_class%%()
|
||||
{
|
||||
// This shuts down the audio device and clears the audio source.
|
||||
shutdownAudio();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
|
||||
{
|
||||
// This function will be called when the audio device is started, or when
|
||||
// its settings (i.e. sample rate, block size, etc) are changed.
|
||||
|
||||
// You can use this function to initialise any resources you might need,
|
||||
// but be careful - it will be called on the audio thread, not the GUI thread.
|
||||
|
||||
// For more details, see the help for AudioProcessor::prepareToPlay()
|
||||
}
|
||||
|
||||
void %%content_component_class%%::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
|
||||
{
|
||||
// Your audio-processing code goes here!
|
||||
|
||||
// For more details, see the help for AudioProcessor::getNextAudioBlock()
|
||||
|
||||
// Right now we are not producing any data, in which case we need to clear the buffer
|
||||
// (to prevent the output of random noise)
|
||||
bufferToFill.clearActiveBufferRegion();
|
||||
}
|
||||
|
||||
void %%content_component_class%%::releaseResources()
|
||||
{
|
||||
// This will be called when the audio device stops, or when it is being
|
||||
// restarted due to a setting change.
|
||||
|
||||
// For more details, see the help for AudioProcessor::releaseResources()
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
// You can add your drawing code here!
|
||||
}
|
||||
|
||||
void %%content_component_class%%::resized()
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%content_component_class%%::%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
|
||||
// Some platforms require permissions to open input channels so request that here
|
||||
if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
|
||||
&& ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
|
||||
{
|
||||
juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
|
||||
[&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Specify the number of input and output channels that we want to open
|
||||
setAudioChannels (2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
%%content_component_class%%::~%%content_component_class%%()
|
||||
{
|
||||
// This shuts down the audio device and clears the audio source.
|
||||
shutdownAudio();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
|
||||
{
|
||||
// This function will be called when the audio device is started, or when
|
||||
// its settings (i.e. sample rate, block size, etc) are changed.
|
||||
|
||||
// You can use this function to initialise any resources you might need,
|
||||
// but be careful - it will be called on the audio thread, not the GUI thread.
|
||||
|
||||
// For more details, see the help for AudioProcessor::prepareToPlay()
|
||||
}
|
||||
|
||||
void %%content_component_class%%::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
|
||||
{
|
||||
// Your audio-processing code goes here!
|
||||
|
||||
// For more details, see the help for AudioProcessor::getNextAudioBlock()
|
||||
|
||||
// Right now we are not producing any data, in which case we need to clear the buffer
|
||||
// (to prevent the output of random noise)
|
||||
bufferToFill.clearActiveBufferRegion();
|
||||
}
|
||||
|
||||
void %%content_component_class%%::releaseResources()
|
||||
{
|
||||
// This will be called when the audio device stops, or when it is being
|
||||
// restarted due to a setting change.
|
||||
|
||||
// For more details, see the help for AudioProcessor::releaseResources()
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
// You can add your drawing code here!
|
||||
}
|
||||
|
||||
void %%content_component_class%%::resized()
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::AudioAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%();
|
||||
~%%content_component_class%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
|
||||
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override;
|
||||
void releaseResources() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::AudioAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%();
|
||||
~%%content_component_class%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
|
||||
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override;
|
||||
void releaseResources() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
It contains the basic framework code for an ARA document controller implementation.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%aradocumentcontroller_headers%%
|
||||
%%araplaybackrenderer_headers%%
|
||||
|
||||
//==============================================================================
|
||||
juce::ARAPlaybackRenderer* %%aradocumentcontroller_class_name%%::doCreatePlaybackRenderer() noexcept
|
||||
{
|
||||
return new %%araplaybackrenderer_class_name%% (getDocumentController());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool %%aradocumentcontroller_class_name%%::doRestoreObjectsFromStream (juce::ARAInputStream& input, const juce::ARARestoreObjectsFilter* filter) noexcept
|
||||
{
|
||||
// You should use this method to read any persistent data associated with
|
||||
// your ARA model graph stored in an archive using the supplied ARAInputStream.
|
||||
// Be sure to check the ARARestoreObjectsFilter to determine which objects to restore.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool %%aradocumentcontroller_class_name%%::doStoreObjectsToStream (juce::ARAOutputStream& output, const juce::ARAStoreObjectsFilter* filter) noexcept
|
||||
{
|
||||
// You should use this method to write any persistent data associated with
|
||||
// your ARA model graph into the an archive using the supplied ARAOutputStream.
|
||||
// Be sure to check the ARAStoreObjectsFilter to determine which objects to store.
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// This creates the static ARAFactory instances for the plugin.
|
||||
const ARA::ARAFactory* JUCE_CALLTYPE createARAFactory()
|
||||
{
|
||||
return juce::ARADocumentControllerSpecialisation::createARAFactory<%%aradocumentcontroller_class_name%%>();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
It contains the basic framework code for an ARA document controller implementation.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <juce_audio_processors/juce_audio_processors.h>
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class %%aradocumentcontroller_class_name%% : public juce::ARADocumentControllerSpecialisation
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using ARADocumentControllerSpecialisation::ARADocumentControllerSpecialisation;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
// Override document controller customization methods here
|
||||
|
||||
juce::ARAPlaybackRenderer* doCreatePlaybackRenderer() noexcept override;
|
||||
|
||||
bool doRestoreObjectsFromStream (juce::ARAInputStream& input, const juce::ARARestoreObjectsFilter* filter) noexcept override;
|
||||
bool doStoreObjectsToStream (juce::ARAOutputStream& output, const juce::ARAStoreObjectsFilter* filter) noexcept override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%aradocumentcontroller_class_name%%)
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
It contains the basic framework code for an ARA playback renderer implementation.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%araplaybackrenderer_headers%%
|
||||
|
||||
//==============================================================================
|
||||
void %%araplaybackrenderer_class_name%%::prepareToPlay (double sampleRateIn, int maximumSamplesPerBlockIn, int numChannelsIn, juce::AudioProcessor::ProcessingPrecision, AlwaysNonRealtime alwaysNonRealtime)
|
||||
{
|
||||
numChannels = numChannelsIn;
|
||||
sampleRate = sampleRateIn;
|
||||
maximumSamplesPerBlock = maximumSamplesPerBlockIn;
|
||||
useBufferedAudioSourceReader = alwaysNonRealtime == AlwaysNonRealtime::no;
|
||||
}
|
||||
|
||||
void %%araplaybackrenderer_class_name%%::releaseResources()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool %%araplaybackrenderer_class_name%%::processBlock (juce::AudioBuffer<float>& buffer,
|
||||
juce::AudioProcessor::Realtime realtime,
|
||||
const juce::AudioPlayHead::PositionInfo& positionInfo) noexcept
|
||||
{
|
||||
const auto numSamples = buffer.getNumSamples();
|
||||
jassert (numSamples <= maximumSamplesPerBlock);
|
||||
jassert (numChannels == buffer.getNumChannels());
|
||||
jassert (realtime == juce::AudioProcessor::Realtime::no || useBufferedAudioSourceReader);
|
||||
const auto timeInSamples = positionInfo.getTimeInSamples().orFallback (0);
|
||||
const auto isPlaying = positionInfo.getIsPlaying();
|
||||
|
||||
bool success = true;
|
||||
bool didRenderAnyRegion = false;
|
||||
|
||||
if (isPlaying)
|
||||
{
|
||||
const auto blockRange = juce::Range<juce::int64>::withStartAndLength (timeInSamples, numSamples);
|
||||
|
||||
for (const auto& playbackRegion : getPlaybackRegions())
|
||||
{
|
||||
// Evaluate region borders in song time, calculate sample range to render in song time.
|
||||
// Note that this example does not use head- or tailtime, so the includeHeadAndTail
|
||||
// parameter is set to false here - this might need to be adjusted in actual plug-ins.
|
||||
const auto playbackSampleRange = playbackRegion->getSampleRange (sampleRate,
|
||||
juce::ARAPlaybackRegion::IncludeHeadAndTail::no);
|
||||
auto renderRange = blockRange.getIntersectionWith (playbackSampleRange);
|
||||
|
||||
if (renderRange.isEmpty())
|
||||
continue;
|
||||
|
||||
// Evaluate region borders in modification/source time and calculate offset between
|
||||
// song and source samples, then clip song samples accordingly
|
||||
// (if an actual plug-in supports time stretching, this must be taken into account here).
|
||||
juce::Range<juce::int64> modificationSampleRange { playbackRegion->getStartInAudioModificationSamples(),
|
||||
playbackRegion->getEndInAudioModificationSamples() };
|
||||
const auto modificationSampleOffset = modificationSampleRange.getStart() - playbackSampleRange.getStart();
|
||||
|
||||
renderRange = renderRange.getIntersectionWith (modificationSampleRange.movedToStartAt (playbackSampleRange.getStart()));
|
||||
|
||||
if (renderRange.isEmpty())
|
||||
continue;
|
||||
|
||||
// Now calculate the samples in renderRange for this PlaybackRegion based on the ARA model
|
||||
// graph. If didRenderAnyRegion is true, add the region's output samples in renderRange to
|
||||
// the buffer. Otherwise the buffer needs to be initialised so the sample value must be
|
||||
// overwritten.
|
||||
const int numSamplesToRead = (int) renderRange.getLength();
|
||||
const int startInBuffer = (int) (renderRange.getStart() - blockRange.getStart());
|
||||
const auto startInSource = renderRange.getStart() + modificationSampleOffset;
|
||||
|
||||
for (int c = 0; c < numChannels; ++c)
|
||||
{
|
||||
auto* channelData = buffer.getWritePointer (c);
|
||||
|
||||
for (int i = 0; i < numSamplesToRead; ++i)
|
||||
{
|
||||
// Calculate region output sample at index startInSource + i ...
|
||||
float sample = 0.0f;
|
||||
|
||||
if (didRenderAnyRegion)
|
||||
channelData[startInBuffer + i] += sample;
|
||||
else
|
||||
channelData[startInBuffer + i] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
// If rendering first region, clear any excess at start or end of the region.
|
||||
if (! didRenderAnyRegion)
|
||||
{
|
||||
if (startInBuffer != 0)
|
||||
buffer.clear (0, startInBuffer);
|
||||
|
||||
const int endInBuffer = startInBuffer + numSamples;
|
||||
const int remainingSamples = numSamples - endInBuffer;
|
||||
|
||||
if (remainingSamples != 0)
|
||||
buffer.clear (endInBuffer, remainingSamples);
|
||||
|
||||
didRenderAnyRegion = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! didRenderAnyRegion)
|
||||
buffer.clear();
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file was auto-generated!
|
||||
|
||||
It contains the basic framework code for an ARA playback renderer implementation.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <juce_audio_processors/juce_audio_processors.h>
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class %%araplaybackrenderer_class_name%% : public juce::ARAPlaybackRenderer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using juce::ARAPlaybackRenderer::ARAPlaybackRenderer;
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (double sampleRate,
|
||||
int maximumSamplesPerBlock,
|
||||
int numChannels,
|
||||
juce::AudioProcessor::ProcessingPrecision,
|
||||
AlwaysNonRealtime alwaysNonRealtime) override;
|
||||
void releaseResources() override;
|
||||
|
||||
//==============================================================================
|
||||
bool processBlock (juce::AudioBuffer<float>& buffer,
|
||||
juce::AudioProcessor::Realtime realtime,
|
||||
const juce::AudioPlayHead::PositionInfo& positionInfo) noexcept override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
double sampleRate = 44100.0;
|
||||
int maximumSamplesPerBlock = 4096;
|
||||
int numChannels = 1;
|
||||
bool useBufferedAudioSourceReader = true;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%araplaybackrenderer_class_name%%)
|
||||
};
|
||||
@@ -1,39 +1,39 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin editor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%editor_cpp_headers%%
|
||||
|
||||
//==============================================================================
|
||||
%%editor_class_name%%::%%editor_class_name%% (%%filter_class_name%%& p)
|
||||
: AudioProcessorEditor (&p), audioProcessor (p)
|
||||
{
|
||||
// Make sure that before the constructor has finished, you've set the
|
||||
// editor's size to whatever you need it to be.
|
||||
setSize (400, 300);
|
||||
}
|
||||
|
||||
%%editor_class_name%%::~%%editor_class_name%%()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%editor_class_name%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
g.setColour (juce::Colours::white);
|
||||
g.setFont (15.0f);
|
||||
g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
|
||||
}
|
||||
|
||||
void %%editor_class_name%%::resized()
|
||||
{
|
||||
// This is generally where you'll want to lay out the positions of any
|
||||
// subcomponents in your editor..
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin editor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%editor_cpp_headers%%
|
||||
|
||||
//==============================================================================
|
||||
%%editor_class_name%%::%%editor_class_name%% (%%filter_class_name%%& p)
|
||||
: AudioProcessorEditor (&p), audioProcessor (p)
|
||||
{
|
||||
// Make sure that before the constructor has finished, you've set the
|
||||
// editor's size to whatever you need it to be.
|
||||
setSize (400, 300);
|
||||
}
|
||||
|
||||
%%editor_class_name%%::~%%editor_class_name%%()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%editor_class_name%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
g.setColour (juce::Colours::white);
|
||||
g.setFont (15.0f);
|
||||
g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
|
||||
}
|
||||
|
||||
void %%editor_class_name%%::resized()
|
||||
{
|
||||
// This is generally where you'll want to lay out the positions of any
|
||||
// subcomponents in your editor..
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin editor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
%%editor_headers%%
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class %%editor_class_name%% : public juce::AudioProcessorEditor
|
||||
{
|
||||
public:
|
||||
%%editor_class_name%% (%%filter_class_name%%&);
|
||||
~%%editor_class_name%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
// This reference is provided as a quick way for your editor to
|
||||
// access the processor object that created it.
|
||||
%%filter_class_name%%& audioProcessor;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%editor_class_name%%)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin editor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
%%editor_headers%%
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class %%editor_class_name%% : public juce::AudioProcessorEditor
|
||||
{
|
||||
public:
|
||||
%%editor_class_name%% (%%filter_class_name%%&);
|
||||
~%%editor_class_name%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
// This reference is provided as a quick way for your editor to
|
||||
// access the processor object that created it.
|
||||
%%filter_class_name%%& audioProcessor;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%editor_class_name%%)
|
||||
};
|
||||
|
||||
@@ -1,190 +1,190 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin processor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%filter_headers%%
|
||||
|
||||
//==============================================================================
|
||||
%%filter_class_name%%::%%filter_class_name%%()
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
: AudioProcessor (BusesProperties()
|
||||
#if ! JucePlugin_IsMidiEffect
|
||||
#if ! JucePlugin_IsSynth
|
||||
.withInput ("Input", juce::AudioChannelSet::stereo(), true)
|
||||
#endif
|
||||
.withOutput ("Output", juce::AudioChannelSet::stereo(), true)
|
||||
#endif
|
||||
)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
%%filter_class_name%%::~%%filter_class_name%%()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const juce::String %%filter_class_name%%::getName() const
|
||||
{
|
||||
return JucePlugin_Name;
|
||||
}
|
||||
|
||||
bool %%filter_class_name%%::acceptsMidi() const
|
||||
{
|
||||
#if JucePlugin_WantsMidiInput
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool %%filter_class_name%%::producesMidi() const
|
||||
{
|
||||
#if JucePlugin_ProducesMidiOutput
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool %%filter_class_name%%::isMidiEffect() const
|
||||
{
|
||||
#if JucePlugin_IsMidiEffect
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
double %%filter_class_name%%::getTailLengthSeconds() const
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int %%filter_class_name%%::getNumPrograms()
|
||||
{
|
||||
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
|
||||
// so this should be at least 1, even if you're not really implementing programs.
|
||||
}
|
||||
|
||||
int %%filter_class_name%%::getCurrentProgram()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void %%filter_class_name%%::setCurrentProgram (int index)
|
||||
{
|
||||
}
|
||||
|
||||
const juce::String %%filter_class_name%%::getProgramName (int index)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void %%filter_class_name%%::changeProgramName (int index, const juce::String& newName)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%filter_class_name%%::prepareToPlay (double sampleRate, int samplesPerBlock)
|
||||
{
|
||||
// Use this method as the place to do any pre-playback
|
||||
// initialisation that you need..
|
||||
}
|
||||
|
||||
void %%filter_class_name%%::releaseResources()
|
||||
{
|
||||
// When playback stops, you can use this as an opportunity to free up any
|
||||
// spare memory, etc.
|
||||
}
|
||||
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool %%filter_class_name%%::isBusesLayoutSupported (const BusesLayout& layouts) const
|
||||
{
|
||||
#if JucePlugin_IsMidiEffect
|
||||
juce::ignoreUnused (layouts);
|
||||
return true;
|
||||
#else
|
||||
// This is the place where you check if the layout is supported.
|
||||
// In this template code we only support mono or stereo.
|
||||
// Some plugin hosts, such as certain GarageBand versions, will only
|
||||
// load plugins that support stereo bus layouts.
|
||||
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
|
||||
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
|
||||
return false;
|
||||
|
||||
// This checks if the input layout matches the output layout
|
||||
#if ! JucePlugin_IsSynth
|
||||
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void %%filter_class_name%%::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
|
||||
{
|
||||
juce::ScopedNoDenormals noDenormals;
|
||||
auto totalNumInputChannels = getTotalNumInputChannels();
|
||||
auto totalNumOutputChannels = getTotalNumOutputChannels();
|
||||
|
||||
// In case we have more outputs than inputs, this code clears any output
|
||||
// channels that didn't contain input data, (because these aren't
|
||||
// guaranteed to be empty - they may contain garbage).
|
||||
// This is here to avoid people getting screaming feedback
|
||||
// when they first compile a plugin, but obviously you don't need to keep
|
||||
// this code if your algorithm always overwrites all the output channels.
|
||||
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
||||
buffer.clear (i, 0, buffer.getNumSamples());
|
||||
|
||||
// This is the place where you'd normally do the guts of your plugin's
|
||||
// audio processing...
|
||||
// Make sure to reset the state if your inner loop is processing
|
||||
// the samples and the outer loop is handling the channels.
|
||||
// Alternatively, you can process the samples with the channels
|
||||
// interleaved by keeping the same state.
|
||||
for (int channel = 0; channel < totalNumInputChannels; ++channel)
|
||||
{
|
||||
auto* channelData = buffer.getWritePointer (channel);
|
||||
|
||||
// ..do something to the data...
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool %%filter_class_name%%::hasEditor() const
|
||||
{
|
||||
return true; // (change this to false if you choose to not supply an editor)
|
||||
}
|
||||
|
||||
juce::AudioProcessorEditor* %%filter_class_name%%::createEditor()
|
||||
{
|
||||
return new %%editor_class_name%% (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%filter_class_name%%::getStateInformation (juce::MemoryBlock& destData)
|
||||
{
|
||||
// You should use this method to store your parameters in the memory block.
|
||||
// You could do that either as raw data, or use the XML or ValueTree classes
|
||||
// as intermediaries to make it easy to save and load complex data.
|
||||
}
|
||||
|
||||
void %%filter_class_name%%::setStateInformation (const void* data, int sizeInBytes)
|
||||
{
|
||||
// You should use this method to restore your parameters from this memory block,
|
||||
// whose contents will have been created by the getStateInformation() call.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// This creates new instances of the plugin..
|
||||
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
||||
{
|
||||
return new %%filter_class_name%%();
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin processor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%filter_headers%%
|
||||
|
||||
//==============================================================================
|
||||
%%filter_class_name%%::%%filter_class_name%%()
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
: AudioProcessor (BusesProperties()
|
||||
#if ! JucePlugin_IsMidiEffect
|
||||
#if ! JucePlugin_IsSynth
|
||||
.withInput ("Input", juce::AudioChannelSet::stereo(), true)
|
||||
#endif
|
||||
.withOutput ("Output", juce::AudioChannelSet::stereo(), true)
|
||||
#endif
|
||||
)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
%%filter_class_name%%::~%%filter_class_name%%()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const juce::String %%filter_class_name%%::getName() const
|
||||
{
|
||||
return JucePlugin_Name;
|
||||
}
|
||||
|
||||
bool %%filter_class_name%%::acceptsMidi() const
|
||||
{
|
||||
#if JucePlugin_WantsMidiInput
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool %%filter_class_name%%::producesMidi() const
|
||||
{
|
||||
#if JucePlugin_ProducesMidiOutput
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool %%filter_class_name%%::isMidiEffect() const
|
||||
{
|
||||
#if JucePlugin_IsMidiEffect
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
double %%filter_class_name%%::getTailLengthSeconds() const
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int %%filter_class_name%%::getNumPrograms()
|
||||
{
|
||||
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
|
||||
// so this should be at least 1, even if you're not really implementing programs.
|
||||
}
|
||||
|
||||
int %%filter_class_name%%::getCurrentProgram()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void %%filter_class_name%%::setCurrentProgram (int index)
|
||||
{
|
||||
}
|
||||
|
||||
const juce::String %%filter_class_name%%::getProgramName (int index)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void %%filter_class_name%%::changeProgramName (int index, const juce::String& newName)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%filter_class_name%%::prepareToPlay (double sampleRate, int samplesPerBlock)
|
||||
{
|
||||
// Use this method as the place to do any pre-playback
|
||||
// initialisation that you need..
|
||||
}
|
||||
|
||||
void %%filter_class_name%%::releaseResources()
|
||||
{
|
||||
// When playback stops, you can use this as an opportunity to free up any
|
||||
// spare memory, etc.
|
||||
}
|
||||
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool %%filter_class_name%%::isBusesLayoutSupported (const BusesLayout& layouts) const
|
||||
{
|
||||
#if JucePlugin_IsMidiEffect
|
||||
juce::ignoreUnused (layouts);
|
||||
return true;
|
||||
#else
|
||||
// This is the place where you check if the layout is supported.
|
||||
// In this template code we only support mono or stereo.
|
||||
// Some plugin hosts, such as certain GarageBand versions, will only
|
||||
// load plugins that support stereo bus layouts.
|
||||
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
|
||||
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
|
||||
return false;
|
||||
|
||||
// This checks if the input layout matches the output layout
|
||||
#if ! JucePlugin_IsSynth
|
||||
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void %%filter_class_name%%::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
|
||||
{
|
||||
juce::ScopedNoDenormals noDenormals;
|
||||
auto totalNumInputChannels = getTotalNumInputChannels();
|
||||
auto totalNumOutputChannels = getTotalNumOutputChannels();
|
||||
|
||||
// In case we have more outputs than inputs, this code clears any output
|
||||
// channels that didn't contain input data, (because these aren't
|
||||
// guaranteed to be empty - they may contain garbage).
|
||||
// This is here to avoid people getting screaming feedback
|
||||
// when they first compile a plugin, but obviously you don't need to keep
|
||||
// this code if your algorithm always overwrites all the output channels.
|
||||
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
||||
buffer.clear (i, 0, buffer.getNumSamples());
|
||||
|
||||
// This is the place where you'd normally do the guts of your plugin's
|
||||
// audio processing...
|
||||
// Make sure to reset the state if your inner loop is processing
|
||||
// the samples and the outer loop is handling the channels.
|
||||
// Alternatively, you can process the samples with the channels
|
||||
// interleaved by keeping the same state.
|
||||
for (int channel = 0; channel < totalNumInputChannels; ++channel)
|
||||
{
|
||||
auto* channelData = buffer.getWritePointer (channel);
|
||||
|
||||
// ..do something to the data...
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool %%filter_class_name%%::hasEditor() const
|
||||
{
|
||||
return true; // (change this to false if you choose to not supply an editor)
|
||||
}
|
||||
|
||||
juce::AudioProcessorEditor* %%filter_class_name%%::createEditor()
|
||||
{
|
||||
return new %%editor_class_name%% (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%filter_class_name%%::getStateInformation (juce::MemoryBlock& destData)
|
||||
{
|
||||
// You should use this method to store your parameters in the memory block.
|
||||
// You could do that either as raw data, or use the XML or ValueTree classes
|
||||
// as intermediaries to make it easy to save and load complex data.
|
||||
}
|
||||
|
||||
void %%filter_class_name%%::setStateInformation (const void* data, int sizeInBytes)
|
||||
{
|
||||
// You should use this method to restore your parameters from this memory block,
|
||||
// whose contents will have been created by the getStateInformation() call.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// This creates new instances of the plugin..
|
||||
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
||||
{
|
||||
return new %%filter_class_name%%();
|
||||
}
|
||||
|
||||
@@ -1,59 +1,62 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin processor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
%%app_headers%%
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class %%filter_class_name%% : public juce::AudioProcessor
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%filter_class_name%%();
|
||||
~%%filter_class_name%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
|
||||
void releaseResources() override;
|
||||
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
|
||||
#endif
|
||||
|
||||
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
|
||||
|
||||
//==============================================================================
|
||||
juce::AudioProcessorEditor* createEditor() override;
|
||||
bool hasEditor() const override;
|
||||
|
||||
//==============================================================================
|
||||
const juce::String getName() const override;
|
||||
|
||||
bool acceptsMidi() const override;
|
||||
bool producesMidi() const override;
|
||||
bool isMidiEffect() const override;
|
||||
double getTailLengthSeconds() const override;
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override;
|
||||
int getCurrentProgram() override;
|
||||
void setCurrentProgram (int index) override;
|
||||
const juce::String getProgramName (int index) override;
|
||||
void changeProgramName (int index, const juce::String& newName) override;
|
||||
|
||||
//==============================================================================
|
||||
void getStateInformation (juce::MemoryBlock& destData) override;
|
||||
void setStateInformation (const void* data, int sizeInBytes) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%filter_class_name%%)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic framework code for a JUCE plugin processor.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
%%app_headers%%
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class %%filter_class_name%% : public juce::AudioProcessor
|
||||
#if JucePlugin_Enable_ARA
|
||||
, public juce::AudioProcessorARAExtension
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%filter_class_name%%();
|
||||
~%%filter_class_name%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
|
||||
void releaseResources() override;
|
||||
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
|
||||
#endif
|
||||
|
||||
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
|
||||
|
||||
//==============================================================================
|
||||
juce::AudioProcessorEditor* createEditor() override;
|
||||
bool hasEditor() const override;
|
||||
|
||||
//==============================================================================
|
||||
const juce::String getName() const override;
|
||||
|
||||
bool acceptsMidi() const override;
|
||||
bool producesMidi() const override;
|
||||
bool isMidiEffect() const override;
|
||||
double getTailLengthSeconds() const override;
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override;
|
||||
int getCurrentProgram() override;
|
||||
void setCurrentProgram (int index) override;
|
||||
const juce::String getProgramName (int index) override;
|
||||
void changeProgramName (int index, const juce::String& newName) override;
|
||||
|
||||
//==============================================================================
|
||||
void getStateInformation (juce::MemoryBlock& destData) override;
|
||||
void setStateInformation (const void* data, int sizeInBytes) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%filter_class_name%%)
|
||||
};
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This is an automatically generated GUI class created by the Projucer!
|
||||
|
||||
Be careful when adding custom code to these files, as only the code within
|
||||
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
|
||||
and re-saved.
|
||||
|
||||
Created with Projucer version: %%version%%
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
The Projucer is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
//[Headers] You can add your own extra header files here...
|
||||
//[/Headers]
|
||||
|
||||
%%include_files_cpp%%
|
||||
|
||||
//[MiscUserDefs] You can add your own user definitions and misc code here...
|
||||
//[/MiscUserDefs]
|
||||
|
||||
//==============================================================================
|
||||
%%class_name%%::%%class_name%% (%%constructor_params%%)
|
||||
%%initialisers%%{
|
||||
//[Constructor_pre] You can add your own custom stuff here..
|
||||
//[/Constructor_pre]
|
||||
|
||||
%%constructor%%
|
||||
|
||||
//[Constructor] You can add your own custom stuff here..
|
||||
//[/Constructor]
|
||||
}
|
||||
|
||||
%%class_name%%::~%%class_name%%()
|
||||
{
|
||||
//[Destructor_pre]. You can add your own custom destruction code here..
|
||||
//[/Destructor_pre]
|
||||
|
||||
%%destructor%%
|
||||
|
||||
//[Destructor]. You can add your own custom destruction code here..
|
||||
//[/Destructor]
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
%%method_definitions%%
|
||||
|
||||
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
|
||||
//[/MiscUserCode]
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if 0
|
||||
/* -- Projucer information section --
|
||||
|
||||
This is where the Projucer stores the metadata that describe this GUI layout, so
|
||||
make changes in here at your peril!
|
||||
|
||||
BEGIN_JUCER_METADATA
|
||||
|
||||
%%metadata%%
|
||||
END_JUCER_METADATA
|
||||
*/
|
||||
#endif
|
||||
|
||||
%%static_member_definitions%%
|
||||
//[EndFile] You can add extra defines here...
|
||||
//[/EndFile]
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This is an automatically generated GUI class created by the Projucer!
|
||||
|
||||
Be careful when adding custom code to these files, as only the code within
|
||||
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
|
||||
and re-saved.
|
||||
|
||||
Created with Projucer version: %%version%%
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
The Projucer is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
//[Headers] You can add your own extra header files here...
|
||||
//[/Headers]
|
||||
|
||||
%%include_files_cpp%%
|
||||
|
||||
//[MiscUserDefs] You can add your own user definitions and misc code here...
|
||||
//[/MiscUserDefs]
|
||||
|
||||
//==============================================================================
|
||||
%%class_name%%::%%class_name%% (%%constructor_params%%)
|
||||
%%initialisers%%{
|
||||
//[Constructor_pre] You can add your own custom stuff here..
|
||||
//[/Constructor_pre]
|
||||
|
||||
%%constructor%%
|
||||
|
||||
//[Constructor] You can add your own custom stuff here..
|
||||
//[/Constructor]
|
||||
}
|
||||
|
||||
%%class_name%%::~%%class_name%%()
|
||||
{
|
||||
//[Destructor_pre]. You can add your own custom destruction code here..
|
||||
//[/Destructor_pre]
|
||||
|
||||
%%destructor%%
|
||||
|
||||
//[Destructor]. You can add your own custom destruction code here..
|
||||
//[/Destructor]
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
%%method_definitions%%
|
||||
|
||||
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
|
||||
//[/MiscUserCode]
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if 0
|
||||
/* -- Projucer information section --
|
||||
|
||||
This is where the Projucer stores the metadata that describe this GUI layout, so
|
||||
make changes in here at your peril!
|
||||
|
||||
BEGIN_JUCER_METADATA
|
||||
|
||||
%%metadata%%
|
||||
END_JUCER_METADATA
|
||||
*/
|
||||
#endif
|
||||
|
||||
%%static_member_definitions%%
|
||||
//[EndFile] You can add extra defines here...
|
||||
//[/EndFile]
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This is an automatically generated GUI class created by the Projucer!
|
||||
|
||||
Be careful when adding custom code to these files, as only the code within
|
||||
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
|
||||
and re-saved.
|
||||
|
||||
Created with Projucer version: %%version%%
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
The Projucer is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//[Headers] -- You can add your own extra header files here --
|
||||
%%include_juce%%
|
||||
//[/Headers]
|
||||
|
||||
%%include_files_h%%
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
//[Comments]
|
||||
An auto-generated component, created by the Projucer.
|
||||
|
||||
Describe your class and how it works here!
|
||||
//[/Comments]
|
||||
*/
|
||||
%%class_declaration%%
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%class_name%% (%%constructor_params%%);
|
||||
~%%class_name%%() override;
|
||||
|
||||
//==============================================================================
|
||||
//[UserMethods] -- You can add your own custom methods in this section.
|
||||
//[/UserMethods]
|
||||
|
||||
%%public_member_declarations%%
|
||||
|
||||
private:
|
||||
//[UserVariables] -- You can add your own custom variables in this section.
|
||||
//[/UserVariables]
|
||||
|
||||
//==============================================================================
|
||||
%%private_member_declarations%%
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%class_name%%)
|
||||
};
|
||||
|
||||
//[EndFile] You can add extra defines here...
|
||||
//[/EndFile]
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This is an automatically generated GUI class created by the Projucer!
|
||||
|
||||
Be careful when adding custom code to these files, as only the code within
|
||||
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
|
||||
and re-saved.
|
||||
|
||||
Created with Projucer version: %%version%%
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
The Projucer is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//[Headers] -- You can add your own extra header files here --
|
||||
%%include_juce%%
|
||||
//[/Headers]
|
||||
|
||||
%%include_files_h%%
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
//[Comments]
|
||||
An auto-generated component, created by the Projucer.
|
||||
|
||||
Describe your class and how it works here!
|
||||
//[/Comments]
|
||||
*/
|
||||
%%class_declaration%%
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%class_name%% (%%constructor_params%%);
|
||||
~%%class_name%%() override;
|
||||
|
||||
//==============================================================================
|
||||
//[UserMethods] -- You can add your own custom methods in this section.
|
||||
//[/UserMethods]
|
||||
|
||||
%%public_member_declarations%%
|
||||
|
||||
private:
|
||||
//[UserVariables] -- You can add your own custom variables in this section.
|
||||
//[/UserVariables]
|
||||
|
||||
//==============================================================================
|
||||
%%private_member_declarations%%
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%class_name%%)
|
||||
};
|
||||
|
||||
//[EndFile] You can add extra defines here...
|
||||
//[/EndFile]
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%()
|
||||
{
|
||||
setSize (600, 400);
|
||||
}
|
||||
|
||||
~%%content_component_class%%() override
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
g.setFont (juce::Font (16.0f));
|
||||
g.setColour (juce::Colours::white);
|
||||
g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the %%content_component_class%% is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%()
|
||||
{
|
||||
setSize (600, 400);
|
||||
}
|
||||
|
||||
~%%content_component_class%%() override
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
g.setFont (juce::Font (16.0f));
|
||||
g.setColour (juce::Colours::white);
|
||||
g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the %%content_component_class%% is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%content_component_class%%::%%content_component_class%%()
|
||||
{
|
||||
setSize (600, 400);
|
||||
}
|
||||
|
||||
%%content_component_class%%::~%%content_component_class%%()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
g.setFont (juce::Font (16.0f));
|
||||
g.setColour (juce::Colours::white);
|
||||
g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
|
||||
}
|
||||
|
||||
void %%content_component_class%%::resized()
|
||||
{
|
||||
// This is called when the %%content_component_class%% is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%content_component_class%%::%%content_component_class%%()
|
||||
{
|
||||
setSize (600, 400);
|
||||
}
|
||||
|
||||
%%content_component_class%%::~%%content_component_class%%()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
|
||||
|
||||
g.setFont (juce::Font (16.0f));
|
||||
g.setColour (juce::Colours::white);
|
||||
g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
|
||||
}
|
||||
|
||||
void %%content_component_class%%::resized()
|
||||
{
|
||||
// This is called when the %%content_component_class%% is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%();
|
||||
~%%content_component_class%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%();
|
||||
~%%content_component_class%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
//==============================================================================
|
||||
class %%component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
%%component_class%%()
|
||||
{
|
||||
// In your constructor, you should add any child components, and
|
||||
// initialise any special settings that your component needs.
|
||||
|
||||
}
|
||||
|
||||
~%%component_class%%() override
|
||||
{
|
||||
}
|
||||
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// You should replace everything in this method with your own drawing code..
|
||||
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); // clear the background
|
||||
|
||||
g.setColour (juce::Colours::grey);
|
||||
g.drawRect (getLocalBounds(), 1); // draw an outline around the component
|
||||
|
||||
g.setColour (juce::Colours::white);
|
||||
g.setFont (14.0f);
|
||||
g.drawText ("%%component_class%%", getLocalBounds(),
|
||||
juce::Justification::centred, true); // draw some placeholder text
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This method is where you should set the bounds of any child
|
||||
// components that your component contains..
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%component_class%%)
|
||||
};
|
||||
//==============================================================================
|
||||
class %%component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
%%component_class%%()
|
||||
{
|
||||
// In your constructor, you should add any child components, and
|
||||
// initialise any special settings that your component needs.
|
||||
|
||||
}
|
||||
|
||||
~%%component_class%%() override
|
||||
{
|
||||
}
|
||||
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// You should replace everything in this method with your own drawing code..
|
||||
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); // clear the background
|
||||
|
||||
g.setColour (juce::Colours::grey);
|
||||
g.drawRect (getLocalBounds(), 1); // draw an outline around the component
|
||||
|
||||
g.setColour (juce::Colours::white);
|
||||
g.setFont (14.0f);
|
||||
g.drawText ("%%component_class%%", getLocalBounds(),
|
||||
juce::Justification::centred, true); // draw some placeholder text
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This method is where you should set the bounds of any child
|
||||
// components that your component contains..
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic startup code for a JUCE application.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%app_headers%%
|
||||
|
||||
//==============================================================================
|
||||
int main (int argc, char* argv[])
|
||||
{
|
||||
|
||||
// ..your code goes here!
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic startup code for a JUCE application.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%app_headers%%
|
||||
|
||||
//==============================================================================
|
||||
int main (int argc, char* argv[])
|
||||
{
|
||||
|
||||
// ..your code goes here!
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic startup code for a JUCE application.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%app_headers%%
|
||||
|
||||
//==============================================================================
|
||||
class %%app_class_name%% : public juce::JUCEApplication
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%app_class_name%%() {}
|
||||
|
||||
const juce::String getApplicationName() override { return ProjectInfo::projectName; }
|
||||
const juce::String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||
bool moreThanOneInstanceAllowed() override { return true; }
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const juce::String& commandLine) override
|
||||
{
|
||||
// Add your application's initialisation code here..
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
// Add your application's shutdown code here..
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
// This is called when the app is being asked to quit: you can ignore this
|
||||
// request and let the app carry on running, or call quit() to allow the app to close.
|
||||
quit();
|
||||
}
|
||||
|
||||
void anotherInstanceStarted (const juce::String& commandLine) override
|
||||
{
|
||||
// When another instance of the app is launched while this one is running,
|
||||
// this method is invoked, and the commandLine parameter tells you what
|
||||
// the other instance's command-line arguments were.
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// This macro generates the main() routine that launches the app.
|
||||
START_JUCE_APPLICATION (%%app_class_name%%)
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic startup code for a JUCE application.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%app_headers%%
|
||||
|
||||
//==============================================================================
|
||||
class %%app_class_name%% : public juce::JUCEApplication
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%app_class_name%%() {}
|
||||
|
||||
const juce::String getApplicationName() override { return ProjectInfo::projectName; }
|
||||
const juce::String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||
bool moreThanOneInstanceAllowed() override { return true; }
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const juce::String& commandLine) override
|
||||
{
|
||||
// Add your application's initialisation code here..
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
// Add your application's shutdown code here..
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
// This is called when the app is being asked to quit: you can ignore this
|
||||
// request and let the app carry on running, or call quit() to allow the app to close.
|
||||
quit();
|
||||
}
|
||||
|
||||
void anotherInstanceStarted (const juce::String& commandLine) override
|
||||
{
|
||||
// When another instance of the app is launched while this one is running,
|
||||
// this method is invoked, and the commandLine parameter tells you what
|
||||
// the other instance's command-line arguments were.
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// This macro generates the main() routine that launches the app.
|
||||
START_JUCE_APPLICATION (%%app_class_name%%)
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic startup code for a JUCE application.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%app_headers%%
|
||||
|
||||
//==============================================================================
|
||||
class %%app_class_name%% : public juce::JUCEApplication
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%app_class_name%%() {}
|
||||
|
||||
const juce::String getApplicationName() override { return ProjectInfo::projectName; }
|
||||
const juce::String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||
bool moreThanOneInstanceAllowed() override { return true; }
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const juce::String& commandLine) override
|
||||
{
|
||||
// This method is where you should put your application's initialisation code..
|
||||
|
||||
mainWindow.reset (new MainWindow (getApplicationName()));
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
// Add your application's shutdown code here..
|
||||
|
||||
mainWindow = nullptr; // (deletes our window)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
// This is called when the app is being asked to quit: you can ignore this
|
||||
// request and let the app carry on running, or call quit() to allow the app to close.
|
||||
quit();
|
||||
}
|
||||
|
||||
void anotherInstanceStarted (const juce::String& commandLine) override
|
||||
{
|
||||
// When another instance of the app is launched while this one is running,
|
||||
// this method is invoked, and the commandLine parameter tells you what
|
||||
// the other instance's command-line arguments were.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This class implements the desktop window that contains an instance of
|
||||
our %%content_component_class%% class.
|
||||
*/
|
||||
class MainWindow : public juce::DocumentWindow
|
||||
{
|
||||
public:
|
||||
MainWindow (juce::String name)
|
||||
: DocumentWindow (name,
|
||||
juce::Desktop::getInstance().getDefaultLookAndFeel()
|
||||
.findColour (juce::ResizableWindow::backgroundColourId),
|
||||
DocumentWindow::allButtons)
|
||||
{
|
||||
setUsingNativeTitleBar (true);
|
||||
setContentOwned (new %%content_component_class%%(), true);
|
||||
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
setFullScreen (true);
|
||||
#else
|
||||
setResizable (true, true);
|
||||
centreWithSize (getWidth(), getHeight());
|
||||
#endif
|
||||
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
// This is called when the user tries to close this window. Here, we'll just
|
||||
// ask the app to quit when this happens, but you can change this to do
|
||||
// whatever you need.
|
||||
JUCEApplication::getInstance()->systemRequestedQuit();
|
||||
}
|
||||
|
||||
/* Note: Be careful if you override any DocumentWindow methods - the base
|
||||
class uses a lot of them, so by overriding you might break its functionality.
|
||||
It's best to do all your work in your content component instead, but if
|
||||
you really have to override any DocumentWindow methods, make sure your
|
||||
subclass also calls the superclass's method.
|
||||
*/
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<MainWindow> mainWindow;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// This macro generates the main() routine that launches the app.
|
||||
START_JUCE_APPLICATION (%%app_class_name%%)
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file contains the basic startup code for a JUCE application.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%app_headers%%
|
||||
|
||||
//==============================================================================
|
||||
class %%app_class_name%% : public juce::JUCEApplication
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%app_class_name%%() {}
|
||||
|
||||
const juce::String getApplicationName() override { return ProjectInfo::projectName; }
|
||||
const juce::String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||
bool moreThanOneInstanceAllowed() override { return true; }
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const juce::String& commandLine) override
|
||||
{
|
||||
// This method is where you should put your application's initialisation code..
|
||||
|
||||
mainWindow.reset (new MainWindow (getApplicationName()));
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
// Add your application's shutdown code here..
|
||||
|
||||
mainWindow = nullptr; // (deletes our window)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
// This is called when the app is being asked to quit: you can ignore this
|
||||
// request and let the app carry on running, or call quit() to allow the app to close.
|
||||
quit();
|
||||
}
|
||||
|
||||
void anotherInstanceStarted (const juce::String& commandLine) override
|
||||
{
|
||||
// When another instance of the app is launched while this one is running,
|
||||
// this method is invoked, and the commandLine parameter tells you what
|
||||
// the other instance's command-line arguments were.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This class implements the desktop window that contains an instance of
|
||||
our %%content_component_class%% class.
|
||||
*/
|
||||
class MainWindow : public juce::DocumentWindow
|
||||
{
|
||||
public:
|
||||
MainWindow (juce::String name)
|
||||
: DocumentWindow (name,
|
||||
juce::Desktop::getInstance().getDefaultLookAndFeel()
|
||||
.findColour (juce::ResizableWindow::backgroundColourId),
|
||||
DocumentWindow::allButtons)
|
||||
{
|
||||
setUsingNativeTitleBar (true);
|
||||
setContentOwned (new %%content_component_class%%(), true);
|
||||
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
setFullScreen (true);
|
||||
#else
|
||||
setResizable (true, true);
|
||||
centreWithSize (getWidth(), getHeight());
|
||||
#endif
|
||||
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
// This is called when the user tries to close this window. Here, we'll just
|
||||
// ask the app to quit when this happens, but you can change this to do
|
||||
// whatever you need.
|
||||
JUCEApplication::getInstance()->systemRequestedQuit();
|
||||
}
|
||||
|
||||
/* Note: Be careful if you override any DocumentWindow methods - the base
|
||||
class uses a lot of them, so by overriding you might break its functionality.
|
||||
It's best to do all your work in your content component instead, but if
|
||||
you really have to override any DocumentWindow methods, make sure your
|
||||
subclass also calls the superclass's method.
|
||||
*/
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<MainWindow> mainWindow;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// This macro generates the main() routine that launches the app.
|
||||
START_JUCE_APPLICATION (%%app_class_name%%)
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%include_juce%%
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%component_class%%::%%component_class%%()
|
||||
{
|
||||
// In your constructor, you should add any child components, and
|
||||
// initialise any special settings that your component needs.
|
||||
|
||||
}
|
||||
|
||||
%%component_class%%::~%%component_class%%()
|
||||
{
|
||||
}
|
||||
|
||||
void %%component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
/* This demo code just fills the component's background and
|
||||
draws some placeholder text to get you started.
|
||||
|
||||
You should replace everything in this method with your own
|
||||
drawing code..
|
||||
*/
|
||||
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); // clear the background
|
||||
|
||||
g.setColour (juce::Colours::grey);
|
||||
g.drawRect (getLocalBounds(), 1); // draw an outline around the component
|
||||
|
||||
g.setColour (juce::Colours::white);
|
||||
g.setFont (14.0f);
|
||||
g.drawText ("%%component_class%%", getLocalBounds(),
|
||||
juce::Justification::centred, true); // draw some placeholder text
|
||||
}
|
||||
|
||||
void %%component_class%%::resized()
|
||||
{
|
||||
// This method is where you should set the bounds of any child
|
||||
// components that your component contains..
|
||||
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%include_juce%%
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%component_class%%::%%component_class%%()
|
||||
{
|
||||
// In your constructor, you should add any child components, and
|
||||
// initialise any special settings that your component needs.
|
||||
|
||||
}
|
||||
|
||||
%%component_class%%::~%%component_class%%()
|
||||
{
|
||||
}
|
||||
|
||||
void %%component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
/* This demo code just fills the component's background and
|
||||
draws some placeholder text to get you started.
|
||||
|
||||
You should replace everything in this method with your own
|
||||
drawing code..
|
||||
*/
|
||||
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); // clear the background
|
||||
|
||||
g.setColour (juce::Colours::grey);
|
||||
g.drawRect (getLocalBounds(), 1); // draw an outline around the component
|
||||
|
||||
g.setColour (juce::Colours::white);
|
||||
g.setFont (14.0f);
|
||||
g.drawText ("%%component_class%%", getLocalBounds(),
|
||||
juce::Justification::centred, true); // draw some placeholder text
|
||||
}
|
||||
|
||||
void %%component_class%%::resized()
|
||||
{
|
||||
// This method is where you should set the bounds of any child
|
||||
// components that your component contains..
|
||||
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
*/
|
||||
class %%component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
%%component_class%%();
|
||||
~%%component_class%%() override;
|
||||
|
||||
void paint (juce::Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%component_class%%)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
*/
|
||||
class %%component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
%%component_class%%();
|
||||
~%%component_class%%() override;
|
||||
|
||||
void paint (juce::Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%include_corresponding_header%%
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
%%include_corresponding_header%%
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
*/
|
||||
class %%component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
%%component_class%%()
|
||||
{
|
||||
// In your constructor, you should add any child components, and
|
||||
// initialise any special settings that your component needs.
|
||||
|
||||
}
|
||||
|
||||
~%%component_class%%() override
|
||||
{
|
||||
}
|
||||
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
/* This demo code just fills the component's background and
|
||||
draws some placeholder text to get you started.
|
||||
|
||||
You should replace everything in this method with your own
|
||||
drawing code..
|
||||
*/
|
||||
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); // clear the background
|
||||
|
||||
g.setColour (juce::Colours::grey);
|
||||
g.drawRect (getLocalBounds(), 1); // draw an outline around the component
|
||||
|
||||
g.setColour (juce::Colours::white);
|
||||
g.setFont (14.0f);
|
||||
g.drawText ("%%component_class%%", getLocalBounds(),
|
||||
juce::Justification::centred, true); // draw some placeholder text
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This method is where you should set the bounds of any child
|
||||
// components that your component contains..
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%component_class%%)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
%%filename%%
|
||||
Created: %%date%%
|
||||
Author: %%author%%
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
*/
|
||||
class %%component_class%% : public juce::Component
|
||||
{
|
||||
public:
|
||||
%%component_class%%()
|
||||
{
|
||||
// In your constructor, you should add any child components, and
|
||||
// initialise any special settings that your component needs.
|
||||
|
||||
}
|
||||
|
||||
~%%component_class%%() override
|
||||
{
|
||||
}
|
||||
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
/* This demo code just fills the component's background and
|
||||
draws some placeholder text to get you started.
|
||||
|
||||
You should replace everything in this method with your own
|
||||
drawing code..
|
||||
*/
|
||||
|
||||
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId)); // clear the background
|
||||
|
||||
g.setColour (juce::Colours::grey);
|
||||
g.drawRect (getLocalBounds(), 1); // draw an outline around the component
|
||||
|
||||
g.setColour (juce::Colours::white);
|
||||
g.setFont (14.0f);
|
||||
g.drawText ("%%component_class%%", getLocalBounds(),
|
||||
juce::Justification::centred, true); // draw some placeholder text
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This method is where you should set the bounds of any child
|
||||
// components that your component contains..
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,67 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::OpenGLAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
}
|
||||
|
||||
~%%content_component_class%%() override
|
||||
{
|
||||
// This shuts down the GL system and stops the rendering calls.
|
||||
shutdownOpenGL();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void initialise() override
|
||||
{
|
||||
// Initialise GL objects for rendering here.
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
// Free any GL objects created for rendering here.
|
||||
}
|
||||
|
||||
void render() override
|
||||
{
|
||||
// This clears the context with a black background.
|
||||
juce::OpenGLHelpers::clear (Colours::black);
|
||||
|
||||
// Add your rendering code here...
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// You can add your component specific drawing code here!
|
||||
// This will draw over the top of the openGL background.
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::OpenGLAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
}
|
||||
|
||||
~%%content_component_class%%() override
|
||||
{
|
||||
// This shuts down the GL system and stops the rendering calls.
|
||||
shutdownOpenGL();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void initialise() override
|
||||
{
|
||||
// Initialise GL objects for rendering here.
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
// Free any GL objects created for rendering here.
|
||||
}
|
||||
|
||||
void render() override
|
||||
{
|
||||
// This clears the context with a black background.
|
||||
juce::OpenGLHelpers::clear (Colours::black);
|
||||
|
||||
// Add your rendering code here...
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override
|
||||
{
|
||||
// You can add your component specific drawing code here!
|
||||
// This will draw over the top of the openGL background.
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
// This is called when the MainContentComponent is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%content_component_class%%::%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
}
|
||||
|
||||
%%content_component_class%%::~%%content_component_class%%()
|
||||
{
|
||||
// This shuts down the GL system and stops the rendering calls.
|
||||
shutdownOpenGL();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::initialise()
|
||||
{
|
||||
// Initialise GL objects for rendering here.
|
||||
}
|
||||
|
||||
void %%content_component_class%%::shutdown()
|
||||
{
|
||||
// Free any GL objects created for rendering here.
|
||||
}
|
||||
|
||||
void %%content_component_class%%::render()
|
||||
{
|
||||
// This clears the context with a black background.
|
||||
juce::OpenGLHelpers::clear (juce::Colours::black);
|
||||
|
||||
// Add your rendering code here...
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// You can add your component specific drawing code here!
|
||||
// This will draw over the top of the openGL background.
|
||||
}
|
||||
|
||||
void %%content_component_class%%::resized()
|
||||
{
|
||||
// This is called when the %%content_component_class%% is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
%%include_corresponding_header%%
|
||||
|
||||
//==============================================================================
|
||||
%%content_component_class%%::%%content_component_class%%()
|
||||
{
|
||||
// Make sure you set the size of the component after
|
||||
// you add any child components.
|
||||
setSize (800, 600);
|
||||
}
|
||||
|
||||
%%content_component_class%%::~%%content_component_class%%()
|
||||
{
|
||||
// This shuts down the GL system and stops the rendering calls.
|
||||
shutdownOpenGL();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::initialise()
|
||||
{
|
||||
// Initialise GL objects for rendering here.
|
||||
}
|
||||
|
||||
void %%content_component_class%%::shutdown()
|
||||
{
|
||||
// Free any GL objects created for rendering here.
|
||||
}
|
||||
|
||||
void %%content_component_class%%::render()
|
||||
{
|
||||
// This clears the context with a black background.
|
||||
juce::OpenGLHelpers::clear (juce::Colours::black);
|
||||
|
||||
// Add your rendering code here...
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void %%content_component_class%%::paint (juce::Graphics& g)
|
||||
{
|
||||
// You can add your component specific drawing code here!
|
||||
// This will draw over the top of the openGL background.
|
||||
}
|
||||
|
||||
void %%content_component_class%%::resized()
|
||||
{
|
||||
// This is called when the %%content_component_class%% is resized.
|
||||
// If you add any child components, this is where you should
|
||||
// update their positions.
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::OpenGLAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%();
|
||||
~%%content_component_class%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void initialise() override;
|
||||
void shutdown() override;
|
||||
void render() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
#pragma once
|
||||
|
||||
%%include_juce%%
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
This component lives inside our window, and this is where you should put all
|
||||
your controls and content.
|
||||
*/
|
||||
class %%content_component_class%% : public juce::OpenGLAppComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%content_component_class%%();
|
||||
~%%content_component_class%%() override;
|
||||
|
||||
//==============================================================================
|
||||
void initialise() override;
|
||||
void shutdown() override;
|
||||
void render() override;
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics& g) override;
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// Your private member variables go here...
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%content_component_class%%)
|
||||
};
|
||||
|
||||
@@ -1,107 +1,107 @@
|
||||
class %%class_name%% : public juce::AudioProcessor
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%class_name%%()
|
||||
: AudioProcessor (BusesProperties().withInput ("Input", juce::AudioChannelSet::stereo())
|
||||
.withOutput ("Output", juce::AudioChannelSet::stereo()))
|
||||
{
|
||||
}
|
||||
|
||||
~%%class_name%%() override
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (double, int) override
|
||||
{
|
||||
// Use this method as the place to do any pre-playback
|
||||
// initialisation that you need..
|
||||
}
|
||||
|
||||
void releaseResources() override
|
||||
{
|
||||
// When playback stops, you can use this as an opportunity to free up any
|
||||
// spare memory, etc.
|
||||
}
|
||||
|
||||
void processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer&) override
|
||||
{
|
||||
juce::ScopedNoDenormals noDenormals;
|
||||
auto totalNumInputChannels = getTotalNumInputChannels();
|
||||
auto totalNumOutputChannels = getTotalNumOutputChannels();
|
||||
|
||||
// In case we have more outputs than inputs, this code clears any output
|
||||
// channels that didn't contain input data, (because these aren't
|
||||
// guaranteed to be empty - they may contain garbage).
|
||||
// This is here to avoid people getting screaming feedback
|
||||
// when they first compile a plugin, but obviously you don't need to keep
|
||||
// this code if your algorithm always overwrites all the output channels.
|
||||
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
||||
buffer.clear (i, 0, buffer.getNumSamples());
|
||||
|
||||
// This is the place where you'd normally do the guts of your plugin's
|
||||
// audio processing...
|
||||
// Make sure to reset the state if your inner loop is processing
|
||||
// the samples and the outer loop is handling the channels.
|
||||
// Alternatively, you can process the samples with the channels
|
||||
// interleaved by keeping the same state.
|
||||
for (int channel = 0; channel < totalNumInputChannels; ++channel)
|
||||
{
|
||||
auto* channelData = buffer.getWritePointer (channel);
|
||||
|
||||
// ..do something to the data...
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
juce::AudioProcessorEditor* createEditor() override { return nullptr; }
|
||||
bool hasEditor() const override { return false; }
|
||||
|
||||
//==============================================================================
|
||||
const juce::String getName() const override { return "%%name%%"; }
|
||||
bool acceptsMidi() const override { return false; }
|
||||
bool producesMidi() const override { return false; }
|
||||
double getTailLengthSeconds() const override { return 0; }
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override { return 1; }
|
||||
int getCurrentProgram() override { return 0; }
|
||||
void setCurrentProgram (int) override {}
|
||||
const juce::String getProgramName (int) override { return {}; }
|
||||
void changeProgramName (int, const juce::String&) override {}
|
||||
|
||||
//==============================================================================
|
||||
void getStateInformation (juce::MemoryBlock& destData) override
|
||||
{
|
||||
// You should use this method to store your parameters in the memory block.
|
||||
// You could do that either as raw data, or use the XML or ValueTree classes
|
||||
// as intermediaries to make it easy to save and load complex data.
|
||||
}
|
||||
|
||||
void setStateInformation (const void* data, int sizeInBytes) override
|
||||
{
|
||||
// You should use this method to restore your parameters from this memory block,
|
||||
// whose contents will have been created by the getStateInformation() call.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isBusesLayoutSupported (const BusesLayout& layouts) const override
|
||||
{
|
||||
// This is the place where you check if the layout is supported.
|
||||
// In this template code we only support mono or stereo.
|
||||
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
|
||||
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
|
||||
return false;
|
||||
|
||||
// This checks if the input layout matches the output layout
|
||||
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%class_name%%)
|
||||
};
|
||||
class %%class_name%% : public juce::AudioProcessor
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
%%class_name%%()
|
||||
: AudioProcessor (BusesProperties().withInput ("Input", juce::AudioChannelSet::stereo())
|
||||
.withOutput ("Output", juce::AudioChannelSet::stereo()))
|
||||
{
|
||||
}
|
||||
|
||||
~%%class_name%%() override
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay (double, int) override
|
||||
{
|
||||
// Use this method as the place to do any pre-playback
|
||||
// initialisation that you need..
|
||||
}
|
||||
|
||||
void releaseResources() override
|
||||
{
|
||||
// When playback stops, you can use this as an opportunity to free up any
|
||||
// spare memory, etc.
|
||||
}
|
||||
|
||||
void processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer&) override
|
||||
{
|
||||
juce::ScopedNoDenormals noDenormals;
|
||||
auto totalNumInputChannels = getTotalNumInputChannels();
|
||||
auto totalNumOutputChannels = getTotalNumOutputChannels();
|
||||
|
||||
// In case we have more outputs than inputs, this code clears any output
|
||||
// channels that didn't contain input data, (because these aren't
|
||||
// guaranteed to be empty - they may contain garbage).
|
||||
// This is here to avoid people getting screaming feedback
|
||||
// when they first compile a plugin, but obviously you don't need to keep
|
||||
// this code if your algorithm always overwrites all the output channels.
|
||||
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
||||
buffer.clear (i, 0, buffer.getNumSamples());
|
||||
|
||||
// This is the place where you'd normally do the guts of your plugin's
|
||||
// audio processing...
|
||||
// Make sure to reset the state if your inner loop is processing
|
||||
// the samples and the outer loop is handling the channels.
|
||||
// Alternatively, you can process the samples with the channels
|
||||
// interleaved by keeping the same state.
|
||||
for (int channel = 0; channel < totalNumInputChannels; ++channel)
|
||||
{
|
||||
auto* channelData = buffer.getWritePointer (channel);
|
||||
|
||||
// ..do something to the data...
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
juce::AudioProcessorEditor* createEditor() override { return nullptr; }
|
||||
bool hasEditor() const override { return false; }
|
||||
|
||||
//==============================================================================
|
||||
const juce::String getName() const override { return "%%name%%"; }
|
||||
bool acceptsMidi() const override { return false; }
|
||||
bool producesMidi() const override { return false; }
|
||||
double getTailLengthSeconds() const override { return 0; }
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override { return 1; }
|
||||
int getCurrentProgram() override { return 0; }
|
||||
void setCurrentProgram (int) override {}
|
||||
const juce::String getProgramName (int) override { return {}; }
|
||||
void changeProgramName (int, const juce::String&) override {}
|
||||
|
||||
//==============================================================================
|
||||
void getStateInformation (juce::MemoryBlock& destData) override
|
||||
{
|
||||
// You should use this method to store your parameters in the memory block.
|
||||
// You could do that either as raw data, or use the XML or ValueTree classes
|
||||
// as intermediaries to make it easy to save and load complex data.
|
||||
}
|
||||
|
||||
void setStateInformation (const void* data, int sizeInBytes) override
|
||||
{
|
||||
// You should use this method to restore your parameters from this memory block,
|
||||
// whose contents will have been created by the getStateInformation() call.
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isBusesLayoutSupported (const BusesLayout& layouts) const override
|
||||
{
|
||||
// This is the place where you check if the layout is supported.
|
||||
// In this template code we only support mono or stereo.
|
||||
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
|
||||
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
|
||||
return false;
|
||||
|
||||
// This checks if the input layout matches the output layout
|
||||
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (%%class_name%%)
|
||||
};
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this PIP. A PIP is a short snippet
|
||||
of code that can be read by the Projucer and used to generate a JUCE project.
|
||||
|
||||
BEGIN_JUCE_PIP_METADATA
|
||||
|
||||
%%pip_metadata%%
|
||||
|
||||
END_JUCE_PIP_METADATA
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
%%pip_code%%
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this PIP. A PIP is a short snippet
|
||||
of code that can be read by the Projucer and used to generate a JUCE project.
|
||||
|
||||
BEGIN_JUCE_PIP_METADATA
|
||||
|
||||
%%pip_metadata%%
|
||||
|
||||
END_JUCE_PIP_METADATA
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
%%pip_code%%
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../Application/jucer_Headers.h"
|
||||
#include "jucer_DocumentEditorComponent.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
#include "../Project/UI/jucer_ProjectContentComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
DocumentEditorComponent::DocumentEditorComponent (OpenDocumentManager::Document* doc)
|
||||
: document (doc)
|
||||
{
|
||||
ProjucerApplication::getApp().openDocumentManager.addListener (this);
|
||||
}
|
||||
|
||||
DocumentEditorComponent::~DocumentEditorComponent()
|
||||
{
|
||||
ProjucerApplication::getApp().openDocumentManager.removeListener (this);
|
||||
}
|
||||
|
||||
bool DocumentEditorComponent::documentAboutToClose (OpenDocumentManager::Document* closingDoc)
|
||||
{
|
||||
if (document == closingDoc)
|
||||
{
|
||||
jassert (document != nullptr);
|
||||
|
||||
if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->hideDocument (document);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DocumentEditorComponent::setEditedState (bool hasBeenEdited)
|
||||
{
|
||||
if (hasBeenEdited != lastEditedState)
|
||||
{
|
||||
if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->refreshProjectTreeFileStatuses();
|
||||
|
||||
lastEditedState = hasBeenEdited;
|
||||
}
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../Application/jucer_Headers.h"
|
||||
#include "jucer_DocumentEditorComponent.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
#include "../Project/UI/jucer_ProjectContentComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
DocumentEditorComponent::DocumentEditorComponent (OpenDocumentManager::Document* doc)
|
||||
: document (doc)
|
||||
{
|
||||
ProjucerApplication::getApp().openDocumentManager.addListener (this);
|
||||
}
|
||||
|
||||
DocumentEditorComponent::~DocumentEditorComponent()
|
||||
{
|
||||
ProjucerApplication::getApp().openDocumentManager.removeListener (this);
|
||||
}
|
||||
|
||||
bool DocumentEditorComponent::documentAboutToClose (OpenDocumentManager::Document* closingDoc)
|
||||
{
|
||||
if (document == closingDoc)
|
||||
{
|
||||
jassert (document != nullptr);
|
||||
|
||||
if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->hideDocument (document);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DocumentEditorComponent::setEditedState (bool hasBeenEdited)
|
||||
{
|
||||
if (hasBeenEdited != lastEditedState)
|
||||
{
|
||||
if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->refreshProjectTreeFileStatuses();
|
||||
|
||||
lastEditedState = hasBeenEdited;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_OpenDocumentManager.h"
|
||||
|
||||
//==============================================================================
|
||||
class DocumentEditorComponent : public Component,
|
||||
private OpenDocumentManager::DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DocumentEditorComponent (OpenDocumentManager::Document* document);
|
||||
~DocumentEditorComponent() override;
|
||||
|
||||
OpenDocumentManager::Document* getDocument() const { return document; }
|
||||
|
||||
protected:
|
||||
OpenDocumentManager::Document* document;
|
||||
bool lastEditedState = false;
|
||||
|
||||
void setEditedState (bool hasBeenEdited);
|
||||
|
||||
private:
|
||||
bool documentAboutToClose (OpenDocumentManager::Document*) override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DocumentEditorComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_OpenDocumentManager.h"
|
||||
|
||||
//==============================================================================
|
||||
class DocumentEditorComponent : public Component,
|
||||
private OpenDocumentManager::DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DocumentEditorComponent (OpenDocumentManager::Document* document);
|
||||
~DocumentEditorComponent() override;
|
||||
|
||||
OpenDocumentManager::Document* getDocument() const { return document; }
|
||||
|
||||
protected:
|
||||
OpenDocumentManager::Document* document;
|
||||
bool lastEditedState = false;
|
||||
|
||||
void setEditedState (bool hasBeenEdited);
|
||||
|
||||
private:
|
||||
bool documentAboutToClose (OpenDocumentManager::Document*) override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DocumentEditorComponent)
|
||||
};
|
||||
|
||||
@@ -1,117 +1,117 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ItemPreviewComponent : public Component
|
||||
{
|
||||
public:
|
||||
ItemPreviewComponent (const File& f) : file (f)
|
||||
{
|
||||
setOpaque (true);
|
||||
tryToLoadImage();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
if (drawable != nullptr)
|
||||
{
|
||||
auto contentBounds = drawable->getDrawableBounds();
|
||||
|
||||
if (auto* dc = dynamic_cast<DrawableComposite*> (drawable.get()))
|
||||
{
|
||||
auto r = dc->getContentArea();
|
||||
|
||||
if (! r.isEmpty())
|
||||
contentBounds = r;
|
||||
}
|
||||
|
||||
auto area = RectanglePlacement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize)
|
||||
.appliedTo (contentBounds, Rectangle<float> (4.0f, 22.0f, (float) getWidth() - 8.0f, (float) getHeight() - 26.0f));
|
||||
|
||||
Path p;
|
||||
p.addRectangle (area);
|
||||
DropShadow (Colours::black.withAlpha (0.5f), 6, Point<int> (0, 1)).drawForPath (g, p);
|
||||
|
||||
g.fillCheckerBoard (area, 24.0f, 24.0f, Colour (0xffffffff), Colour (0xffeeeeee));
|
||||
|
||||
drawable->draw (g, 1.0f, RectanglePlacement (RectanglePlacement::stretchToFit)
|
||||
.getTransformToFit (contentBounds, area.toFloat()));
|
||||
}
|
||||
|
||||
g.setFont (Font (14.0f, Font::bold));
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
g.drawMultiLineText (facts.joinIntoString ("\n"), 10, 15, getWidth() - 16);
|
||||
}
|
||||
|
||||
private:
|
||||
StringArray facts;
|
||||
File file;
|
||||
std::unique_ptr<Drawable> drawable;
|
||||
|
||||
void tryToLoadImage()
|
||||
{
|
||||
facts.clear();
|
||||
facts.add (file.getFullPathName());
|
||||
drawable.reset();
|
||||
|
||||
if (auto input = std::unique_ptr<FileInputStream> (file.createInputStream()))
|
||||
{
|
||||
auto totalSize = input->getTotalLength();
|
||||
String formatName;
|
||||
|
||||
if (auto* format = ImageFileFormat::findImageFormatForStream (*input))
|
||||
formatName = " " + format->getFormatName();
|
||||
|
||||
input.reset();
|
||||
|
||||
auto image = ImageCache::getFromFile (file);
|
||||
|
||||
if (image.isValid())
|
||||
{
|
||||
auto* d = new DrawableImage();
|
||||
d->setImage (image);
|
||||
drawable.reset (d);
|
||||
|
||||
facts.add (String (image.getWidth()) + " x " + String (image.getHeight()) + formatName);
|
||||
}
|
||||
|
||||
if (totalSize > 0)
|
||||
facts.add (File::descriptionOfSizeInBytes (totalSize));
|
||||
}
|
||||
|
||||
if (drawable == nullptr)
|
||||
if (auto svg = parseXML (file))
|
||||
drawable = Drawable::createFromSVG (*svg);
|
||||
|
||||
facts.removeEmptyStrings (true);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemPreviewComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ItemPreviewComponent : public Component
|
||||
{
|
||||
public:
|
||||
ItemPreviewComponent (const File& f) : file (f)
|
||||
{
|
||||
setOpaque (true);
|
||||
tryToLoadImage();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
if (drawable != nullptr)
|
||||
{
|
||||
auto contentBounds = drawable->getDrawableBounds();
|
||||
|
||||
if (auto* dc = dynamic_cast<DrawableComposite*> (drawable.get()))
|
||||
{
|
||||
auto r = dc->getContentArea();
|
||||
|
||||
if (! r.isEmpty())
|
||||
contentBounds = r;
|
||||
}
|
||||
|
||||
auto area = RectanglePlacement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize)
|
||||
.appliedTo (contentBounds, Rectangle<float> (4.0f, 22.0f, (float) getWidth() - 8.0f, (float) getHeight() - 26.0f));
|
||||
|
||||
Path p;
|
||||
p.addRectangle (area);
|
||||
DropShadow (Colours::black.withAlpha (0.5f), 6, Point<int> (0, 1)).drawForPath (g, p);
|
||||
|
||||
g.fillCheckerBoard (area, 24.0f, 24.0f, Colour (0xffffffff), Colour (0xffeeeeee));
|
||||
|
||||
drawable->draw (g, 1.0f, RectanglePlacement (RectanglePlacement::stretchToFit)
|
||||
.getTransformToFit (contentBounds, area.toFloat()));
|
||||
}
|
||||
|
||||
g.setFont (Font (14.0f, Font::bold));
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
g.drawMultiLineText (facts.joinIntoString ("\n"), 10, 15, getWidth() - 16);
|
||||
}
|
||||
|
||||
private:
|
||||
StringArray facts;
|
||||
File file;
|
||||
std::unique_ptr<Drawable> drawable;
|
||||
|
||||
void tryToLoadImage()
|
||||
{
|
||||
facts.clear();
|
||||
facts.add (file.getFullPathName());
|
||||
drawable.reset();
|
||||
|
||||
if (auto input = std::unique_ptr<FileInputStream> (file.createInputStream()))
|
||||
{
|
||||
auto totalSize = input->getTotalLength();
|
||||
String formatName;
|
||||
|
||||
if (auto* format = ImageFileFormat::findImageFormatForStream (*input))
|
||||
formatName = " " + format->getFormatName();
|
||||
|
||||
input.reset();
|
||||
|
||||
auto image = ImageCache::getFromFile (file);
|
||||
|
||||
if (image.isValid())
|
||||
{
|
||||
auto* d = new DrawableImage();
|
||||
d->setImage (image);
|
||||
drawable.reset (d);
|
||||
|
||||
facts.add (String (image.getWidth()) + " x " + String (image.getHeight()) + formatName);
|
||||
}
|
||||
|
||||
if (totalSize > 0)
|
||||
facts.add (File::descriptionOfSizeInBytes (totalSize));
|
||||
}
|
||||
|
||||
if (drawable == nullptr)
|
||||
if (auto svg = parseXML (file))
|
||||
drawable = Drawable::createFromSVG (*svg);
|
||||
|
||||
facts.removeEmptyStrings (true);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemPreviewComponent)
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,167 +1,167 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Project/jucer_Project.h"
|
||||
|
||||
//==============================================================================
|
||||
class OpenDocumentManager
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
OpenDocumentManager();
|
||||
~OpenDocumentManager();
|
||||
|
||||
//==============================================================================
|
||||
class Document
|
||||
{
|
||||
public:
|
||||
Document() {}
|
||||
virtual ~Document() {}
|
||||
|
||||
virtual bool loadedOk() const = 0;
|
||||
virtual bool isForFile (const File& file) const = 0;
|
||||
virtual bool isForNode (const ValueTree& node) const = 0;
|
||||
virtual bool refersToProject (Project& project) const = 0;
|
||||
virtual Project* getProject() const = 0;
|
||||
virtual String getName() const = 0;
|
||||
virtual String getType() const = 0;
|
||||
virtual File getFile() const = 0;
|
||||
virtual bool needsSaving() const = 0;
|
||||
virtual bool saveSyncWithoutAsking() = 0;
|
||||
virtual void saveAsync (std::function<void (bool)>) = 0;
|
||||
virtual void saveAsAsync (std::function<void (bool)>) = 0;
|
||||
virtual bool hasFileBeenModifiedExternally() = 0;
|
||||
virtual void reloadFromFile() = 0;
|
||||
virtual std::unique_ptr<Component> createEditor() = 0;
|
||||
virtual std::unique_ptr<Component> createViewer() = 0;
|
||||
virtual void fileHasBeenRenamed (const File& newFile) = 0;
|
||||
virtual String getState() const = 0;
|
||||
virtual void restoreState (const String& state) = 0;
|
||||
virtual File getCounterpartFile() const { return {}; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
int getNumOpenDocuments() const;
|
||||
Document* getOpenDocument (int index) const;
|
||||
void clear();
|
||||
|
||||
enum class SaveIfNeeded { no, yes };
|
||||
|
||||
bool canOpenFile (const File& file);
|
||||
Document* openFile (Project* project, const File& file);
|
||||
|
||||
void closeDocumentAsync (Document* document, SaveIfNeeded saveIfNeeded, std::function<void (bool)> callback);
|
||||
bool closeDocumentWithoutSaving (Document* document);
|
||||
|
||||
void closeAllAsync (SaveIfNeeded askUserToSave, std::function<void (bool)> callback);
|
||||
void closeAllDocumentsUsingProjectAsync (Project& project, SaveIfNeeded askUserToSave, std::function<void (bool)> callback);
|
||||
void closeAllDocumentsUsingProjectWithoutSaving (Project& project);
|
||||
|
||||
void closeFileWithoutSaving (const File& f);
|
||||
bool anyFilesNeedSaving() const;
|
||||
|
||||
void saveAllSyncWithoutAsking();
|
||||
void saveIfNeededAndUserAgrees (Document* doc, std::function<void (FileBasedDocument::SaveResult)>);
|
||||
|
||||
void reloadModifiedFiles();
|
||||
void fileHasBeenRenamed (const File& oldFile, const File& newFile);
|
||||
|
||||
//==============================================================================
|
||||
class DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
DocumentCloseListener() {}
|
||||
virtual ~DocumentCloseListener() {}
|
||||
|
||||
// return false to force it to stop.
|
||||
virtual bool documentAboutToClose (Document* document) = 0;
|
||||
};
|
||||
|
||||
void addListener (DocumentCloseListener*);
|
||||
void removeListener (DocumentCloseListener*);
|
||||
|
||||
//==============================================================================
|
||||
class DocumentType
|
||||
{
|
||||
public:
|
||||
DocumentType() {}
|
||||
virtual ~DocumentType() {}
|
||||
|
||||
virtual bool canOpenFile (const File& file) = 0;
|
||||
virtual Document* openFile (Project* project, const File& file) = 0;
|
||||
};
|
||||
|
||||
void registerType (DocumentType* type, int index = -1);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void closeLastDocumentUsingProjectRecursive (WeakReference<OpenDocumentManager>,
|
||||
Project*,
|
||||
SaveIfNeeded,
|
||||
std::function<void (bool)>);
|
||||
|
||||
//==============================================================================
|
||||
OwnedArray<DocumentType> types;
|
||||
OwnedArray<Document> documents;
|
||||
Array<DocumentCloseListener*> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenDocumentManager)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (OpenDocumentManager)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class RecentDocumentList : private OpenDocumentManager::DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
RecentDocumentList();
|
||||
~RecentDocumentList();
|
||||
|
||||
void clear();
|
||||
|
||||
void newDocumentOpened (OpenDocumentManager::Document* document);
|
||||
|
||||
OpenDocumentManager::Document* getCurrentDocument() const { return previousDocs.getLast(); }
|
||||
|
||||
bool canGoToPrevious() const;
|
||||
bool canGoToNext() const;
|
||||
|
||||
bool contains (const File&) const;
|
||||
|
||||
OpenDocumentManager::Document* getPrevious();
|
||||
OpenDocumentManager::Document* getNext();
|
||||
|
||||
OpenDocumentManager::Document* getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const;
|
||||
|
||||
void restoreFromXML (Project& project, const XmlElement& xml);
|
||||
std::unique_ptr<XmlElement> createXML() const;
|
||||
|
||||
private:
|
||||
bool documentAboutToClose (OpenDocumentManager::Document*);
|
||||
|
||||
Array<OpenDocumentManager::Document*> previousDocs, nextDocs;
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Project/jucer_Project.h"
|
||||
|
||||
//==============================================================================
|
||||
class OpenDocumentManager
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
OpenDocumentManager();
|
||||
~OpenDocumentManager();
|
||||
|
||||
//==============================================================================
|
||||
class Document
|
||||
{
|
||||
public:
|
||||
Document() {}
|
||||
virtual ~Document() {}
|
||||
|
||||
virtual bool loadedOk() const = 0;
|
||||
virtual bool isForFile (const File& file) const = 0;
|
||||
virtual bool isForNode (const ValueTree& node) const = 0;
|
||||
virtual bool refersToProject (Project& project) const = 0;
|
||||
virtual Project* getProject() const = 0;
|
||||
virtual String getName() const = 0;
|
||||
virtual String getType() const = 0;
|
||||
virtual File getFile() const = 0;
|
||||
virtual bool needsSaving() const = 0;
|
||||
virtual bool saveSyncWithoutAsking() = 0;
|
||||
virtual void saveAsync (std::function<void (bool)>) = 0;
|
||||
virtual void saveAsAsync (std::function<void (bool)>) = 0;
|
||||
virtual bool hasFileBeenModifiedExternally() = 0;
|
||||
virtual void reloadFromFile() = 0;
|
||||
virtual std::unique_ptr<Component> createEditor() = 0;
|
||||
virtual std::unique_ptr<Component> createViewer() = 0;
|
||||
virtual void fileHasBeenRenamed (const File& newFile) = 0;
|
||||
virtual String getState() const = 0;
|
||||
virtual void restoreState (const String& state) = 0;
|
||||
virtual File getCounterpartFile() const { return {}; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
int getNumOpenDocuments() const;
|
||||
Document* getOpenDocument (int index) const;
|
||||
void clear();
|
||||
|
||||
enum class SaveIfNeeded { no, yes };
|
||||
|
||||
bool canOpenFile (const File& file);
|
||||
Document* openFile (Project* project, const File& file);
|
||||
|
||||
void closeDocumentAsync (Document* document, SaveIfNeeded saveIfNeeded, std::function<void (bool)> callback);
|
||||
bool closeDocumentWithoutSaving (Document* document);
|
||||
|
||||
void closeAllAsync (SaveIfNeeded askUserToSave, std::function<void (bool)> callback);
|
||||
void closeAllDocumentsUsingProjectAsync (Project& project, SaveIfNeeded askUserToSave, std::function<void (bool)> callback);
|
||||
void closeAllDocumentsUsingProjectWithoutSaving (Project& project);
|
||||
|
||||
void closeFileWithoutSaving (const File& f);
|
||||
bool anyFilesNeedSaving() const;
|
||||
|
||||
void saveAllSyncWithoutAsking();
|
||||
void saveIfNeededAndUserAgrees (Document* doc, std::function<void (FileBasedDocument::SaveResult)>);
|
||||
|
||||
void reloadModifiedFiles();
|
||||
void fileHasBeenRenamed (const File& oldFile, const File& newFile);
|
||||
|
||||
//==============================================================================
|
||||
class DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
DocumentCloseListener() {}
|
||||
virtual ~DocumentCloseListener() {}
|
||||
|
||||
// return false to force it to stop.
|
||||
virtual bool documentAboutToClose (Document* document) = 0;
|
||||
};
|
||||
|
||||
void addListener (DocumentCloseListener*);
|
||||
void removeListener (DocumentCloseListener*);
|
||||
|
||||
//==============================================================================
|
||||
class DocumentType
|
||||
{
|
||||
public:
|
||||
DocumentType() {}
|
||||
virtual ~DocumentType() {}
|
||||
|
||||
virtual bool canOpenFile (const File& file) = 0;
|
||||
virtual Document* openFile (Project* project, const File& file) = 0;
|
||||
};
|
||||
|
||||
void registerType (DocumentType* type, int index = -1);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void closeLastDocumentUsingProjectRecursive (WeakReference<OpenDocumentManager>,
|
||||
Project*,
|
||||
SaveIfNeeded,
|
||||
std::function<void (bool)>);
|
||||
|
||||
//==============================================================================
|
||||
OwnedArray<DocumentType> types;
|
||||
OwnedArray<Document> documents;
|
||||
Array<DocumentCloseListener*> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenDocumentManager)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (OpenDocumentManager)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class RecentDocumentList : private OpenDocumentManager::DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
RecentDocumentList();
|
||||
~RecentDocumentList();
|
||||
|
||||
void clear();
|
||||
|
||||
void newDocumentOpened (OpenDocumentManager::Document* document);
|
||||
|
||||
OpenDocumentManager::Document* getCurrentDocument() const { return previousDocs.getLast(); }
|
||||
|
||||
bool canGoToPrevious() const;
|
||||
bool canGoToNext() const;
|
||||
|
||||
bool contains (const File&) const;
|
||||
|
||||
OpenDocumentManager::Document* getPrevious();
|
||||
OpenDocumentManager::Document* getNext();
|
||||
|
||||
OpenDocumentManager::Document* getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const;
|
||||
|
||||
void restoreFromXML (Project& project, const XmlElement& xml);
|
||||
std::unique_ptr<XmlElement> createXML() const;
|
||||
|
||||
private:
|
||||
bool documentAboutToClose (OpenDocumentManager::Document*);
|
||||
|
||||
Array<OpenDocumentManager::Document*> previousDocs, nextDocs;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,245 +1,245 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_DocumentEditorComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class SourceCodeDocument : public OpenDocumentManager::Document
|
||||
{
|
||||
public:
|
||||
SourceCodeDocument (Project*, const File&);
|
||||
|
||||
bool loadedOk() const override { return true; }
|
||||
bool isForFile (const File& file) const override { return getFile() == file; }
|
||||
bool isForNode (const ValueTree&) const override { return false; }
|
||||
bool refersToProject (Project& p) const override { return project == &p; }
|
||||
Project* getProject() const override { return project; }
|
||||
String getName() const override { return getFile().getFileName(); }
|
||||
String getType() const override { return getFile().getFileExtension() + " file"; }
|
||||
File getFile() const override { return modDetector.getFile(); }
|
||||
bool needsSaving() const override { return codeDoc != nullptr && codeDoc->hasChangedSinceSavePoint(); }
|
||||
bool hasFileBeenModifiedExternally() override { return modDetector.hasBeenModified(); }
|
||||
void fileHasBeenRenamed (const File& newFile) override { modDetector.fileHasBeenRenamed (newFile); }
|
||||
String getState() const override { return lastState != nullptr ? lastState->toString() : String(); }
|
||||
void restoreState (const String& state) override { lastState.reset (new CodeEditorComponent::State (state)); }
|
||||
|
||||
File getCounterpartFile() const override
|
||||
{
|
||||
auto file = getFile();
|
||||
|
||||
if (file.hasFileExtension (sourceFileExtensions))
|
||||
{
|
||||
static const char* extensions[] = { "h", "hpp", "hxx", "hh", nullptr };
|
||||
return findCounterpart (file, extensions);
|
||||
}
|
||||
|
||||
if (file.hasFileExtension (headerFileExtensions))
|
||||
{
|
||||
static const char* extensions[] = { "cpp", "mm", "cc", "cxx", "c", "m", nullptr };
|
||||
return findCounterpart (file, extensions);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static File findCounterpart (const File& file, const char** extensions)
|
||||
{
|
||||
while (*extensions != nullptr)
|
||||
{
|
||||
auto f = file.withFileExtension (*extensions++);
|
||||
|
||||
if (f.existsAsFile())
|
||||
return f;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void reloadFromFile() override;
|
||||
bool saveSyncWithoutAsking() override;
|
||||
void saveAsync (std::function<void (bool)>) override;
|
||||
void saveAsAsync (std::function<void (bool)>) override;
|
||||
|
||||
std::unique_ptr<Component> createEditor() override;
|
||||
std::unique_ptr<Component> createViewer() override { return createEditor(); }
|
||||
|
||||
void updateLastState (CodeEditorComponent&);
|
||||
void applyLastState (CodeEditorComponent&) const;
|
||||
|
||||
CodeDocument& getCodeDocument();
|
||||
|
||||
//==============================================================================
|
||||
struct Type : public OpenDocumentManager::DocumentType
|
||||
{
|
||||
bool canOpenFile (const File& file) override
|
||||
{
|
||||
if (file.hasFileExtension (sourceOrHeaderFileExtensions)
|
||||
|| file.hasFileExtension ("txt;inc;tcc;xml;plist;rtf;html;htm;php;py;rb;cs"))
|
||||
return true;
|
||||
|
||||
MemoryBlock mb;
|
||||
if (file.loadFileAsData (mb)
|
||||
&& seemsToBeText (static_cast<const char*> (mb.getData()), (int) mb.getSize())
|
||||
&& ! file.hasFileExtension ("svg"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool seemsToBeText (const char* const chars, const int num) noexcept
|
||||
{
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
const char c = chars[i];
|
||||
if ((c < 32 && c != '\t' && c != '\r' && c != '\n') || chars[i] > 126)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Document* openFile (Project* p, const File& file) override { return new SourceCodeDocument (p, file); }
|
||||
};
|
||||
|
||||
protected:
|
||||
FileModificationDetector modDetector;
|
||||
std::unique_ptr<CodeDocument> codeDoc;
|
||||
Project* project;
|
||||
|
||||
std::unique_ptr<CodeEditorComponent::State> lastState;
|
||||
|
||||
void reloadInternal();
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
};
|
||||
|
||||
class GenericCodeEditorComponent;
|
||||
|
||||
//==============================================================================
|
||||
class SourceCodeEditor : public DocumentEditorComponent,
|
||||
private ValueTree::Listener,
|
||||
private CodeDocument::Listener
|
||||
{
|
||||
public:
|
||||
SourceCodeEditor (OpenDocumentManager::Document*, CodeDocument&);
|
||||
SourceCodeEditor (OpenDocumentManager::Document*, GenericCodeEditorComponent*);
|
||||
~SourceCodeEditor() override;
|
||||
|
||||
void scrollToKeepRangeOnScreen (Range<int> range);
|
||||
void highlight (Range<int> range, bool cursorAtStart);
|
||||
|
||||
std::unique_ptr<GenericCodeEditorComponent> editor;
|
||||
|
||||
private:
|
||||
void resized() override;
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
|
||||
void valueTreeParentChanged (ValueTree&) override;
|
||||
void valueTreeRedirected (ValueTree&) override;
|
||||
|
||||
void codeDocumentTextInserted (const String&, int) override;
|
||||
void codeDocumentTextDeleted (int, int) override;
|
||||
|
||||
void setEditor (GenericCodeEditorComponent*);
|
||||
void updateColourScheme();
|
||||
void checkSaveState();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SourceCodeEditor)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class GenericCodeEditorComponent : public CodeEditorComponent
|
||||
{
|
||||
public:
|
||||
GenericCodeEditorComponent (const File&, CodeDocument&, CodeTokeniser*);
|
||||
~GenericCodeEditorComponent() override;
|
||||
|
||||
void addPopupMenuItems (PopupMenu&, const MouseEvent*) override;
|
||||
void performPopupMenuAction (int menuItemID) override;
|
||||
|
||||
void getAllCommands (Array<CommandID>&) override;
|
||||
void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
|
||||
bool perform (const InvocationInfo&) override;
|
||||
|
||||
void showFindPanel();
|
||||
void hideFindPanel();
|
||||
void findSelection();
|
||||
void findNext (bool forwards, bool skipCurrentSelection);
|
||||
void handleEscapeKey() override;
|
||||
void editorViewportPositionChanged() override;
|
||||
|
||||
void resized() override;
|
||||
|
||||
static String getSearchString() { return getAppSettings().getGlobalProperties().getValue ("searchString"); }
|
||||
static void setSearchString (const String& s) { getAppSettings().getGlobalProperties().setValue ("searchString", s); }
|
||||
static bool isCaseSensitiveSearch() { return getAppSettings().getGlobalProperties().getBoolValue ("searchCaseSensitive"); }
|
||||
static void setCaseSensitiveSearch (bool b) { getAppSettings().getGlobalProperties().setValue ("searchCaseSensitive", b); }
|
||||
|
||||
struct Listener
|
||||
{
|
||||
virtual ~Listener() {}
|
||||
virtual void codeEditorViewportMoved (CodeEditorComponent&) = 0;
|
||||
};
|
||||
|
||||
void addListener (Listener* listener);
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
private:
|
||||
File file;
|
||||
class FindPanel;
|
||||
std::unique_ptr<FindPanel> findPanel;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericCodeEditorComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class CppCodeEditorComponent : public GenericCodeEditorComponent
|
||||
{
|
||||
public:
|
||||
CppCodeEditorComponent (const File&, CodeDocument&);
|
||||
~CppCodeEditorComponent() override;
|
||||
|
||||
void addPopupMenuItems (PopupMenu&, const MouseEvent*) override;
|
||||
void performPopupMenuAction (int menuItemID) override;
|
||||
|
||||
void handleReturnKey() override;
|
||||
void insertTextAtCaret (const String& newText) override;
|
||||
|
||||
private:
|
||||
void insertComponentClass();
|
||||
|
||||
std::unique_ptr<AlertWindow> asyncAlertWindow;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CppCodeEditorComponent)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_DocumentEditorComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class SourceCodeDocument : public OpenDocumentManager::Document
|
||||
{
|
||||
public:
|
||||
SourceCodeDocument (Project*, const File&);
|
||||
|
||||
bool loadedOk() const override { return true; }
|
||||
bool isForFile (const File& file) const override { return getFile() == file; }
|
||||
bool isForNode (const ValueTree&) const override { return false; }
|
||||
bool refersToProject (Project& p) const override { return project == &p; }
|
||||
Project* getProject() const override { return project; }
|
||||
String getName() const override { return getFile().getFileName(); }
|
||||
String getType() const override { return getFile().getFileExtension() + " file"; }
|
||||
File getFile() const override { return modDetector.getFile(); }
|
||||
bool needsSaving() const override { return codeDoc != nullptr && codeDoc->hasChangedSinceSavePoint(); }
|
||||
bool hasFileBeenModifiedExternally() override { return modDetector.hasBeenModified(); }
|
||||
void fileHasBeenRenamed (const File& newFile) override { modDetector.fileHasBeenRenamed (newFile); }
|
||||
String getState() const override { return lastState != nullptr ? lastState->toString() : String(); }
|
||||
void restoreState (const String& state) override { lastState.reset (new CodeEditorComponent::State (state)); }
|
||||
|
||||
File getCounterpartFile() const override
|
||||
{
|
||||
auto file = getFile();
|
||||
|
||||
if (file.hasFileExtension (sourceFileExtensions))
|
||||
{
|
||||
static const char* extensions[] = { "h", "hpp", "hxx", "hh", nullptr };
|
||||
return findCounterpart (file, extensions);
|
||||
}
|
||||
|
||||
if (file.hasFileExtension (headerFileExtensions))
|
||||
{
|
||||
static const char* extensions[] = { "cpp", "mm", "cc", "cxx", "c", "m", nullptr };
|
||||
return findCounterpart (file, extensions);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static File findCounterpart (const File& file, const char** extensions)
|
||||
{
|
||||
while (*extensions != nullptr)
|
||||
{
|
||||
auto f = file.withFileExtension (*extensions++);
|
||||
|
||||
if (f.existsAsFile())
|
||||
return f;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void reloadFromFile() override;
|
||||
bool saveSyncWithoutAsking() override;
|
||||
void saveAsync (std::function<void (bool)>) override;
|
||||
void saveAsAsync (std::function<void (bool)>) override;
|
||||
|
||||
std::unique_ptr<Component> createEditor() override;
|
||||
std::unique_ptr<Component> createViewer() override { return createEditor(); }
|
||||
|
||||
void updateLastState (CodeEditorComponent&);
|
||||
void applyLastState (CodeEditorComponent&) const;
|
||||
|
||||
CodeDocument& getCodeDocument();
|
||||
|
||||
//==============================================================================
|
||||
struct Type : public OpenDocumentManager::DocumentType
|
||||
{
|
||||
bool canOpenFile (const File& file) override
|
||||
{
|
||||
if (file.hasFileExtension (sourceOrHeaderFileExtensions)
|
||||
|| file.hasFileExtension ("txt;inc;tcc;xml;plist;rtf;html;htm;php;py;rb;cs"))
|
||||
return true;
|
||||
|
||||
MemoryBlock mb;
|
||||
if (file.loadFileAsData (mb)
|
||||
&& seemsToBeText (static_cast<const char*> (mb.getData()), (int) mb.getSize())
|
||||
&& ! file.hasFileExtension ("svg"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool seemsToBeText (const char* const chars, const int num) noexcept
|
||||
{
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
const char c = chars[i];
|
||||
if ((c < 32 && c != '\t' && c != '\r' && c != '\n') || chars[i] > 126)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Document* openFile (Project* p, const File& file) override { return new SourceCodeDocument (p, file); }
|
||||
};
|
||||
|
||||
protected:
|
||||
FileModificationDetector modDetector;
|
||||
std::unique_ptr<CodeDocument> codeDoc;
|
||||
Project* project;
|
||||
|
||||
std::unique_ptr<CodeEditorComponent::State> lastState;
|
||||
|
||||
void reloadInternal();
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
};
|
||||
|
||||
class GenericCodeEditorComponent;
|
||||
|
||||
//==============================================================================
|
||||
class SourceCodeEditor : public DocumentEditorComponent,
|
||||
private ValueTree::Listener,
|
||||
private CodeDocument::Listener
|
||||
{
|
||||
public:
|
||||
SourceCodeEditor (OpenDocumentManager::Document*, CodeDocument&);
|
||||
SourceCodeEditor (OpenDocumentManager::Document*, GenericCodeEditorComponent*);
|
||||
~SourceCodeEditor() override;
|
||||
|
||||
void scrollToKeepRangeOnScreen (Range<int> range);
|
||||
void highlight (Range<int> range, bool cursorAtStart);
|
||||
|
||||
std::unique_ptr<GenericCodeEditorComponent> editor;
|
||||
|
||||
private:
|
||||
void resized() override;
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
|
||||
void valueTreeParentChanged (ValueTree&) override;
|
||||
void valueTreeRedirected (ValueTree&) override;
|
||||
|
||||
void codeDocumentTextInserted (const String&, int) override;
|
||||
void codeDocumentTextDeleted (int, int) override;
|
||||
|
||||
void setEditor (GenericCodeEditorComponent*);
|
||||
void updateColourScheme();
|
||||
void checkSaveState();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SourceCodeEditor)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class GenericCodeEditorComponent : public CodeEditorComponent
|
||||
{
|
||||
public:
|
||||
GenericCodeEditorComponent (const File&, CodeDocument&, CodeTokeniser*);
|
||||
~GenericCodeEditorComponent() override;
|
||||
|
||||
void addPopupMenuItems (PopupMenu&, const MouseEvent*) override;
|
||||
void performPopupMenuAction (int menuItemID) override;
|
||||
|
||||
void getAllCommands (Array<CommandID>&) override;
|
||||
void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
|
||||
bool perform (const InvocationInfo&) override;
|
||||
|
||||
void showFindPanel();
|
||||
void hideFindPanel();
|
||||
void findSelection();
|
||||
void findNext (bool forwards, bool skipCurrentSelection);
|
||||
void handleEscapeKey() override;
|
||||
void editorViewportPositionChanged() override;
|
||||
|
||||
void resized() override;
|
||||
|
||||
static String getSearchString() { return getAppSettings().getGlobalProperties().getValue ("searchString"); }
|
||||
static void setSearchString (const String& s) { getAppSettings().getGlobalProperties().setValue ("searchString", s); }
|
||||
static bool isCaseSensitiveSearch() { return getAppSettings().getGlobalProperties().getBoolValue ("searchCaseSensitive"); }
|
||||
static void setCaseSensitiveSearch (bool b) { getAppSettings().getGlobalProperties().setValue ("searchCaseSensitive", b); }
|
||||
|
||||
struct Listener
|
||||
{
|
||||
virtual ~Listener() {}
|
||||
virtual void codeEditorViewportMoved (CodeEditorComponent&) = 0;
|
||||
};
|
||||
|
||||
void addListener (Listener* listener);
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
private:
|
||||
File file;
|
||||
class FindPanel;
|
||||
std::unique_ptr<FindPanel> findPanel;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericCodeEditorComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class CppCodeEditorComponent : public GenericCodeEditorComponent
|
||||
{
|
||||
public:
|
||||
CppCodeEditorComponent (const File&, CodeDocument&);
|
||||
~CppCodeEditorComponent() override;
|
||||
|
||||
void addPopupMenuItems (PopupMenu&, const MouseEvent*) override;
|
||||
void performPopupMenuAction (int menuItemID) override;
|
||||
|
||||
void handleReturnKey() override;
|
||||
void insertTextAtCaret (const String& newText) override;
|
||||
|
||||
private:
|
||||
void insertComponentClass();
|
||||
|
||||
std::unique_ptr<AlertWindow> asyncAlertWindow;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CppCodeEditorComponent)
|
||||
};
|
||||
|
||||
@@ -1,389 +1,389 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ButtonHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
ButtonHandler (const String& typeDescription_,
|
||||
const String& className_,
|
||||
const std::type_info& componentClass,
|
||||
const int defaultWidth_,
|
||||
const int defaultHeight_)
|
||||
: ComponentTypeHandler (typeDescription_, className_, componentClass,
|
||||
defaultWidth_, defaultHeight_)
|
||||
{}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* b = dynamic_cast<Button*> (component))
|
||||
{
|
||||
props.add (new ButtonTextProperty (b, document));
|
||||
|
||||
props.add (new ButtonCallbackProperty (b, document));
|
||||
|
||||
props.add (new ButtonRadioGroupProperty (b, document));
|
||||
|
||||
props.add (new ButtonConnectedEdgeProperty ("connected left", Button::ConnectedOnLeft, b, document));
|
||||
props.add (new ButtonConnectedEdgeProperty ("connected right", Button::ConnectedOnRight, b, document));
|
||||
props.add (new ButtonConnectedEdgeProperty ("connected top", Button::ConnectedOnTop, b, document));
|
||||
props.add (new ButtonConnectedEdgeProperty ("connected bottom", Button::ConnectedOnBottom, b, document));
|
||||
}
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
Button* const b = dynamic_cast<Button*> (comp);
|
||||
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("buttonText", b->getButtonText());
|
||||
e->setAttribute ("connectedEdges", b->getConnectedEdgeFlags());
|
||||
e->setAttribute ("needsCallback", needsButtonListener (b));
|
||||
e->setAttribute ("radioGroupId", b->getRadioGroupId());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
Button* const b = dynamic_cast<Button*> (comp);
|
||||
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
b->setButtonText (xml.getStringAttribute ("buttonText", b->getButtonText()));
|
||||
b->setConnectedEdges (xml.getIntAttribute ("connectedEdges", 0));
|
||||
setNeedsButtonListener (b, xml.getBoolAttribute ("needsCallback", true));
|
||||
b->setRadioGroupId (xml.getIntAttribute ("radioGroupId", 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* component) override
|
||||
{
|
||||
return quotedString (component->getName(), false);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
Button* const b = dynamic_cast<Button*> (component);
|
||||
|
||||
if (b->getButtonText() != b->getName())
|
||||
{
|
||||
code.constructorCode
|
||||
<< memberVariableName << "->setButtonText ("
|
||||
<< quotedString (b->getButtonText(), code.shouldUseTransMacro()) << ");\n";
|
||||
}
|
||||
|
||||
if (b->getConnectedEdgeFlags() != 0)
|
||||
{
|
||||
StringArray flags;
|
||||
|
||||
if (b->isConnectedOnLeft())
|
||||
flags.add ("juce::Button::ConnectedOnLeft");
|
||||
|
||||
if (b->isConnectedOnRight())
|
||||
flags.add ("juce::Button::ConnectedOnRight");
|
||||
|
||||
if (b->isConnectedOnTop())
|
||||
flags.add ("juce::Button::ConnectedOnTop");
|
||||
|
||||
if (b->isConnectedOnBottom())
|
||||
flags.add ("juce::Button::ConnectedOnBottom");
|
||||
|
||||
String s;
|
||||
s << memberVariableName << "->setConnectedEdges ("
|
||||
<< flags.joinIntoString (" | ") << ");\n";
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
|
||||
if (b->getRadioGroupId() != 0)
|
||||
code.constructorCode << memberVariableName << "->setRadioGroupId ("
|
||||
<< b->getRadioGroupId() << ");\n";
|
||||
|
||||
if (needsButtonListener (component))
|
||||
code.constructorCode << memberVariableName << "->addListener (this);\n";
|
||||
}
|
||||
|
||||
void fillInGeneratedCode (Component* component, GeneratedCode& code) override
|
||||
{
|
||||
ComponentTypeHandler::fillInGeneratedCode (component, code);
|
||||
|
||||
if (needsButtonListener (component))
|
||||
{
|
||||
String& callback = code.getCallbackCode ("public juce::Button::Listener",
|
||||
"void",
|
||||
"buttonClicked (juce::Button* buttonThatWasClicked)",
|
||||
true);
|
||||
|
||||
if (callback.isNotEmpty())
|
||||
callback << "else ";
|
||||
|
||||
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
|
||||
const String userCodeComment ("UserButtonCode_" + memberVariableName);
|
||||
|
||||
callback
|
||||
<< "if (buttonThatWasClicked == " << memberVariableName << ".get())\n"
|
||||
<< "{\n //[" << userCodeComment << "] -- add your button handler code here..\n //[/" << userCodeComment << "]\n}\n";
|
||||
}
|
||||
}
|
||||
|
||||
static bool needsButtonListener (Component* button)
|
||||
{
|
||||
return button->getProperties().getWithDefault ("generateListenerCallback", true);
|
||||
}
|
||||
|
||||
static void setNeedsButtonListener (Component* button, const bool shouldDoCallback)
|
||||
{
|
||||
button->getProperties().set ("generateListenerCallback", shouldDoCallback);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ButtonTextProperty : public ComponentTextProperty <Button>
|
||||
{
|
||||
public:
|
||||
ButtonTextProperty (Button* button_, JucerDocument& doc)
|
||||
: ComponentTextProperty <Button> ("text", 100, false, button_, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ButtonTextChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change button text");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getButtonText();
|
||||
}
|
||||
|
||||
private:
|
||||
class ButtonTextChangeAction : public ComponentUndoableAction <Button>
|
||||
{
|
||||
public:
|
||||
ButtonTextChangeAction (Button* const comp, ComponentLayout& l, const String& newName_)
|
||||
: ComponentUndoableAction <Button> (comp, l),
|
||||
newName (newName_)
|
||||
{
|
||||
oldName = comp->getButtonText();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setButtonText (newName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setButtonText (oldName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
class ButtonCallbackProperty : public ComponentBooleanProperty <Button>
|
||||
{
|
||||
public:
|
||||
ButtonCallbackProperty (Button* b, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <Button> ("callback", "Generate ButtonListener", "Generate ButtonListener", b, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new ButtonCallbackChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change button callback");
|
||||
}
|
||||
|
||||
bool getState() const { return needsButtonListener (component); }
|
||||
|
||||
private:
|
||||
class ButtonCallbackChangeAction : public ComponentUndoableAction <Button>
|
||||
{
|
||||
public:
|
||||
ButtonCallbackChangeAction (Button* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <Button> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = needsButtonListener (comp);
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
setNeedsButtonListener (getComponent(), newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
setNeedsButtonListener (getComponent(), oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
class ButtonRadioGroupProperty : public ComponentTextProperty <Button>
|
||||
{
|
||||
public:
|
||||
ButtonRadioGroupProperty (Button* const button_, JucerDocument& doc)
|
||||
: ComponentTextProperty <Button> ("radio group", 10, false, button_, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ButtonRadioGroupChangeAction (component, *document.getComponentLayout(), newText.getIntValue()),
|
||||
"Change radio group ID");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return String (component->getRadioGroupId());
|
||||
}
|
||||
|
||||
private:
|
||||
class ButtonRadioGroupChangeAction : public ComponentUndoableAction <Button>
|
||||
{
|
||||
public:
|
||||
ButtonRadioGroupChangeAction (Button* const comp, ComponentLayout& l, const int newId_)
|
||||
: ComponentUndoableAction <Button> (comp, l),
|
||||
newId (newId_)
|
||||
{
|
||||
oldId = comp->getRadioGroupId();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setRadioGroupId (newId);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setRadioGroupId (oldId);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
int newId, oldId;
|
||||
};
|
||||
};
|
||||
|
||||
class ButtonConnectedEdgeProperty : public ComponentBooleanProperty <Button>
|
||||
{
|
||||
public:
|
||||
ButtonConnectedEdgeProperty (const String& name, const int flag_,
|
||||
Button* b, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <Button> (name, "Connected", "Connected", b, doc),
|
||||
flag (flag_)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new ButtonConnectedChangeAction (component, *document.getComponentLayout(), flag, newState),
|
||||
"Change button connected edges");
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return (component->getConnectedEdgeFlags() & flag) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const int flag;
|
||||
|
||||
class ButtonConnectedChangeAction : public ComponentUndoableAction <Button>
|
||||
{
|
||||
public:
|
||||
ButtonConnectedChangeAction (Button* const comp, ComponentLayout& l, const int flag_, const bool newState_)
|
||||
: ComponentUndoableAction <Button> (comp, l),
|
||||
flag (flag_),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = ((comp->getConnectedEdgeFlags() & flag) != 0);
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
|
||||
if (newState)
|
||||
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
|
||||
else
|
||||
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
|
||||
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
|
||||
if (oldState)
|
||||
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
|
||||
else
|
||||
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
|
||||
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
const int flag;
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ButtonHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
ButtonHandler (const String& typeDescription_,
|
||||
const String& className_,
|
||||
const std::type_info& componentClass,
|
||||
const int defaultWidth_,
|
||||
const int defaultHeight_)
|
||||
: ComponentTypeHandler (typeDescription_, className_, componentClass,
|
||||
defaultWidth_, defaultHeight_)
|
||||
{}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* b = dynamic_cast<Button*> (component))
|
||||
{
|
||||
props.add (new ButtonTextProperty (b, document));
|
||||
|
||||
props.add (new ButtonCallbackProperty (b, document));
|
||||
|
||||
props.add (new ButtonRadioGroupProperty (b, document));
|
||||
|
||||
props.add (new ButtonConnectedEdgeProperty ("connected left", Button::ConnectedOnLeft, b, document));
|
||||
props.add (new ButtonConnectedEdgeProperty ("connected right", Button::ConnectedOnRight, b, document));
|
||||
props.add (new ButtonConnectedEdgeProperty ("connected top", Button::ConnectedOnTop, b, document));
|
||||
props.add (new ButtonConnectedEdgeProperty ("connected bottom", Button::ConnectedOnBottom, b, document));
|
||||
}
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
Button* const b = dynamic_cast<Button*> (comp);
|
||||
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("buttonText", b->getButtonText());
|
||||
e->setAttribute ("connectedEdges", b->getConnectedEdgeFlags());
|
||||
e->setAttribute ("needsCallback", needsButtonListener (b));
|
||||
e->setAttribute ("radioGroupId", b->getRadioGroupId());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
Button* const b = dynamic_cast<Button*> (comp);
|
||||
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
b->setButtonText (xml.getStringAttribute ("buttonText", b->getButtonText()));
|
||||
b->setConnectedEdges (xml.getIntAttribute ("connectedEdges", 0));
|
||||
setNeedsButtonListener (b, xml.getBoolAttribute ("needsCallback", true));
|
||||
b->setRadioGroupId (xml.getIntAttribute ("radioGroupId", 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* component) override
|
||||
{
|
||||
return quotedString (component->getName(), false);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
Button* const b = dynamic_cast<Button*> (component);
|
||||
|
||||
if (b->getButtonText() != b->getName())
|
||||
{
|
||||
code.constructorCode
|
||||
<< memberVariableName << "->setButtonText ("
|
||||
<< quotedString (b->getButtonText(), code.shouldUseTransMacro()) << ");\n";
|
||||
}
|
||||
|
||||
if (b->getConnectedEdgeFlags() != 0)
|
||||
{
|
||||
StringArray flags;
|
||||
|
||||
if (b->isConnectedOnLeft())
|
||||
flags.add ("juce::Button::ConnectedOnLeft");
|
||||
|
||||
if (b->isConnectedOnRight())
|
||||
flags.add ("juce::Button::ConnectedOnRight");
|
||||
|
||||
if (b->isConnectedOnTop())
|
||||
flags.add ("juce::Button::ConnectedOnTop");
|
||||
|
||||
if (b->isConnectedOnBottom())
|
||||
flags.add ("juce::Button::ConnectedOnBottom");
|
||||
|
||||
String s;
|
||||
s << memberVariableName << "->setConnectedEdges ("
|
||||
<< flags.joinIntoString (" | ") << ");\n";
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
|
||||
if (b->getRadioGroupId() != 0)
|
||||
code.constructorCode << memberVariableName << "->setRadioGroupId ("
|
||||
<< b->getRadioGroupId() << ");\n";
|
||||
|
||||
if (needsButtonListener (component))
|
||||
code.constructorCode << memberVariableName << "->addListener (this);\n";
|
||||
}
|
||||
|
||||
void fillInGeneratedCode (Component* component, GeneratedCode& code) override
|
||||
{
|
||||
ComponentTypeHandler::fillInGeneratedCode (component, code);
|
||||
|
||||
if (needsButtonListener (component))
|
||||
{
|
||||
String& callback = code.getCallbackCode ("public juce::Button::Listener",
|
||||
"void",
|
||||
"buttonClicked (juce::Button* buttonThatWasClicked)",
|
||||
true);
|
||||
|
||||
if (callback.isNotEmpty())
|
||||
callback << "else ";
|
||||
|
||||
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
|
||||
const String userCodeComment ("UserButtonCode_" + memberVariableName);
|
||||
|
||||
callback
|
||||
<< "if (buttonThatWasClicked == " << memberVariableName << ".get())\n"
|
||||
<< "{\n //[" << userCodeComment << "] -- add your button handler code here..\n //[/" << userCodeComment << "]\n}\n";
|
||||
}
|
||||
}
|
||||
|
||||
static bool needsButtonListener (Component* button)
|
||||
{
|
||||
return button->getProperties().getWithDefault ("generateListenerCallback", true);
|
||||
}
|
||||
|
||||
static void setNeedsButtonListener (Component* button, const bool shouldDoCallback)
|
||||
{
|
||||
button->getProperties().set ("generateListenerCallback", shouldDoCallback);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ButtonTextProperty : public ComponentTextProperty <Button>
|
||||
{
|
||||
public:
|
||||
ButtonTextProperty (Button* button_, JucerDocument& doc)
|
||||
: ComponentTextProperty <Button> ("text", 100, false, button_, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ButtonTextChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change button text");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getButtonText();
|
||||
}
|
||||
|
||||
private:
|
||||
class ButtonTextChangeAction : public ComponentUndoableAction <Button>
|
||||
{
|
||||
public:
|
||||
ButtonTextChangeAction (Button* const comp, ComponentLayout& l, const String& newName_)
|
||||
: ComponentUndoableAction <Button> (comp, l),
|
||||
newName (newName_)
|
||||
{
|
||||
oldName = comp->getButtonText();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setButtonText (newName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setButtonText (oldName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
class ButtonCallbackProperty : public ComponentBooleanProperty <Button>
|
||||
{
|
||||
public:
|
||||
ButtonCallbackProperty (Button* b, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <Button> ("callback", "Generate ButtonListener", "Generate ButtonListener", b, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new ButtonCallbackChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change button callback");
|
||||
}
|
||||
|
||||
bool getState() const { return needsButtonListener (component); }
|
||||
|
||||
private:
|
||||
class ButtonCallbackChangeAction : public ComponentUndoableAction <Button>
|
||||
{
|
||||
public:
|
||||
ButtonCallbackChangeAction (Button* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <Button> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = needsButtonListener (comp);
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
setNeedsButtonListener (getComponent(), newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
setNeedsButtonListener (getComponent(), oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
class ButtonRadioGroupProperty : public ComponentTextProperty <Button>
|
||||
{
|
||||
public:
|
||||
ButtonRadioGroupProperty (Button* const button_, JucerDocument& doc)
|
||||
: ComponentTextProperty <Button> ("radio group", 10, false, button_, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ButtonRadioGroupChangeAction (component, *document.getComponentLayout(), newText.getIntValue()),
|
||||
"Change radio group ID");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return String (component->getRadioGroupId());
|
||||
}
|
||||
|
||||
private:
|
||||
class ButtonRadioGroupChangeAction : public ComponentUndoableAction <Button>
|
||||
{
|
||||
public:
|
||||
ButtonRadioGroupChangeAction (Button* const comp, ComponentLayout& l, const int newId_)
|
||||
: ComponentUndoableAction <Button> (comp, l),
|
||||
newId (newId_)
|
||||
{
|
||||
oldId = comp->getRadioGroupId();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setRadioGroupId (newId);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setRadioGroupId (oldId);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
int newId, oldId;
|
||||
};
|
||||
};
|
||||
|
||||
class ButtonConnectedEdgeProperty : public ComponentBooleanProperty <Button>
|
||||
{
|
||||
public:
|
||||
ButtonConnectedEdgeProperty (const String& name, const int flag_,
|
||||
Button* b, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <Button> (name, "Connected", "Connected", b, doc),
|
||||
flag (flag_)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new ButtonConnectedChangeAction (component, *document.getComponentLayout(), flag, newState),
|
||||
"Change button connected edges");
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return (component->getConnectedEdgeFlags() & flag) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const int flag;
|
||||
|
||||
class ButtonConnectedChangeAction : public ComponentUndoableAction <Button>
|
||||
{
|
||||
public:
|
||||
ButtonConnectedChangeAction (Button* const comp, ComponentLayout& l, const int flag_, const bool newState_)
|
||||
: ComponentUndoableAction <Button> (comp, l),
|
||||
flag (flag_),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = ((comp->getConnectedEdgeFlags() & flag) != 0);
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
|
||||
if (newState)
|
||||
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
|
||||
else
|
||||
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
|
||||
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
|
||||
if (oldState)
|
||||
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
|
||||
else
|
||||
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
|
||||
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
const int flag;
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,446 +1,446 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ComboBoxHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
ComboBoxHandler()
|
||||
: ComponentTypeHandler ("Combo Box", "juce::ComboBox", typeid (ComboBox), 150, 24)
|
||||
{}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new ComboBox ("new combo box");
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (auto* const c = dynamic_cast<ComboBox*> (comp))
|
||||
{
|
||||
if (auto* e = ComponentTypeHandler::createXmlFor (comp, layout))
|
||||
{
|
||||
e->setAttribute ("editable", c->isTextEditable());
|
||||
e->setAttribute ("layout", c->getJustificationType().getFlags());
|
||||
e->setAttribute ("items", c->getProperties() ["items"].toString());
|
||||
e->setAttribute ("textWhenNonSelected", c->getTextWhenNothingSelected());
|
||||
e->setAttribute ("textWhenNoItems", c->getTextWhenNoChoicesAvailable());
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
ComboBox defaultBox;
|
||||
|
||||
if (ComboBox* const c = dynamic_cast<ComboBox*> (comp))
|
||||
{
|
||||
c->setEditableText (xml.getBoolAttribute ("editable", defaultBox.isTextEditable()));
|
||||
c->setJustificationType (Justification (xml.getIntAttribute ("layout", defaultBox.getJustificationType().getFlags())));
|
||||
c->getProperties().set ("items", xml.getStringAttribute ("items", String()));
|
||||
c->setTextWhenNothingSelected (xml.getStringAttribute ("textWhenNonSelected", defaultBox.getTextWhenNothingSelected()));
|
||||
c->setTextWhenNoChoicesAvailable (xml.getStringAttribute ("textWhenNoItems", defaultBox.getTextWhenNoChoicesAvailable()));
|
||||
|
||||
updateItems (c);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* c = dynamic_cast<ComboBox*> (component))
|
||||
{
|
||||
props.add (new ComboItemsProperty (c, document));
|
||||
props.add (new ComboEditableProperty (c, document));
|
||||
props.add (new ComboJustificationProperty (c, document));
|
||||
props.add (new ComboTextWhenNoneSelectedProperty (c, document));
|
||||
props.add (new ComboTextWhenNoItemsProperty (c, document));
|
||||
}
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* component) override
|
||||
{
|
||||
return quotedString (component->getName(), false);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
ComboBox* const c = dynamic_cast<ComboBox*> (component);
|
||||
|
||||
if (c == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
String s;
|
||||
s << memberVariableName << "->setEditableText (" << CodeHelpers::boolLiteral (c->isTextEditable()) << ");\n"
|
||||
<< memberVariableName << "->setJustificationType (" << CodeHelpers::justificationToCode (c->getJustificationType()) << ");\n"
|
||||
<< memberVariableName << "->setTextWhenNothingSelected (" << quotedString (c->getTextWhenNothingSelected(), code.shouldUseTransMacro()) << ");\n"
|
||||
<< memberVariableName << "->setTextWhenNoChoicesAvailable (" << quotedString (c->getTextWhenNoChoicesAvailable(), code.shouldUseTransMacro()) << ");\n";
|
||||
|
||||
StringArray lines;
|
||||
lines.addLines (c->getProperties() ["items"].toString());
|
||||
int itemId = 1;
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (lines[i].trim().isEmpty())
|
||||
s << memberVariableName << "->addSeparator();\n";
|
||||
else
|
||||
s << memberVariableName << "->addItem ("
|
||||
<< quotedString (lines[i], code.shouldUseTransMacro()) << ", " << itemId++ << ");\n";
|
||||
}
|
||||
|
||||
if (needsCallback (component))
|
||||
s << memberVariableName << "->addListener (this);\n";
|
||||
|
||||
s << '\n';
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
|
||||
void fillInGeneratedCode (Component* component, GeneratedCode& code) override
|
||||
{
|
||||
ComponentTypeHandler::fillInGeneratedCode (component, code);
|
||||
|
||||
if (needsCallback (component))
|
||||
{
|
||||
String& callback = code.getCallbackCode ("public juce::ComboBox::Listener",
|
||||
"void",
|
||||
"comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)",
|
||||
true);
|
||||
|
||||
if (callback.trim().isNotEmpty())
|
||||
callback << "else ";
|
||||
|
||||
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
|
||||
const String userCodeComment ("UserComboBoxCode_" + memberVariableName);
|
||||
|
||||
callback
|
||||
<< "if (comboBoxThatHasChanged == " << memberVariableName << ".get())\n"
|
||||
<< "{\n //[" << userCodeComment << "] -- add your combo box handling code here..\n //[/" << userCodeComment << "]\n}\n";
|
||||
}
|
||||
}
|
||||
|
||||
static void updateItems (ComboBox* c)
|
||||
{
|
||||
StringArray lines;
|
||||
lines.addLines (c->getProperties() ["items"].toString());
|
||||
|
||||
c->clear();
|
||||
int itemId = 1;
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (lines[i].trim().isEmpty())
|
||||
c->addSeparator();
|
||||
else
|
||||
c->addItem (lines[i], itemId++);
|
||||
}
|
||||
}
|
||||
|
||||
static bool needsCallback (Component*)
|
||||
{
|
||||
return true; // xxx should be configurable
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboEditableProperty : public ComponentBooleanProperty <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboEditableProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <ComboBox> ("editable", "Text is editable", "Text is editable", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new ComboEditableChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change combo box editability");
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return component->isTextEditable();
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboEditableChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboEditableChangeAction (ComboBox* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isTextEditable();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setEditableText (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setEditableText (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComboJustificationProperty : public JustificationProperty
|
||||
{
|
||||
public:
|
||||
ComboJustificationProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: JustificationProperty ("text layout", false),
|
||||
component (comp),
|
||||
document (doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setJustification (Justification newJustification)
|
||||
{
|
||||
document.perform (new ComboJustifyChangeAction (component, *document.getComponentLayout(), newJustification),
|
||||
"Change combo box justification");
|
||||
}
|
||||
|
||||
Justification getJustification() const { return component->getJustificationType(); }
|
||||
|
||||
private:
|
||||
ComboBox* const component;
|
||||
JucerDocument& document;
|
||||
|
||||
class ComboJustifyChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboJustifyChangeAction (ComboBox* const comp, ComponentLayout& l, Justification newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_),
|
||||
oldState (comp->getJustificationType())
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setJustificationType (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setJustificationType (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
Justification newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComboItemsProperty : public ComponentTextProperty <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboItemsProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <ComboBox> ("items", 10000, true, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ComboItemsChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change combo box items");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getProperties() ["items"];
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboItemsChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboItemsChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getProperties() ["items"];
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->getProperties().set ("items", newState);
|
||||
ComboBoxHandler::updateItems (getComponent());
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->getProperties().set ("items", oldState);
|
||||
ComboBoxHandler::updateItems (getComponent());
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComboTextWhenNoneSelectedProperty : public ComponentTextProperty <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboTextWhenNoneSelectedProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <ComboBox> ("text when none selected", 200, false, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ComboNonSelTextChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change combo box text when nothing selected");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getTextWhenNothingSelected();
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboNonSelTextChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboNonSelTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getTextWhenNothingSelected();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextWhenNothingSelected (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextWhenNothingSelected (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComboTextWhenNoItemsProperty : public ComponentTextProperty <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboTextWhenNoItemsProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <ComboBox> ("text when no items", 200, false, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ComboNoItemTextChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change combo box 'no items' text");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getTextWhenNoChoicesAvailable();
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboNoItemTextChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboNoItemTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getTextWhenNoChoicesAvailable();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextWhenNoChoicesAvailable (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextWhenNoChoicesAvailable (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ComboBoxHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
ComboBoxHandler()
|
||||
: ComponentTypeHandler ("Combo Box", "juce::ComboBox", typeid (ComboBox), 150, 24)
|
||||
{}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new ComboBox ("new combo box");
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (auto* const c = dynamic_cast<ComboBox*> (comp))
|
||||
{
|
||||
if (auto* e = ComponentTypeHandler::createXmlFor (comp, layout))
|
||||
{
|
||||
e->setAttribute ("editable", c->isTextEditable());
|
||||
e->setAttribute ("layout", c->getJustificationType().getFlags());
|
||||
e->setAttribute ("items", c->getProperties() ["items"].toString());
|
||||
e->setAttribute ("textWhenNonSelected", c->getTextWhenNothingSelected());
|
||||
e->setAttribute ("textWhenNoItems", c->getTextWhenNoChoicesAvailable());
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
ComboBox defaultBox;
|
||||
|
||||
if (ComboBox* const c = dynamic_cast<ComboBox*> (comp))
|
||||
{
|
||||
c->setEditableText (xml.getBoolAttribute ("editable", defaultBox.isTextEditable()));
|
||||
c->setJustificationType (Justification (xml.getIntAttribute ("layout", defaultBox.getJustificationType().getFlags())));
|
||||
c->getProperties().set ("items", xml.getStringAttribute ("items", String()));
|
||||
c->setTextWhenNothingSelected (xml.getStringAttribute ("textWhenNonSelected", defaultBox.getTextWhenNothingSelected()));
|
||||
c->setTextWhenNoChoicesAvailable (xml.getStringAttribute ("textWhenNoItems", defaultBox.getTextWhenNoChoicesAvailable()));
|
||||
|
||||
updateItems (c);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* c = dynamic_cast<ComboBox*> (component))
|
||||
{
|
||||
props.add (new ComboItemsProperty (c, document));
|
||||
props.add (new ComboEditableProperty (c, document));
|
||||
props.add (new ComboJustificationProperty (c, document));
|
||||
props.add (new ComboTextWhenNoneSelectedProperty (c, document));
|
||||
props.add (new ComboTextWhenNoItemsProperty (c, document));
|
||||
}
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* component) override
|
||||
{
|
||||
return quotedString (component->getName(), false);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
ComboBox* const c = dynamic_cast<ComboBox*> (component);
|
||||
|
||||
if (c == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
String s;
|
||||
s << memberVariableName << "->setEditableText (" << CodeHelpers::boolLiteral (c->isTextEditable()) << ");\n"
|
||||
<< memberVariableName << "->setJustificationType (" << CodeHelpers::justificationToCode (c->getJustificationType()) << ");\n"
|
||||
<< memberVariableName << "->setTextWhenNothingSelected (" << quotedString (c->getTextWhenNothingSelected(), code.shouldUseTransMacro()) << ");\n"
|
||||
<< memberVariableName << "->setTextWhenNoChoicesAvailable (" << quotedString (c->getTextWhenNoChoicesAvailable(), code.shouldUseTransMacro()) << ");\n";
|
||||
|
||||
StringArray lines;
|
||||
lines.addLines (c->getProperties() ["items"].toString());
|
||||
int itemId = 1;
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (lines[i].trim().isEmpty())
|
||||
s << memberVariableName << "->addSeparator();\n";
|
||||
else
|
||||
s << memberVariableName << "->addItem ("
|
||||
<< quotedString (lines[i], code.shouldUseTransMacro()) << ", " << itemId++ << ");\n";
|
||||
}
|
||||
|
||||
if (needsCallback (component))
|
||||
s << memberVariableName << "->addListener (this);\n";
|
||||
|
||||
s << '\n';
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
|
||||
void fillInGeneratedCode (Component* component, GeneratedCode& code) override
|
||||
{
|
||||
ComponentTypeHandler::fillInGeneratedCode (component, code);
|
||||
|
||||
if (needsCallback (component))
|
||||
{
|
||||
String& callback = code.getCallbackCode ("public juce::ComboBox::Listener",
|
||||
"void",
|
||||
"comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)",
|
||||
true);
|
||||
|
||||
if (callback.trim().isNotEmpty())
|
||||
callback << "else ";
|
||||
|
||||
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
|
||||
const String userCodeComment ("UserComboBoxCode_" + memberVariableName);
|
||||
|
||||
callback
|
||||
<< "if (comboBoxThatHasChanged == " << memberVariableName << ".get())\n"
|
||||
<< "{\n //[" << userCodeComment << "] -- add your combo box handling code here..\n //[/" << userCodeComment << "]\n}\n";
|
||||
}
|
||||
}
|
||||
|
||||
static void updateItems (ComboBox* c)
|
||||
{
|
||||
StringArray lines;
|
||||
lines.addLines (c->getProperties() ["items"].toString());
|
||||
|
||||
c->clear();
|
||||
int itemId = 1;
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (lines[i].trim().isEmpty())
|
||||
c->addSeparator();
|
||||
else
|
||||
c->addItem (lines[i], itemId++);
|
||||
}
|
||||
}
|
||||
|
||||
static bool needsCallback (Component*)
|
||||
{
|
||||
return true; // xxx should be configurable
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboEditableProperty : public ComponentBooleanProperty <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboEditableProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <ComboBox> ("editable", "Text is editable", "Text is editable", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new ComboEditableChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change combo box editability");
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return component->isTextEditable();
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboEditableChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboEditableChangeAction (ComboBox* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isTextEditable();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setEditableText (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setEditableText (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComboJustificationProperty : public JustificationProperty
|
||||
{
|
||||
public:
|
||||
ComboJustificationProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: JustificationProperty ("text layout", false),
|
||||
component (comp),
|
||||
document (doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setJustification (Justification newJustification)
|
||||
{
|
||||
document.perform (new ComboJustifyChangeAction (component, *document.getComponentLayout(), newJustification),
|
||||
"Change combo box justification");
|
||||
}
|
||||
|
||||
Justification getJustification() const { return component->getJustificationType(); }
|
||||
|
||||
private:
|
||||
ComboBox* const component;
|
||||
JucerDocument& document;
|
||||
|
||||
class ComboJustifyChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboJustifyChangeAction (ComboBox* const comp, ComponentLayout& l, Justification newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_),
|
||||
oldState (comp->getJustificationType())
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setJustificationType (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setJustificationType (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
Justification newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComboItemsProperty : public ComponentTextProperty <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboItemsProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <ComboBox> ("items", 10000, true, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ComboItemsChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change combo box items");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getProperties() ["items"];
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboItemsChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboItemsChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getProperties() ["items"];
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->getProperties().set ("items", newState);
|
||||
ComboBoxHandler::updateItems (getComponent());
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->getProperties().set ("items", oldState);
|
||||
ComboBoxHandler::updateItems (getComponent());
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComboTextWhenNoneSelectedProperty : public ComponentTextProperty <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboTextWhenNoneSelectedProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <ComboBox> ("text when none selected", 200, false, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ComboNonSelTextChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change combo box text when nothing selected");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getTextWhenNothingSelected();
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboNonSelTextChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboNonSelTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getTextWhenNothingSelected();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextWhenNothingSelected (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextWhenNothingSelected (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComboTextWhenNoItemsProperty : public ComponentTextProperty <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboTextWhenNoItemsProperty (ComboBox* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <ComboBox> ("text when no items", 200, false, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ComboNoItemTextChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change combo box 'no items' text");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getTextWhenNoChoicesAvailable();
|
||||
}
|
||||
|
||||
private:
|
||||
class ComboNoItemTextChangeAction : public ComponentUndoableAction <ComboBox>
|
||||
{
|
||||
public:
|
||||
ComboNoItemTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <ComboBox> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getTextWhenNoChoicesAvailable();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextWhenNoChoicesAvailable (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextWhenNoChoicesAvailable (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,178 +1,178 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_ComponentTypeHandler.h"
|
||||
#include "jucer_ComponentUndoableAction.h"
|
||||
#include "../Properties/jucer_ComponentTextProperty.h"
|
||||
|
||||
//==============================================================================
|
||||
class ComponentNameProperty : public ComponentTextProperty <Component>
|
||||
{
|
||||
public:
|
||||
ComponentNameProperty (Component* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <Component> ("name", 40, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new CompNameChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change component name");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getName();
|
||||
}
|
||||
|
||||
private:
|
||||
class CompNameChangeAction : public ComponentUndoableAction <Component>
|
||||
{
|
||||
public:
|
||||
CompNameChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
|
||||
: ComponentUndoableAction <Component> (comp, l),
|
||||
newName (nm), oldName (comp->getName())
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setName (newName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setName (oldName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComponentMemberNameProperty : public ComponentTextProperty <Component>
|
||||
{
|
||||
public:
|
||||
ComponentMemberNameProperty (Component* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <Component> ("member name", 40, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new CompMemberNameChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change component member name");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return document.getComponentLayout()->getComponentMemberVariableName (component);
|
||||
}
|
||||
|
||||
private:
|
||||
class CompMemberNameChangeAction : public ComponentUndoableAction <Component>
|
||||
{
|
||||
public:
|
||||
CompMemberNameChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
|
||||
: ComponentUndoableAction <Component> (comp, l),
|
||||
newName (nm), oldName (layout.getComponentMemberVariableName (comp))
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
layout.setComponentMemberVariableName (getComponent(), newName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
layout.setComponentMemberVariableName (getComponent(), oldName);
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ComponentVirtualClassProperty : public ComponentTextProperty <Component>
|
||||
{
|
||||
public:
|
||||
ComponentVirtualClassProperty (Component* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <Component> ("virtual class", 40, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new CompVirtualClassChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change component virtual class name");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return document.getComponentLayout()->getComponentVirtualClassName (component);
|
||||
}
|
||||
|
||||
private:
|
||||
class CompVirtualClassChangeAction : public ComponentUndoableAction <Component>
|
||||
{
|
||||
public:
|
||||
CompVirtualClassChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
|
||||
: ComponentUndoableAction <Component> (comp, l),
|
||||
newName (nm), oldName (layout.getComponentVirtualClassName (comp))
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
layout.setComponentVirtualClassName (getComponent(), newName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
layout.setComponentVirtualClassName (getComponent(), oldName);
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_ComponentTypeHandler.h"
|
||||
#include "jucer_ComponentUndoableAction.h"
|
||||
#include "../Properties/jucer_ComponentTextProperty.h"
|
||||
|
||||
//==============================================================================
|
||||
class ComponentNameProperty : public ComponentTextProperty <Component>
|
||||
{
|
||||
public:
|
||||
ComponentNameProperty (Component* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <Component> ("name", 40, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new CompNameChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change component name");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getName();
|
||||
}
|
||||
|
||||
private:
|
||||
class CompNameChangeAction : public ComponentUndoableAction <Component>
|
||||
{
|
||||
public:
|
||||
CompNameChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
|
||||
: ComponentUndoableAction <Component> (comp, l),
|
||||
newName (nm), oldName (comp->getName())
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setName (newName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setName (oldName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComponentMemberNameProperty : public ComponentTextProperty <Component>
|
||||
{
|
||||
public:
|
||||
ComponentMemberNameProperty (Component* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <Component> ("member name", 40, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new CompMemberNameChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change component member name");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return document.getComponentLayout()->getComponentMemberVariableName (component);
|
||||
}
|
||||
|
||||
private:
|
||||
class CompMemberNameChangeAction : public ComponentUndoableAction <Component>
|
||||
{
|
||||
public:
|
||||
CompMemberNameChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
|
||||
: ComponentUndoableAction <Component> (comp, l),
|
||||
newName (nm), oldName (layout.getComponentMemberVariableName (comp))
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
layout.setComponentMemberVariableName (getComponent(), newName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
layout.setComponentMemberVariableName (getComponent(), oldName);
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ComponentVirtualClassProperty : public ComponentTextProperty <Component>
|
||||
{
|
||||
public:
|
||||
ComponentVirtualClassProperty (Component* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <Component> ("virtual class", 40, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new CompVirtualClassChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change component virtual class name");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return document.getComponentLayout()->getComponentVirtualClassName (component);
|
||||
}
|
||||
|
||||
private:
|
||||
class CompVirtualClassChangeAction : public ComponentUndoableAction <Component>
|
||||
{
|
||||
public:
|
||||
CompVirtualClassChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
|
||||
: ComponentUndoableAction <Component> (comp, l),
|
||||
newName (nm), oldName (layout.getComponentVirtualClassName (comp))
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
layout.setComponentVirtualClassName (getComponent(), newName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
layout.setComponentVirtualClassName (getComponent(), oldName);
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,147 +1,147 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class ComponentOverlayComponent;
|
||||
class ComponentLayout;
|
||||
#include "../jucer_GeneratedCode.h"
|
||||
#include "../UI/jucer_RelativePositionedRectangle.h"
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for handlers that can understand the properties of all the component classes.
|
||||
*/
|
||||
class ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
ComponentTypeHandler (const String& typeDescription_,
|
||||
const String& className_,
|
||||
const std::type_info& componentClass,
|
||||
const int defaultWidth_,
|
||||
const int defaultHeight_);
|
||||
|
||||
virtual ~ComponentTypeHandler() {}
|
||||
|
||||
//==============================================================================
|
||||
virtual bool canHandle (Component& component) const;
|
||||
|
||||
static ComponentTypeHandler* getHandlerFor (Component& component);
|
||||
|
||||
//==============================================================================
|
||||
virtual String getXmlTagName() const noexcept
|
||||
{
|
||||
if (className.startsWith ("juce::"))
|
||||
return className.substring (6).toUpperCase();
|
||||
|
||||
return className.toUpperCase();
|
||||
}
|
||||
|
||||
static ComponentTypeHandler* getHandlerForXmlTag (const String& tagName);
|
||||
|
||||
virtual XmlElement* createXmlFor (Component* component, const ComponentLayout* layout);
|
||||
virtual bool restoreFromXml (const XmlElement& xml, Component* component, const ComponentLayout* layout);
|
||||
|
||||
virtual void getEditableProperties (Component* component,
|
||||
JucerDocument& document,
|
||||
Array<PropertyComponent*>& props,
|
||||
bool multipleSelected);
|
||||
|
||||
virtual void addPropertiesToPropertyPanel (Component* component,
|
||||
JucerDocument& document,
|
||||
PropertyPanel& panel,
|
||||
bool multipleSelected);
|
||||
|
||||
|
||||
void registerEditableColour (int colourId,
|
||||
const String& colourIdCode,
|
||||
const String& colourName,
|
||||
const String& xmlTagName);
|
||||
|
||||
#define registerColour(colourId, colourName, xmlTagName) \
|
||||
registerEditableColour (colourId, #colourId, colourName, xmlTagName)
|
||||
|
||||
void addColourProperties (Component* component,
|
||||
JucerDocument& document,
|
||||
Array<PropertyComponent*>& props);
|
||||
|
||||
String getColourIntialisationCode (Component* component,
|
||||
const String& objectName);
|
||||
|
||||
//==============================================================================
|
||||
virtual Component* createNewComponent (JucerDocument*) = 0;
|
||||
|
||||
virtual Component* createCopyOf (JucerDocument*, Component& existing);
|
||||
|
||||
virtual ComponentOverlayComponent* createOverlayComponent (Component* child, ComponentLayout& layout);
|
||||
|
||||
virtual void showPopupMenu (Component* component,
|
||||
ComponentLayout& layout);
|
||||
|
||||
//==============================================================================
|
||||
// Code-generation methods:
|
||||
|
||||
virtual void fillInGeneratedCode (Component* component, GeneratedCode& code);
|
||||
|
||||
virtual void fillInMemberVariableDeclarations (GeneratedCode&, Component*, const String& memberVariableName);
|
||||
virtual void fillInResizeCode (GeneratedCode&, Component*, const String& memberVariableName);
|
||||
virtual void fillInCreationCode (GeneratedCode&, Component*, const String& memberVariableName);
|
||||
virtual String getCreationParameters (GeneratedCode&, Component*);
|
||||
virtual void fillInDeletionCode (GeneratedCode&, Component*, const String& memberVariableName);
|
||||
|
||||
//==============================================================================
|
||||
const String& getTypeName() const noexcept { return typeName; }
|
||||
virtual String getClassName (Component*) const { return className; }
|
||||
|
||||
int getDefaultWidth() const noexcept { return defaultWidth; }
|
||||
int getDefaultHeight() const noexcept { return defaultHeight; }
|
||||
|
||||
static int64 getComponentId (Component* comp);
|
||||
static void setComponentId (Component* comp, const int64 newID);
|
||||
|
||||
static RelativePositionedRectangle getComponentPosition (Component* comp);
|
||||
static void setComponentPosition (Component* comp,
|
||||
const RelativePositionedRectangle& newPos,
|
||||
const ComponentLayout* layout);
|
||||
|
||||
static JucerDocument* findParentDocument (Component* component);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
const String typeName, className, virtualClass, componentClassRawName;
|
||||
int defaultWidth, defaultHeight;
|
||||
|
||||
struct ComponentColourInfo
|
||||
{
|
||||
int colourId;
|
||||
String colourIdCode, colourName, xmlTagName;
|
||||
};
|
||||
|
||||
OwnedArray<ComponentColourInfo> colours;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ComponentTypeHandler)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class ComponentOverlayComponent;
|
||||
class ComponentLayout;
|
||||
#include "../jucer_GeneratedCode.h"
|
||||
#include "../UI/jucer_RelativePositionedRectangle.h"
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for handlers that can understand the properties of all the component classes.
|
||||
*/
|
||||
class ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
ComponentTypeHandler (const String& typeDescription_,
|
||||
const String& className_,
|
||||
const std::type_info& componentClass,
|
||||
const int defaultWidth_,
|
||||
const int defaultHeight_);
|
||||
|
||||
virtual ~ComponentTypeHandler() {}
|
||||
|
||||
//==============================================================================
|
||||
virtual bool canHandle (Component& component) const;
|
||||
|
||||
static ComponentTypeHandler* getHandlerFor (Component& component);
|
||||
|
||||
//==============================================================================
|
||||
virtual String getXmlTagName() const noexcept
|
||||
{
|
||||
if (className.startsWith ("juce::"))
|
||||
return className.substring (6).toUpperCase();
|
||||
|
||||
return className.toUpperCase();
|
||||
}
|
||||
|
||||
static ComponentTypeHandler* getHandlerForXmlTag (const String& tagName);
|
||||
|
||||
virtual XmlElement* createXmlFor (Component* component, const ComponentLayout* layout);
|
||||
virtual bool restoreFromXml (const XmlElement& xml, Component* component, const ComponentLayout* layout);
|
||||
|
||||
virtual void getEditableProperties (Component* component,
|
||||
JucerDocument& document,
|
||||
Array<PropertyComponent*>& props,
|
||||
bool multipleSelected);
|
||||
|
||||
virtual void addPropertiesToPropertyPanel (Component* component,
|
||||
JucerDocument& document,
|
||||
PropertyPanel& panel,
|
||||
bool multipleSelected);
|
||||
|
||||
|
||||
void registerEditableColour (int colourId,
|
||||
const String& colourIdCode,
|
||||
const String& colourName,
|
||||
const String& xmlTagName);
|
||||
|
||||
#define registerColour(colourId, colourName, xmlTagName) \
|
||||
registerEditableColour (colourId, #colourId, colourName, xmlTagName)
|
||||
|
||||
void addColourProperties (Component* component,
|
||||
JucerDocument& document,
|
||||
Array<PropertyComponent*>& props);
|
||||
|
||||
String getColourIntialisationCode (Component* component,
|
||||
const String& objectName);
|
||||
|
||||
//==============================================================================
|
||||
virtual Component* createNewComponent (JucerDocument*) = 0;
|
||||
|
||||
virtual Component* createCopyOf (JucerDocument*, Component& existing);
|
||||
|
||||
virtual ComponentOverlayComponent* createOverlayComponent (Component* child, ComponentLayout& layout);
|
||||
|
||||
virtual void showPopupMenu (Component* component,
|
||||
ComponentLayout& layout);
|
||||
|
||||
//==============================================================================
|
||||
// Code-generation methods:
|
||||
|
||||
virtual void fillInGeneratedCode (Component* component, GeneratedCode& code);
|
||||
|
||||
virtual void fillInMemberVariableDeclarations (GeneratedCode&, Component*, const String& memberVariableName);
|
||||
virtual void fillInResizeCode (GeneratedCode&, Component*, const String& memberVariableName);
|
||||
virtual void fillInCreationCode (GeneratedCode&, Component*, const String& memberVariableName);
|
||||
virtual String getCreationParameters (GeneratedCode&, Component*);
|
||||
virtual void fillInDeletionCode (GeneratedCode&, Component*, const String& memberVariableName);
|
||||
|
||||
//==============================================================================
|
||||
const String& getTypeName() const noexcept { return typeName; }
|
||||
virtual String getClassName (Component*) const { return className; }
|
||||
|
||||
int getDefaultWidth() const noexcept { return defaultWidth; }
|
||||
int getDefaultHeight() const noexcept { return defaultHeight; }
|
||||
|
||||
static int64 getComponentId (Component* comp);
|
||||
static void setComponentId (Component* comp, const int64 newID);
|
||||
|
||||
static RelativePositionedRectangle getComponentPosition (Component* comp);
|
||||
static void setComponentPosition (Component* comp,
|
||||
const RelativePositionedRectangle& newPos,
|
||||
const ComponentLayout* layout);
|
||||
|
||||
static JucerDocument* findParentDocument (Component* component);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
const String typeName, className, virtualClass, componentClassRawName;
|
||||
int defaultWidth, defaultHeight;
|
||||
|
||||
struct ComponentColourInfo
|
||||
{
|
||||
int colourId;
|
||||
String colourIdCode, colourName, xmlTagName;
|
||||
};
|
||||
|
||||
OwnedArray<ComponentColourInfo> colours;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ComponentTypeHandler)
|
||||
};
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../UI/jucer_JucerDocumentEditor.h"
|
||||
|
||||
//==============================================================================
|
||||
template <class ComponentType>
|
||||
class ComponentUndoableAction : public UndoableAction
|
||||
{
|
||||
public:
|
||||
ComponentUndoableAction (ComponentType* const comp,
|
||||
ComponentLayout& layout_)
|
||||
: layout (layout_),
|
||||
componentIndex (layout_.indexOfComponent (comp))
|
||||
{
|
||||
jassert (comp != nullptr);
|
||||
jassert (componentIndex >= 0);
|
||||
}
|
||||
|
||||
ComponentType* getComponent() const
|
||||
{
|
||||
ComponentType* const c = dynamic_cast<ComponentType*> (layout.getComponent (componentIndex));
|
||||
jassert (c != nullptr);
|
||||
return c;
|
||||
}
|
||||
|
||||
int getSizeInUnits() { return 2; }
|
||||
|
||||
protected:
|
||||
ComponentLayout& layout;
|
||||
const int componentIndex;
|
||||
|
||||
void changed() const
|
||||
{
|
||||
jassert (layout.getDocument() != nullptr);
|
||||
layout.getDocument()->changed();
|
||||
}
|
||||
|
||||
void showCorrectTab() const
|
||||
{
|
||||
if (JucerDocumentEditor* const ed = JucerDocumentEditor::getActiveDocumentHolder())
|
||||
ed->showLayout();
|
||||
|
||||
if (layout.getSelectedSet().getNumSelected() == 0)
|
||||
if (ComponentType* const c = dynamic_cast<ComponentType*> (layout.getComponent (componentIndex)))
|
||||
layout.getSelectedSet().selectOnly (getComponent());
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentUndoableAction)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../UI/jucer_JucerDocumentEditor.h"
|
||||
|
||||
//==============================================================================
|
||||
template <class ComponentType>
|
||||
class ComponentUndoableAction : public UndoableAction
|
||||
{
|
||||
public:
|
||||
ComponentUndoableAction (ComponentType* const comp,
|
||||
ComponentLayout& layout_)
|
||||
: layout (layout_),
|
||||
componentIndex (layout_.indexOfComponent (comp))
|
||||
{
|
||||
jassert (comp != nullptr);
|
||||
jassert (componentIndex >= 0);
|
||||
}
|
||||
|
||||
ComponentType* getComponent() const
|
||||
{
|
||||
ComponentType* const c = dynamic_cast<ComponentType*> (layout.getComponent (componentIndex));
|
||||
jassert (c != nullptr);
|
||||
return c;
|
||||
}
|
||||
|
||||
int getSizeInUnits() { return 2; }
|
||||
|
||||
protected:
|
||||
ComponentLayout& layout;
|
||||
const int componentIndex;
|
||||
|
||||
void changed() const
|
||||
{
|
||||
jassert (layout.getDocument() != nullptr);
|
||||
layout.getDocument()->changed();
|
||||
}
|
||||
|
||||
void showCorrectTab() const
|
||||
{
|
||||
if (JucerDocumentEditor* const ed = JucerDocumentEditor::getActiveDocumentHolder())
|
||||
ed->showLayout();
|
||||
|
||||
if (layout.getSelectedSet().getNumSelected() == 0)
|
||||
if (ComponentType* const c = dynamic_cast<ComponentType*> (layout.getComponent (componentIndex)))
|
||||
layout.getSelectedSet().selectOnly (getComponent());
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentUndoableAction)
|
||||
};
|
||||
|
||||
@@ -1,241 +1,241 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class GenericComponent : public Component
|
||||
{
|
||||
public:
|
||||
GenericComponent()
|
||||
: Component ("new component"),
|
||||
actualClassName ("juce::Component")
|
||||
{
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (Colours::white.withAlpha (0.25f));
|
||||
|
||||
g.setColour (Colours::black.withAlpha (0.5f));
|
||||
g.drawRect (getLocalBounds());
|
||||
g.drawLine (0.0f, 0.0f, (float) getWidth(), (float) getHeight());
|
||||
g.drawLine (0.0f, (float) getHeight(), (float) getWidth(), 0.0f);
|
||||
|
||||
g.setFont (14.0f);
|
||||
g.drawText (actualClassName, 0, 0, getWidth(), getHeight() / 2, Justification::centred, true);
|
||||
}
|
||||
|
||||
void setClassName (const String& newName)
|
||||
{
|
||||
if (actualClassName != newName)
|
||||
{
|
||||
actualClassName = newName;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void setParams (const String& newParams)
|
||||
{
|
||||
if (constructorParams != newParams)
|
||||
{
|
||||
constructorParams = newParams;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
String actualClassName, constructorParams;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class GenericComponentHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
GenericComponentHandler()
|
||||
: ComponentTypeHandler ("Generic Component", "GenericComponent", typeid (GenericComponent), 150, 24)
|
||||
{}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new GenericComponent();
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("class", ((GenericComponent*) comp)->actualClassName);
|
||||
e->setAttribute ("params", ((GenericComponent*) comp)->constructorParams);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
((GenericComponent*) comp)->actualClassName = xml.getStringAttribute ("class", "juce::Component");
|
||||
((GenericComponent*) comp)->constructorParams = xml.getStringAttribute ("params", String());
|
||||
return true;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
props.add (new GenericCompClassProperty (dynamic_cast<GenericComponent*> (component), document));
|
||||
props.add (new GenericCompParamsProperty (dynamic_cast<GenericComponent*> (component), document));
|
||||
}
|
||||
|
||||
String getClassName (Component* comp) const override
|
||||
{
|
||||
return static_cast<GenericComponent*> (comp)->actualClassName;
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* comp) override
|
||||
{
|
||||
return static_cast<GenericComponent*> (comp)->constructorParams;
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
if (component->getName().isNotEmpty())
|
||||
code.constructorCode
|
||||
<< memberVariableName << "->setName ("
|
||||
<< quotedString (component->getName(), false)
|
||||
<< ");\n\n";
|
||||
else
|
||||
code.constructorCode << "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
class GenericCompClassProperty : public ComponentTextProperty <GenericComponent>
|
||||
{
|
||||
public:
|
||||
GenericCompClassProperty (GenericComponent* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <GenericComponent> ("class", 300, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new GenericCompClassChangeAction (component, *document.getComponentLayout(),
|
||||
build_tools::makeValidIdentifier (newText, false, false, true)),
|
||||
"Change generic component class");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->actualClassName;
|
||||
}
|
||||
|
||||
private:
|
||||
class GenericCompClassChangeAction : public ComponentUndoableAction <GenericComponent>
|
||||
{
|
||||
public:
|
||||
GenericCompClassChangeAction (GenericComponent* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <GenericComponent> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->actualClassName;
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setClassName (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setClassName (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
class GenericCompParamsProperty : public ComponentTextProperty <GenericComponent>
|
||||
{
|
||||
public:
|
||||
GenericCompParamsProperty (GenericComponent* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <GenericComponent> ("constructor params", 1024, true, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new GenericCompParamsChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change generic component class");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->constructorParams;
|
||||
}
|
||||
|
||||
private:
|
||||
class GenericCompParamsChangeAction : public ComponentUndoableAction <GenericComponent>
|
||||
{
|
||||
public:
|
||||
GenericCompParamsChangeAction (GenericComponent* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <GenericComponent> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->constructorParams;
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setParams (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setParams (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class GenericComponent : public Component
|
||||
{
|
||||
public:
|
||||
GenericComponent()
|
||||
: Component ("new component"),
|
||||
actualClassName ("juce::Component")
|
||||
{
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (Colours::white.withAlpha (0.25f));
|
||||
|
||||
g.setColour (Colours::black.withAlpha (0.5f));
|
||||
g.drawRect (getLocalBounds());
|
||||
g.drawLine (0.0f, 0.0f, (float) getWidth(), (float) getHeight());
|
||||
g.drawLine (0.0f, (float) getHeight(), (float) getWidth(), 0.0f);
|
||||
|
||||
g.setFont (14.0f);
|
||||
g.drawText (actualClassName, 0, 0, getWidth(), getHeight() / 2, Justification::centred, true);
|
||||
}
|
||||
|
||||
void setClassName (const String& newName)
|
||||
{
|
||||
if (actualClassName != newName)
|
||||
{
|
||||
actualClassName = newName;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void setParams (const String& newParams)
|
||||
{
|
||||
if (constructorParams != newParams)
|
||||
{
|
||||
constructorParams = newParams;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
String actualClassName, constructorParams;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class GenericComponentHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
GenericComponentHandler()
|
||||
: ComponentTypeHandler ("Generic Component", "GenericComponent", typeid (GenericComponent), 150, 24)
|
||||
{}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new GenericComponent();
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("class", ((GenericComponent*) comp)->actualClassName);
|
||||
e->setAttribute ("params", ((GenericComponent*) comp)->constructorParams);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
((GenericComponent*) comp)->actualClassName = xml.getStringAttribute ("class", "juce::Component");
|
||||
((GenericComponent*) comp)->constructorParams = xml.getStringAttribute ("params", String());
|
||||
return true;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
props.add (new GenericCompClassProperty (dynamic_cast<GenericComponent*> (component), document));
|
||||
props.add (new GenericCompParamsProperty (dynamic_cast<GenericComponent*> (component), document));
|
||||
}
|
||||
|
||||
String getClassName (Component* comp) const override
|
||||
{
|
||||
return static_cast<GenericComponent*> (comp)->actualClassName;
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* comp) override
|
||||
{
|
||||
return static_cast<GenericComponent*> (comp)->constructorParams;
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
if (component->getName().isNotEmpty())
|
||||
code.constructorCode
|
||||
<< memberVariableName << "->setName ("
|
||||
<< quotedString (component->getName(), false)
|
||||
<< ");\n\n";
|
||||
else
|
||||
code.constructorCode << "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
class GenericCompClassProperty : public ComponentTextProperty <GenericComponent>
|
||||
{
|
||||
public:
|
||||
GenericCompClassProperty (GenericComponent* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <GenericComponent> ("class", 300, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new GenericCompClassChangeAction (component, *document.getComponentLayout(),
|
||||
build_tools::makeValidIdentifier (newText, false, false, true)),
|
||||
"Change generic component class");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->actualClassName;
|
||||
}
|
||||
|
||||
private:
|
||||
class GenericCompClassChangeAction : public ComponentUndoableAction <GenericComponent>
|
||||
{
|
||||
public:
|
||||
GenericCompClassChangeAction (GenericComponent* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <GenericComponent> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->actualClassName;
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setClassName (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setClassName (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
class GenericCompParamsProperty : public ComponentTextProperty <GenericComponent>
|
||||
{
|
||||
public:
|
||||
GenericCompParamsProperty (GenericComponent* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <GenericComponent> ("constructor params", 1024, true, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new GenericCompParamsChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change generic component class");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->constructorParams;
|
||||
}
|
||||
|
||||
private:
|
||||
class GenericCompParamsChangeAction : public ComponentUndoableAction <GenericComponent>
|
||||
{
|
||||
public:
|
||||
GenericCompParamsChangeAction (GenericComponent* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <GenericComponent> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->constructorParams;
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setParams (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setParams (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,237 +1,237 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class GroupComponentHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
GroupComponentHandler()
|
||||
: ComponentTypeHandler ("Group Box", "juce::GroupComponent", typeid (GroupComponent), 200, 150)
|
||||
{
|
||||
registerColour (juce::GroupComponent::outlineColourId, "outline", "outlinecol");
|
||||
registerColour (juce::GroupComponent::textColourId, "text", "textcol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new GroupComponent ("new group", "group");
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
GroupComponent* const g = (GroupComponent*) comp;
|
||||
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("title", g->getText());
|
||||
|
||||
GroupComponent defaultComp;
|
||||
|
||||
if (g->getTextLabelPosition().getFlags() != defaultComp.getTextLabelPosition().getFlags())
|
||||
e->setAttribute ("textpos", g->getTextLabelPosition().getFlags());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
GroupComponent* const g = (GroupComponent*) comp;
|
||||
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
g->setText (xml.getStringAttribute ("title", g->getText()));
|
||||
g->setTextLabelPosition (Justification (xml.getIntAttribute ("textpos", g->getTextLabelPosition().getFlags())));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode& code, Component* component) override
|
||||
{
|
||||
GroupComponent* g = dynamic_cast<GroupComponent*> (component);
|
||||
|
||||
return quotedString (component->getName(), false)
|
||||
+ ",\n"
|
||||
+ quotedString (g->getText(), code.shouldUseTransMacro());
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
GroupComponent* const g = dynamic_cast<GroupComponent*> (component);
|
||||
|
||||
String s;
|
||||
|
||||
GroupComponent defaultComp;
|
||||
|
||||
if (g->getTextLabelPosition().getFlags() != defaultComp.getTextLabelPosition().getFlags())
|
||||
{
|
||||
s << memberVariableName << "->setTextLabelPosition ("
|
||||
<< CodeHelpers::justificationToCode (g->getTextLabelPosition())
|
||||
<< ");\n";
|
||||
}
|
||||
|
||||
s << getColourIntialisationCode (component, memberVariableName)
|
||||
<< '\n';
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* gc = dynamic_cast<GroupComponent*> (component))
|
||||
{
|
||||
props.add (new GroupTitleProperty (gc, document));
|
||||
props.add (new GroupJustificationProperty (gc, document));
|
||||
}
|
||||
|
||||
addColourProperties (component, document, props);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class GroupTitleProperty : public ComponentTextProperty <GroupComponent>
|
||||
{
|
||||
public:
|
||||
GroupTitleProperty (GroupComponent* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <GroupComponent> ("text", 200, false, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new GroupTitleChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change group title");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getText();
|
||||
}
|
||||
|
||||
private:
|
||||
class GroupTitleChangeAction : public ComponentUndoableAction <GroupComponent>
|
||||
{
|
||||
public:
|
||||
GroupTitleChangeAction (GroupComponent* const comp, ComponentLayout& l, const String& newName_)
|
||||
: ComponentUndoableAction <GroupComponent> (comp, l),
|
||||
newName (newName_)
|
||||
{
|
||||
oldName = comp->getText();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setText (newName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setText (oldName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class GroupJustificationProperty : public JustificationProperty,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
GroupJustificationProperty (GroupComponent* const group_, JucerDocument& doc)
|
||||
: JustificationProperty ("layout", true),
|
||||
group (group_),
|
||||
document (doc)
|
||||
{
|
||||
document.addChangeListener (this);
|
||||
}
|
||||
|
||||
~GroupJustificationProperty() override
|
||||
{
|
||||
document.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void setJustification (Justification newJustification) override
|
||||
{
|
||||
document.perform (new GroupJustifyChangeAction (group, *document.getComponentLayout(), newJustification),
|
||||
"Change text label position");
|
||||
}
|
||||
|
||||
Justification getJustification() const override
|
||||
{
|
||||
return group->getTextLabelPosition();
|
||||
}
|
||||
|
||||
private:
|
||||
void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
|
||||
|
||||
GroupComponent* const group;
|
||||
JucerDocument& document;
|
||||
|
||||
class GroupJustifyChangeAction : public ComponentUndoableAction <GroupComponent>
|
||||
{
|
||||
public:
|
||||
GroupJustifyChangeAction (GroupComponent* const comp, ComponentLayout& l, Justification newState_)
|
||||
: ComponentUndoableAction <GroupComponent> (comp, l),
|
||||
newState (newState_),
|
||||
oldState (comp->getTextLabelPosition())
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextLabelPosition (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextLabelPosition (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
Justification newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class GroupComponentHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
GroupComponentHandler()
|
||||
: ComponentTypeHandler ("Group Box", "juce::GroupComponent", typeid (GroupComponent), 200, 150)
|
||||
{
|
||||
registerColour (juce::GroupComponent::outlineColourId, "outline", "outlinecol");
|
||||
registerColour (juce::GroupComponent::textColourId, "text", "textcol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new GroupComponent ("new group", "group");
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
GroupComponent* const g = (GroupComponent*) comp;
|
||||
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("title", g->getText());
|
||||
|
||||
GroupComponent defaultComp;
|
||||
|
||||
if (g->getTextLabelPosition().getFlags() != defaultComp.getTextLabelPosition().getFlags())
|
||||
e->setAttribute ("textpos", g->getTextLabelPosition().getFlags());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
GroupComponent* const g = (GroupComponent*) comp;
|
||||
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
g->setText (xml.getStringAttribute ("title", g->getText()));
|
||||
g->setTextLabelPosition (Justification (xml.getIntAttribute ("textpos", g->getTextLabelPosition().getFlags())));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode& code, Component* component) override
|
||||
{
|
||||
GroupComponent* g = dynamic_cast<GroupComponent*> (component);
|
||||
|
||||
return quotedString (component->getName(), false)
|
||||
+ ",\n"
|
||||
+ quotedString (g->getText(), code.shouldUseTransMacro());
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
GroupComponent* const g = dynamic_cast<GroupComponent*> (component);
|
||||
|
||||
String s;
|
||||
|
||||
GroupComponent defaultComp;
|
||||
|
||||
if (g->getTextLabelPosition().getFlags() != defaultComp.getTextLabelPosition().getFlags())
|
||||
{
|
||||
s << memberVariableName << "->setTextLabelPosition ("
|
||||
<< CodeHelpers::justificationToCode (g->getTextLabelPosition())
|
||||
<< ");\n";
|
||||
}
|
||||
|
||||
s << getColourIntialisationCode (component, memberVariableName)
|
||||
<< '\n';
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* gc = dynamic_cast<GroupComponent*> (component))
|
||||
{
|
||||
props.add (new GroupTitleProperty (gc, document));
|
||||
props.add (new GroupJustificationProperty (gc, document));
|
||||
}
|
||||
|
||||
addColourProperties (component, document, props);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class GroupTitleProperty : public ComponentTextProperty <GroupComponent>
|
||||
{
|
||||
public:
|
||||
GroupTitleProperty (GroupComponent* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <GroupComponent> ("text", 200, false, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new GroupTitleChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change group title");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getText();
|
||||
}
|
||||
|
||||
private:
|
||||
class GroupTitleChangeAction : public ComponentUndoableAction <GroupComponent>
|
||||
{
|
||||
public:
|
||||
GroupTitleChangeAction (GroupComponent* const comp, ComponentLayout& l, const String& newName_)
|
||||
: ComponentUndoableAction <GroupComponent> (comp, l),
|
||||
newName (newName_)
|
||||
{
|
||||
oldName = comp->getText();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setText (newName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setText (oldName);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newName, oldName;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class GroupJustificationProperty : public JustificationProperty,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
GroupJustificationProperty (GroupComponent* const group_, JucerDocument& doc)
|
||||
: JustificationProperty ("layout", true),
|
||||
group (group_),
|
||||
document (doc)
|
||||
{
|
||||
document.addChangeListener (this);
|
||||
}
|
||||
|
||||
~GroupJustificationProperty() override
|
||||
{
|
||||
document.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void setJustification (Justification newJustification) override
|
||||
{
|
||||
document.perform (new GroupJustifyChangeAction (group, *document.getComponentLayout(), newJustification),
|
||||
"Change text label position");
|
||||
}
|
||||
|
||||
Justification getJustification() const override
|
||||
{
|
||||
return group->getTextLabelPosition();
|
||||
}
|
||||
|
||||
private:
|
||||
void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
|
||||
|
||||
GroupComponent* const group;
|
||||
JucerDocument& document;
|
||||
|
||||
class GroupJustifyChangeAction : public ComponentUndoableAction <GroupComponent>
|
||||
{
|
||||
public:
|
||||
GroupJustifyChangeAction (GroupComponent* const comp, ComponentLayout& l, Justification newState_)
|
||||
: ComponentUndoableAction <GroupComponent> (comp, l),
|
||||
newState (newState_),
|
||||
oldState (comp->getTextLabelPosition())
|
||||
{
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextLabelPosition (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setTextLabelPosition (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
Justification newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,149 +1,149 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class HyperlinkButtonHandler : public ButtonHandler
|
||||
{
|
||||
public:
|
||||
HyperlinkButtonHandler()
|
||||
: ButtonHandler ("Hyperlink Button", "juce::HyperlinkButton", typeid (HyperlinkButton), 150, 24)
|
||||
{
|
||||
registerColour (juce::HyperlinkButton::textColourId, "text", "textCol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
HyperlinkButton* hb = new HyperlinkButton ("new hyperlink", URL ("http://www.juce.com"));
|
||||
|
||||
setNeedsButtonListener (hb, false);
|
||||
return hb;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* hb = dynamic_cast<HyperlinkButton*> (component))
|
||||
props.add (new HyperlinkURLProperty (hb, document));
|
||||
|
||||
addColourProperties (component, document, props);
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
HyperlinkButton* const hb = (HyperlinkButton*) comp;
|
||||
XmlElement* const e = ButtonHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("url", hb->getURL().toString (false));
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
HyperlinkButton* const hb = (HyperlinkButton*) comp;
|
||||
|
||||
if (! ButtonHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
hb->setURL (URL (xml.getStringAttribute ("url", hb->getURL().toString (false))));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode& code, Component* comp) override
|
||||
{
|
||||
HyperlinkButton* const hb = dynamic_cast<HyperlinkButton*> (comp);
|
||||
|
||||
return quotedString (hb->getButtonText(), code.shouldUseTransMacro())
|
||||
+ ",\nURL ("
|
||||
+ quotedString (hb->getURL().toString (false), false)
|
||||
+ ")";
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
code.constructorCode << getColourIntialisationCode (component, memberVariableName)
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class HyperlinkURLProperty : public ComponentTextProperty <HyperlinkButton>
|
||||
{
|
||||
public:
|
||||
HyperlinkURLProperty (HyperlinkButton* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <HyperlinkButton> ("URL", 512, false, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new HyperlinkURLChangeAction (component, *document.getComponentLayout(), URL::createWithoutParsing (newText)),
|
||||
"Change hyperlink URL");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getURL().toString (false);
|
||||
}
|
||||
|
||||
private:
|
||||
class HyperlinkURLChangeAction : public ComponentUndoableAction <HyperlinkButton>
|
||||
{
|
||||
public:
|
||||
HyperlinkURLChangeAction (HyperlinkButton* const comp, ComponentLayout& l, const URL& newState_)
|
||||
: ComponentUndoableAction <HyperlinkButton> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getURL();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setURL (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setURL (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
URL newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class HyperlinkButtonHandler : public ButtonHandler
|
||||
{
|
||||
public:
|
||||
HyperlinkButtonHandler()
|
||||
: ButtonHandler ("Hyperlink Button", "juce::HyperlinkButton", typeid (HyperlinkButton), 150, 24)
|
||||
{
|
||||
registerColour (juce::HyperlinkButton::textColourId, "text", "textCol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
HyperlinkButton* hb = new HyperlinkButton ("new hyperlink", URL ("http://www.juce.com"));
|
||||
|
||||
setNeedsButtonListener (hb, false);
|
||||
return hb;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* hb = dynamic_cast<HyperlinkButton*> (component))
|
||||
props.add (new HyperlinkURLProperty (hb, document));
|
||||
|
||||
addColourProperties (component, document, props);
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
HyperlinkButton* const hb = (HyperlinkButton*) comp;
|
||||
XmlElement* const e = ButtonHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("url", hb->getURL().toString (false));
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
HyperlinkButton* const hb = (HyperlinkButton*) comp;
|
||||
|
||||
if (! ButtonHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
hb->setURL (URL (xml.getStringAttribute ("url", hb->getURL().toString (false))));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode& code, Component* comp) override
|
||||
{
|
||||
HyperlinkButton* const hb = dynamic_cast<HyperlinkButton*> (comp);
|
||||
|
||||
return quotedString (hb->getButtonText(), code.shouldUseTransMacro())
|
||||
+ ",\njuce::URL ("
|
||||
+ quotedString (hb->getURL().toString (false), false)
|
||||
+ ")";
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
code.constructorCode << getColourIntialisationCode (component, memberVariableName)
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class HyperlinkURLProperty : public ComponentTextProperty <HyperlinkButton>
|
||||
{
|
||||
public:
|
||||
HyperlinkURLProperty (HyperlinkButton* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <HyperlinkButton> ("URL", 512, false, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new HyperlinkURLChangeAction (component, *document.getComponentLayout(), URL::createWithoutParsing (newText)),
|
||||
"Change hyperlink URL");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getURL().toString (false);
|
||||
}
|
||||
|
||||
private:
|
||||
class HyperlinkURLChangeAction : public ComponentUndoableAction <HyperlinkButton>
|
||||
{
|
||||
public:
|
||||
HyperlinkURLChangeAction (HyperlinkButton* const comp, ComponentLayout& l, const URL& newState_)
|
||||
: ComponentUndoableAction <HyperlinkButton> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getURL();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setURL (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setURL (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
URL newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,273 +1,273 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../UI/jucer_TestComponent.h"
|
||||
#include "../Properties/jucer_FilePropertyComponent.h"
|
||||
#include "../Properties/jucer_ComponentTextProperty.h"
|
||||
#include "jucer_ComponentUndoableAction.h"
|
||||
#include "../../Project/UI/jucer_ProjectContentComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class JucerComponentHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
JucerComponentHandler()
|
||||
: ComponentTypeHandler ("Projucer Component", "xxx",
|
||||
typeid (TestComponent), 300, 200)
|
||||
{}
|
||||
|
||||
Component* createNewComponent (JucerDocument* doc) override
|
||||
{
|
||||
return new TestComponent (doc, nullptr, false);
|
||||
}
|
||||
|
||||
String getXmlTagName() const noexcept override { return "JUCERCOMP"; }
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
TestComponent* const tc = dynamic_cast<TestComponent*> (comp);
|
||||
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("sourceFile", tc->getFilename());
|
||||
e->setAttribute ("constructorParams", tc->getConstructorParams());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
auto tc = dynamic_cast<TestComponent*> (comp);
|
||||
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
tc->setFilename (xml.getStringAttribute ("sourceFile", tc->getFilename()));
|
||||
tc->setConstructorParams (xml.getStringAttribute ("constructorParams"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getClassName (Component* comp) const override
|
||||
{
|
||||
auto tc = dynamic_cast<TestComponent*> (comp);
|
||||
|
||||
String jucerCompClassName;
|
||||
|
||||
if (tc->getDocument() != nullptr)
|
||||
jucerCompClassName = tc->getDocument()->getClassName();
|
||||
|
||||
if (jucerCompClassName.isEmpty())
|
||||
jucerCompClassName = "juce::Component";
|
||||
|
||||
return jucerCompClassName;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto tc = dynamic_cast<TestComponent*> (component))
|
||||
{
|
||||
props.add (new JucerCompFileProperty (tc, document));
|
||||
props.add (new ConstructorParamsProperty (tc, document));
|
||||
props.add (new JucerCompOpenDocProperty (tc));
|
||||
}
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* component) override
|
||||
{
|
||||
return dynamic_cast<TestComponent*> (component)->getConstructorParams().trim();
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
if (auto tc = dynamic_cast<TestComponent*> (component))
|
||||
code.includeFilesH.add (tc->findFile().withFileExtension (".h"));
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JucerCompFileChangeAction : public ComponentUndoableAction <TestComponent>
|
||||
{
|
||||
public:
|
||||
JucerCompFileChangeAction (TestComponent* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <TestComponent> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getFilename();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setFilename (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setFilename (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
|
||||
static void setJucerComponentFile (JucerDocument& document, TestComponent* comp, const String& newFilename)
|
||||
{
|
||||
jassert (comp != nullptr);
|
||||
|
||||
if (comp != nullptr)
|
||||
document.perform (new JucerCompFileChangeAction (comp, *document.getComponentLayout(), newFilename),
|
||||
"Change Projucer component file");
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class JucerCompFileProperty : public FilePropertyComponent,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
JucerCompFileProperty (TestComponent* const comp, JucerDocument& doc)
|
||||
: FilePropertyComponent ("Jucer file", false, true),
|
||||
component (comp),
|
||||
document (doc)
|
||||
{
|
||||
document.addChangeListener (this);
|
||||
}
|
||||
|
||||
~JucerCompFileProperty() override
|
||||
{
|
||||
document.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void setFile (const File& newFile) override
|
||||
{
|
||||
setJucerComponentFile (document, component,
|
||||
newFile.getRelativePathFrom (document.getCppFile().getParentDirectory())
|
||||
.replaceCharacter ('\\', '/'));
|
||||
}
|
||||
|
||||
File getFile() const override
|
||||
{
|
||||
return component->findFile();
|
||||
}
|
||||
|
||||
private:
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
TestComponent* const component;
|
||||
JucerDocument& document;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JucerCompOpenDocProperty : public ButtonPropertyComponent
|
||||
{
|
||||
JucerCompOpenDocProperty (TestComponent* const c)
|
||||
: ButtonPropertyComponent ("edit", false),
|
||||
component (c)
|
||||
{
|
||||
}
|
||||
|
||||
void buttonClicked()
|
||||
{
|
||||
if (ProjectContentComponent* const pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->showEditorForFile (component->findFile(), true);
|
||||
}
|
||||
|
||||
String getButtonText() const
|
||||
{
|
||||
return "Open file for editing";
|
||||
}
|
||||
|
||||
TestComponent* const component;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ConstructorParamsProperty : public ComponentTextProperty <TestComponent>
|
||||
{
|
||||
ConstructorParamsProperty (TestComponent* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <TestComponent> ("constructor params", 512, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ConstructorParamChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change Viewport content constructor params");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getConstructorParams();
|
||||
}
|
||||
|
||||
private:
|
||||
struct ConstructorParamChangeAction : public ComponentUndoableAction <TestComponent>
|
||||
{
|
||||
ConstructorParamChangeAction (TestComponent* const comp, ComponentLayout& l, const String& newValue_)
|
||||
: ComponentUndoableAction <TestComponent> (comp, l),
|
||||
newValue (newValue_)
|
||||
{
|
||||
oldValue = comp->getConstructorParams();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setConstructorParams (newValue);
|
||||
changed();
|
||||
layout.getDocument()->refreshAllPropertyComps();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setConstructorParams (oldValue);
|
||||
changed();
|
||||
layout.getDocument()->refreshAllPropertyComps();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newValue, oldValue;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../UI/jucer_TestComponent.h"
|
||||
#include "../Properties/jucer_FilePropertyComponent.h"
|
||||
#include "../Properties/jucer_ComponentTextProperty.h"
|
||||
#include "jucer_ComponentUndoableAction.h"
|
||||
#include "../../Project/UI/jucer_ProjectContentComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class JucerComponentHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
JucerComponentHandler()
|
||||
: ComponentTypeHandler ("Projucer Component", "xxx",
|
||||
typeid (TestComponent), 300, 200)
|
||||
{}
|
||||
|
||||
Component* createNewComponent (JucerDocument* doc) override
|
||||
{
|
||||
return new TestComponent (doc, nullptr, false);
|
||||
}
|
||||
|
||||
String getXmlTagName() const noexcept override { return "JUCERCOMP"; }
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
TestComponent* const tc = dynamic_cast<TestComponent*> (comp);
|
||||
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("sourceFile", tc->getFilename());
|
||||
e->setAttribute ("constructorParams", tc->getConstructorParams());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
auto tc = dynamic_cast<TestComponent*> (comp);
|
||||
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
tc->setFilename (xml.getStringAttribute ("sourceFile", tc->getFilename()));
|
||||
tc->setConstructorParams (xml.getStringAttribute ("constructorParams"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getClassName (Component* comp) const override
|
||||
{
|
||||
auto tc = dynamic_cast<TestComponent*> (comp);
|
||||
|
||||
String jucerCompClassName;
|
||||
|
||||
if (tc->getDocument() != nullptr)
|
||||
jucerCompClassName = tc->getDocument()->getClassName();
|
||||
|
||||
if (jucerCompClassName.isEmpty())
|
||||
jucerCompClassName = "juce::Component";
|
||||
|
||||
return jucerCompClassName;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto tc = dynamic_cast<TestComponent*> (component))
|
||||
{
|
||||
props.add (new JucerCompFileProperty (tc, document));
|
||||
props.add (new ConstructorParamsProperty (tc, document));
|
||||
props.add (new JucerCompOpenDocProperty (tc));
|
||||
}
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* component) override
|
||||
{
|
||||
return dynamic_cast<TestComponent*> (component)->getConstructorParams().trim();
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
if (auto tc = dynamic_cast<TestComponent*> (component))
|
||||
code.includeFilesH.add (tc->findFile().withFileExtension (".h"));
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JucerCompFileChangeAction : public ComponentUndoableAction <TestComponent>
|
||||
{
|
||||
public:
|
||||
JucerCompFileChangeAction (TestComponent* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <TestComponent> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getFilename();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setFilename (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setFilename (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
|
||||
static void setJucerComponentFile (JucerDocument& document, TestComponent* comp, const String& newFilename)
|
||||
{
|
||||
jassert (comp != nullptr);
|
||||
|
||||
if (comp != nullptr)
|
||||
document.perform (new JucerCompFileChangeAction (comp, *document.getComponentLayout(), newFilename),
|
||||
"Change Projucer component file");
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class JucerCompFileProperty : public FilePropertyComponent,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
JucerCompFileProperty (TestComponent* const comp, JucerDocument& doc)
|
||||
: FilePropertyComponent ("Jucer file", false, true),
|
||||
component (comp),
|
||||
document (doc)
|
||||
{
|
||||
document.addChangeListener (this);
|
||||
}
|
||||
|
||||
~JucerCompFileProperty() override
|
||||
{
|
||||
document.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void setFile (const File& newFile) override
|
||||
{
|
||||
setJucerComponentFile (document, component,
|
||||
newFile.getRelativePathFrom (document.getCppFile().getParentDirectory())
|
||||
.replaceCharacter ('\\', '/'));
|
||||
}
|
||||
|
||||
File getFile() const override
|
||||
{
|
||||
return component->findFile();
|
||||
}
|
||||
|
||||
private:
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
TestComponent* const component;
|
||||
JucerDocument& document;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JucerCompOpenDocProperty : public ButtonPropertyComponent
|
||||
{
|
||||
JucerCompOpenDocProperty (TestComponent* const c)
|
||||
: ButtonPropertyComponent ("edit", false),
|
||||
component (c)
|
||||
{
|
||||
}
|
||||
|
||||
void buttonClicked()
|
||||
{
|
||||
if (ProjectContentComponent* const pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->showEditorForFile (component->findFile(), true);
|
||||
}
|
||||
|
||||
String getButtonText() const
|
||||
{
|
||||
return "Open file for editing";
|
||||
}
|
||||
|
||||
TestComponent* const component;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ConstructorParamsProperty : public ComponentTextProperty <TestComponent>
|
||||
{
|
||||
ConstructorParamsProperty (TestComponent* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <TestComponent> ("constructor params", 512, false, comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new ConstructorParamChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change Viewport content constructor params");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getConstructorParams();
|
||||
}
|
||||
|
||||
private:
|
||||
struct ConstructorParamChangeAction : public ComponentUndoableAction <TestComponent>
|
||||
{
|
||||
ConstructorParamChangeAction (TestComponent* const comp, ComponentLayout& l, const String& newValue_)
|
||||
: ComponentUndoableAction <TestComponent> (comp, l),
|
||||
newValue (newValue_)
|
||||
{
|
||||
oldValue = comp->getConstructorParams();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setConstructorParams (newValue);
|
||||
changed();
|
||||
layout.getDocument()->refreshAllPropertyComps();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setConstructorParams (oldValue);
|
||||
changed();
|
||||
layout.getDocument()->refreshAllPropertyComps();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newValue, oldValue;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,79 +1,79 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class TextButtonHandler : public ButtonHandler
|
||||
{
|
||||
public:
|
||||
TextButtonHandler()
|
||||
: ButtonHandler ("Text Button", "juce::TextButton", typeid (TextButton), 150, 24)
|
||||
{
|
||||
registerColour (juce::TextButton::buttonColourId, "background (normal)", "bgColOff");
|
||||
registerColour (juce::TextButton::buttonOnColourId, "background (on)", "bgColOn");
|
||||
registerColour (juce::TextButton::textColourOffId, "text colour (normal)", "textCol");
|
||||
registerColour (juce::TextButton::textColourOnId, "text colour (on)", "textColOn");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new TextButton ("new button", String());
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
addColourProperties (component, document, props);
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
return ButtonHandler::createXmlFor (comp, layout);
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
return ButtonHandler::restoreFromXml (xml, comp, layout);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
String s;
|
||||
|
||||
s << getColourIntialisationCode (component, memberVariableName)
|
||||
<< '\n';
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class TextButtonHandler : public ButtonHandler
|
||||
{
|
||||
public:
|
||||
TextButtonHandler()
|
||||
: ButtonHandler ("Text Button", "juce::TextButton", typeid (TextButton), 150, 24)
|
||||
{
|
||||
registerColour (juce::TextButton::buttonColourId, "background (normal)", "bgColOff");
|
||||
registerColour (juce::TextButton::buttonOnColourId, "background (on)", "bgColOn");
|
||||
registerColour (juce::TextButton::textColourOffId, "text colour (normal)", "textCol");
|
||||
registerColour (juce::TextButton::textColourOnId, "text colour (on)", "textColOn");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new TextButton ("new button", String());
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
addColourProperties (component, document, props);
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
return ButtonHandler::createXmlFor (comp, layout);
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
return ButtonHandler::restoreFromXml (xml, comp, layout);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
String s;
|
||||
|
||||
s << getColourIntialisationCode (component, memberVariableName)
|
||||
<< '\n';
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,433 +1,433 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
TextEditorHandler()
|
||||
: ComponentTypeHandler ("Text Editor", "juce::TextEditor", typeid (TextEditor), 150, 24)
|
||||
{
|
||||
registerColour (juce::TextEditor::textColourId, "text", "textcol");
|
||||
registerColour (juce::TextEditor::backgroundColourId, "background", "bkgcol");
|
||||
registerColour (juce::TextEditor::highlightColourId, "highlight", "hilitecol");
|
||||
registerColour (juce::TextEditor::outlineColourId, "outline", "outlinecol");
|
||||
registerColour (juce::TextEditor::shadowColourId, "shadow", "shadowcol");
|
||||
registerColour (juce::CaretComponent::caretColourId, "caret", "caretcol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new TextEditor ("new text editor");
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
TextEditor* te = (TextEditor*) comp;
|
||||
|
||||
e->setAttribute ("initialText", comp->getProperties() ["initialText"].toString());
|
||||
|
||||
e->setAttribute ("multiline", te->isMultiLine());
|
||||
e->setAttribute ("retKeyStartsLine", te->getReturnKeyStartsNewLine());
|
||||
e->setAttribute ("readonly", te->isReadOnly());
|
||||
e->setAttribute ("scrollbars", te->areScrollbarsShown());
|
||||
e->setAttribute ("caret", te->isCaretVisible());
|
||||
e->setAttribute ("popupmenu", te->isPopupMenuEnabled());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
TextEditor* te = (TextEditor*) comp;
|
||||
TextEditor defaultEditor;
|
||||
|
||||
te->setMultiLine (xml.getBoolAttribute ("multiline", defaultEditor.isMultiLine()));
|
||||
te->setReturnKeyStartsNewLine (xml.getBoolAttribute ("retKeyStartsLine", defaultEditor.getReturnKeyStartsNewLine()));
|
||||
te->setReadOnly (xml.getBoolAttribute ("readonly", defaultEditor.isReadOnly()));
|
||||
te->setScrollbarsShown (xml.getBoolAttribute ("scrollbars", defaultEditor.areScrollbarsShown()));
|
||||
te->setCaretVisible (xml.getBoolAttribute ("caret", defaultEditor.isCaretVisible()));
|
||||
te->setPopupMenuEnabled (xml.getBoolAttribute ("popupmenu", defaultEditor.isPopupMenuEnabled()));
|
||||
|
||||
const String initialText (xml.getStringAttribute ("initialText"));
|
||||
te->setText (initialText, false);
|
||||
te->getProperties().set ("initialText", initialText);
|
||||
return true;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* t = dynamic_cast<TextEditor*> (component))
|
||||
{
|
||||
props.add (new TextEditorInitialTextProperty (t, document));
|
||||
props.add (new TextEditorMultiLineProperty (t, document));
|
||||
props.add (new TextEditorReadOnlyProperty (t, document));
|
||||
props.add (new TextEditorScrollbarsProperty (t, document));
|
||||
props.add (new TextEditorCaretProperty (t, document));
|
||||
props.add (new TextEditorPopupMenuProperty (t, document));
|
||||
|
||||
addColourProperties (t, document, props);
|
||||
}
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* component) override
|
||||
{
|
||||
return quotedString (component->getName(), false);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
if (auto* te = dynamic_cast<TextEditor*> (component))
|
||||
{
|
||||
String s;
|
||||
s << memberVariableName << "->setMultiLine (" << CodeHelpers::boolLiteral (te->isMultiLine()) << ");\n"
|
||||
<< memberVariableName << "->setReturnKeyStartsNewLine (" << CodeHelpers::boolLiteral (te->getReturnKeyStartsNewLine()) << ");\n"
|
||||
<< memberVariableName << "->setReadOnly (" << CodeHelpers::boolLiteral (te->isReadOnly()) << ");\n"
|
||||
<< memberVariableName << "->setScrollbarsShown (" << CodeHelpers::boolLiteral (te->areScrollbarsShown()) << ");\n"
|
||||
<< memberVariableName << "->setCaretVisible (" << CodeHelpers::boolLiteral (te->isCaretVisible()) << ");\n"
|
||||
<< memberVariableName << "->setPopupMenuEnabled (" << CodeHelpers::boolLiteral (te->isPopupMenuEnabled()) << ");\n"
|
||||
<< getColourIntialisationCode (component, memberVariableName)
|
||||
<< memberVariableName << "->setText (" << quotedString (te->getProperties() ["initialText"].toString(), code.shouldUseTransMacro()) << ");\n\n";
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class TextEditorMultiLineProperty : public ComponentChoiceProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorMultiLineProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentChoiceProperty <TextEditor> ("mode", comp, doc)
|
||||
{
|
||||
choices.add ("single line");
|
||||
choices.add ("multi-line, return key starts new line");
|
||||
choices.add ("multi-line, return key disabled");
|
||||
}
|
||||
|
||||
void setIndex (int newIndex)
|
||||
{
|
||||
document.perform (new TextEditorMultilineChangeAction (component, *document.getComponentLayout(), newIndex),
|
||||
"Change TextEditor multiline mode");
|
||||
}
|
||||
|
||||
int getIndex() const
|
||||
{
|
||||
return component->isMultiLine() ? (component->getReturnKeyStartsNewLine() ? 1 : 2) : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
class TextEditorMultilineChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorMultilineChangeAction (TextEditor* const comp, ComponentLayout& l, const int newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isMultiLine() ? (comp->getReturnKeyStartsNewLine() ? 1 : 2) : 0;
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setMultiLine (newState > 0);
|
||||
getComponent()->setReturnKeyStartsNewLine (newState == 1);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setMultiLine (oldState > 0);
|
||||
getComponent()->setReturnKeyStartsNewLine (oldState == 1);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
int newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorReadOnlyProperty : public ComponentBooleanProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorReadOnlyProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TextEditor> ("editable", "Editable", "Editable", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TextEditorReadonlyChangeAction (component, *document.getComponentLayout(), ! newState),
|
||||
"Change TextEditor read-only mode");
|
||||
}
|
||||
|
||||
bool getState() const { return ! component->isReadOnly(); }
|
||||
|
||||
private:
|
||||
class TextEditorReadonlyChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorReadonlyChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isReadOnly();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setReadOnly (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setReadOnly (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorScrollbarsProperty : public ComponentBooleanProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorScrollbarsProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TextEditor> ("scrollbars", "Scrollbars enabled", "Scrollbars enabled", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TextEditorScrollbarChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change TextEditor scrollbars");
|
||||
}
|
||||
|
||||
bool getState() const { return component->areScrollbarsShown(); }
|
||||
|
||||
private:
|
||||
class TextEditorScrollbarChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorScrollbarChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->areScrollbarsShown();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setScrollbarsShown (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setScrollbarsShown (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorCaretProperty : public ComponentBooleanProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorCaretProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TextEditor> ("caret", "Caret visible", "Caret visible", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TextEditorCaretChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change TextEditor caret");
|
||||
}
|
||||
|
||||
bool getState() const { return component->isCaretVisible(); }
|
||||
|
||||
private:
|
||||
class TextEditorCaretChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorCaretChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isCaretVisible();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setCaretVisible (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setCaretVisible (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorPopupMenuProperty : public ComponentBooleanProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorPopupMenuProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TextEditor> ("popup menu", "Popup menu enabled", "Popup menu enabled", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TextEditorPopupMenuChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change TextEditor popup menu");
|
||||
}
|
||||
|
||||
bool getState() const { return component->isPopupMenuEnabled(); }
|
||||
|
||||
private:
|
||||
class TextEditorPopupMenuChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorPopupMenuChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isPopupMenuEnabled();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setPopupMenuEnabled (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setPopupMenuEnabled (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorInitialTextProperty : public ComponentTextProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorInitialTextProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <TextEditor> ("initial text", 10000, true, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new TextEditorInitialTextChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change TextEditor initial text");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getProperties() ["initialText"];
|
||||
}
|
||||
|
||||
private:
|
||||
class TextEditorInitialTextChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorInitialTextChangeAction (TextEditor* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getProperties() ["initialText"];
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setText (newState, false);
|
||||
getComponent()->getProperties().set ("initialText", newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setText (oldState, false);
|
||||
getComponent()->getProperties().set ("initialText", oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
TextEditorHandler()
|
||||
: ComponentTypeHandler ("Text Editor", "juce::TextEditor", typeid (TextEditor), 150, 24)
|
||||
{
|
||||
registerColour (juce::TextEditor::textColourId, "text", "textcol");
|
||||
registerColour (juce::TextEditor::backgroundColourId, "background", "bkgcol");
|
||||
registerColour (juce::TextEditor::highlightColourId, "highlight", "hilitecol");
|
||||
registerColour (juce::TextEditor::outlineColourId, "outline", "outlinecol");
|
||||
registerColour (juce::TextEditor::shadowColourId, "shadow", "shadowcol");
|
||||
registerColour (juce::CaretComponent::caretColourId, "caret", "caretcol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new TextEditor ("new text editor");
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
TextEditor* te = (TextEditor*) comp;
|
||||
|
||||
e->setAttribute ("initialText", comp->getProperties() ["initialText"].toString());
|
||||
|
||||
e->setAttribute ("multiline", te->isMultiLine());
|
||||
e->setAttribute ("retKeyStartsLine", te->getReturnKeyStartsNewLine());
|
||||
e->setAttribute ("readonly", te->isReadOnly());
|
||||
e->setAttribute ("scrollbars", te->areScrollbarsShown());
|
||||
e->setAttribute ("caret", te->isCaretVisible());
|
||||
e->setAttribute ("popupmenu", te->isPopupMenuEnabled());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
TextEditor* te = (TextEditor*) comp;
|
||||
TextEditor defaultEditor;
|
||||
|
||||
te->setMultiLine (xml.getBoolAttribute ("multiline", defaultEditor.isMultiLine()));
|
||||
te->setReturnKeyStartsNewLine (xml.getBoolAttribute ("retKeyStartsLine", defaultEditor.getReturnKeyStartsNewLine()));
|
||||
te->setReadOnly (xml.getBoolAttribute ("readonly", defaultEditor.isReadOnly()));
|
||||
te->setScrollbarsShown (xml.getBoolAttribute ("scrollbars", defaultEditor.areScrollbarsShown()));
|
||||
te->setCaretVisible (xml.getBoolAttribute ("caret", defaultEditor.isCaretVisible()));
|
||||
te->setPopupMenuEnabled (xml.getBoolAttribute ("popupmenu", defaultEditor.isPopupMenuEnabled()));
|
||||
|
||||
const String initialText (xml.getStringAttribute ("initialText"));
|
||||
te->setText (initialText, false);
|
||||
te->getProperties().set ("initialText", initialText);
|
||||
return true;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* t = dynamic_cast<TextEditor*> (component))
|
||||
{
|
||||
props.add (new TextEditorInitialTextProperty (t, document));
|
||||
props.add (new TextEditorMultiLineProperty (t, document));
|
||||
props.add (new TextEditorReadOnlyProperty (t, document));
|
||||
props.add (new TextEditorScrollbarsProperty (t, document));
|
||||
props.add (new TextEditorCaretProperty (t, document));
|
||||
props.add (new TextEditorPopupMenuProperty (t, document));
|
||||
|
||||
addColourProperties (t, document, props);
|
||||
}
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* component) override
|
||||
{
|
||||
return quotedString (component->getName(), false);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
if (auto* te = dynamic_cast<TextEditor*> (component))
|
||||
{
|
||||
String s;
|
||||
s << memberVariableName << "->setMultiLine (" << CodeHelpers::boolLiteral (te->isMultiLine()) << ");\n"
|
||||
<< memberVariableName << "->setReturnKeyStartsNewLine (" << CodeHelpers::boolLiteral (te->getReturnKeyStartsNewLine()) << ");\n"
|
||||
<< memberVariableName << "->setReadOnly (" << CodeHelpers::boolLiteral (te->isReadOnly()) << ");\n"
|
||||
<< memberVariableName << "->setScrollbarsShown (" << CodeHelpers::boolLiteral (te->areScrollbarsShown()) << ");\n"
|
||||
<< memberVariableName << "->setCaretVisible (" << CodeHelpers::boolLiteral (te->isCaretVisible()) << ");\n"
|
||||
<< memberVariableName << "->setPopupMenuEnabled (" << CodeHelpers::boolLiteral (te->isPopupMenuEnabled()) << ");\n"
|
||||
<< getColourIntialisationCode (component, memberVariableName)
|
||||
<< memberVariableName << "->setText (" << quotedString (te->getProperties() ["initialText"].toString(), code.shouldUseTransMacro()) << ");\n\n";
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class TextEditorMultiLineProperty : public ComponentChoiceProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorMultiLineProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentChoiceProperty <TextEditor> ("mode", comp, doc)
|
||||
{
|
||||
choices.add ("single line");
|
||||
choices.add ("multi-line, return key starts new line");
|
||||
choices.add ("multi-line, return key disabled");
|
||||
}
|
||||
|
||||
void setIndex (int newIndex)
|
||||
{
|
||||
document.perform (new TextEditorMultilineChangeAction (component, *document.getComponentLayout(), newIndex),
|
||||
"Change TextEditor multiline mode");
|
||||
}
|
||||
|
||||
int getIndex() const
|
||||
{
|
||||
return component->isMultiLine() ? (component->getReturnKeyStartsNewLine() ? 1 : 2) : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
class TextEditorMultilineChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorMultilineChangeAction (TextEditor* const comp, ComponentLayout& l, const int newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isMultiLine() ? (comp->getReturnKeyStartsNewLine() ? 1 : 2) : 0;
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setMultiLine (newState > 0);
|
||||
getComponent()->setReturnKeyStartsNewLine (newState == 1);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setMultiLine (oldState > 0);
|
||||
getComponent()->setReturnKeyStartsNewLine (oldState == 1);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
int newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorReadOnlyProperty : public ComponentBooleanProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorReadOnlyProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TextEditor> ("editable", "Editable", "Editable", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TextEditorReadonlyChangeAction (component, *document.getComponentLayout(), ! newState),
|
||||
"Change TextEditor read-only mode");
|
||||
}
|
||||
|
||||
bool getState() const { return ! component->isReadOnly(); }
|
||||
|
||||
private:
|
||||
class TextEditorReadonlyChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorReadonlyChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isReadOnly();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setReadOnly (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setReadOnly (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorScrollbarsProperty : public ComponentBooleanProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorScrollbarsProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TextEditor> ("scrollbars", "Scrollbars enabled", "Scrollbars enabled", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TextEditorScrollbarChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change TextEditor scrollbars");
|
||||
}
|
||||
|
||||
bool getState() const { return component->areScrollbarsShown(); }
|
||||
|
||||
private:
|
||||
class TextEditorScrollbarChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorScrollbarChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->areScrollbarsShown();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setScrollbarsShown (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setScrollbarsShown (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorCaretProperty : public ComponentBooleanProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorCaretProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TextEditor> ("caret", "Caret visible", "Caret visible", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TextEditorCaretChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change TextEditor caret");
|
||||
}
|
||||
|
||||
bool getState() const { return component->isCaretVisible(); }
|
||||
|
||||
private:
|
||||
class TextEditorCaretChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorCaretChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isCaretVisible();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setCaretVisible (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setCaretVisible (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorPopupMenuProperty : public ComponentBooleanProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorPopupMenuProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TextEditor> ("popup menu", "Popup menu enabled", "Popup menu enabled", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TextEditorPopupMenuChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change TextEditor popup menu");
|
||||
}
|
||||
|
||||
bool getState() const { return component->isPopupMenuEnabled(); }
|
||||
|
||||
private:
|
||||
class TextEditorPopupMenuChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorPopupMenuChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isPopupMenuEnabled();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setPopupMenuEnabled (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setPopupMenuEnabled (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TextEditorInitialTextProperty : public ComponentTextProperty <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorInitialTextProperty (TextEditor* comp, JucerDocument& doc)
|
||||
: ComponentTextProperty <TextEditor> ("initial text", 10000, true, comp, doc)
|
||||
{}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
document.perform (new TextEditorInitialTextChangeAction (component, *document.getComponentLayout(), newText),
|
||||
"Change TextEditor initial text");
|
||||
}
|
||||
|
||||
String getText() const override
|
||||
{
|
||||
return component->getProperties() ["initialText"];
|
||||
}
|
||||
|
||||
private:
|
||||
class TextEditorInitialTextChangeAction : public ComponentUndoableAction <TextEditor>
|
||||
{
|
||||
public:
|
||||
TextEditorInitialTextChangeAction (TextEditor* const comp, ComponentLayout& l, const String& newState_)
|
||||
: ComponentUndoableAction <TextEditor> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getProperties() ["initialText"];
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setText (newState, false);
|
||||
getComponent()->getProperties().set ("initialText", newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setText (oldState, false);
|
||||
getComponent()->getProperties().set ("initialText", oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
String newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,146 +1,146 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ToggleButtonHandler : public ButtonHandler
|
||||
{
|
||||
public:
|
||||
ToggleButtonHandler()
|
||||
: ButtonHandler ("Toggle Button", "juce::ToggleButton", typeid (ToggleButton), 150, 24)
|
||||
{
|
||||
registerColour (juce::ToggleButton::textColourId, "text colour", "txtcol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new ToggleButton ("new toggle button");
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* tb = dynamic_cast<ToggleButton*> (component))
|
||||
props.add (new ToggleButtonStateProperty (tb, document));
|
||||
|
||||
addColourProperties (component, document, props);
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
ToggleButton* tb = (ToggleButton*) comp;
|
||||
|
||||
XmlElement* e = ButtonHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("state", tb->getToggleState());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
ToggleButton* const tb = (ToggleButton*) comp;
|
||||
|
||||
if (! ButtonHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
tb->setToggleState (xml.getBoolAttribute ("state", false), dontSendNotification);
|
||||
return true;
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
ToggleButton* const tb = dynamic_cast<ToggleButton*> (component);
|
||||
|
||||
String s;
|
||||
|
||||
if (tb->getToggleState())
|
||||
s << memberVariableName << "->setToggleState (true, juce::dontSendNotification);\n";
|
||||
|
||||
s << getColourIntialisationCode (component, memberVariableName)
|
||||
<< '\n';
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
|
||||
private:
|
||||
class ToggleButtonStateProperty : public ComponentBooleanProperty <ToggleButton>
|
||||
{
|
||||
public:
|
||||
ToggleButtonStateProperty (ToggleButton* button_, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <ToggleButton> ("initial state", "on", "off", button_, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new ToggleStateChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change ToggleButton state");
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return component->getToggleState();
|
||||
}
|
||||
|
||||
private:
|
||||
class ToggleStateChangeAction : public ComponentUndoableAction <ToggleButton>
|
||||
{
|
||||
public:
|
||||
ToggleStateChangeAction (ToggleButton* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <ToggleButton> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getToggleState();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setToggleState (newState, dontSendNotification);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setToggleState (oldState, dontSendNotification);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ToggleButtonHandler : public ButtonHandler
|
||||
{
|
||||
public:
|
||||
ToggleButtonHandler()
|
||||
: ButtonHandler ("Toggle Button", "juce::ToggleButton", typeid (ToggleButton), 150, 24)
|
||||
{
|
||||
registerColour (juce::ToggleButton::textColourId, "text colour", "txtcol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new ToggleButton ("new toggle button");
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
if (auto* tb = dynamic_cast<ToggleButton*> (component))
|
||||
props.add (new ToggleButtonStateProperty (tb, document));
|
||||
|
||||
addColourProperties (component, document, props);
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
ToggleButton* tb = (ToggleButton*) comp;
|
||||
|
||||
XmlElement* e = ButtonHandler::createXmlFor (comp, layout);
|
||||
e->setAttribute ("state", tb->getToggleState());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
ToggleButton* const tb = (ToggleButton*) comp;
|
||||
|
||||
if (! ButtonHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
tb->setToggleState (xml.getBoolAttribute ("state", false), dontSendNotification);
|
||||
return true;
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
ToggleButton* const tb = dynamic_cast<ToggleButton*> (component);
|
||||
|
||||
String s;
|
||||
|
||||
if (tb->getToggleState())
|
||||
s << memberVariableName << "->setToggleState (true, juce::dontSendNotification);\n";
|
||||
|
||||
s << getColourIntialisationCode (component, memberVariableName)
|
||||
<< '\n';
|
||||
|
||||
code.constructorCode += s;
|
||||
}
|
||||
|
||||
private:
|
||||
class ToggleButtonStateProperty : public ComponentBooleanProperty <ToggleButton>
|
||||
{
|
||||
public:
|
||||
ToggleButtonStateProperty (ToggleButton* button_, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <ToggleButton> ("initial state", "on", "off", button_, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new ToggleStateChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change ToggleButton state");
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return component->getToggleState();
|
||||
}
|
||||
|
||||
private:
|
||||
class ToggleStateChangeAction : public ComponentUndoableAction <ToggleButton>
|
||||
{
|
||||
public:
|
||||
ToggleStateChangeAction (ToggleButton* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <ToggleButton> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->getToggleState();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setToggleState (newState, dontSendNotification);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setToggleState (oldState, dontSendNotification);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,265 +1,265 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class TreeViewHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
TreeViewHandler()
|
||||
: ComponentTypeHandler ("TreeView", "juce::TreeView", typeid (DemoTreeView), 150, 150)
|
||||
{
|
||||
registerColour (juce::TreeView::backgroundColourId, "background", "backgroundColour");
|
||||
registerColour (juce::TreeView::linesColourId, "lines", "linecol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new DemoTreeView();
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
TreeView* const t = dynamic_cast<TreeView*> (comp);
|
||||
XmlElement* const e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
|
||||
e->setAttribute ("rootVisible", t->isRootItemVisible());
|
||||
e->setAttribute ("openByDefault", t->areItemsOpenByDefault());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
TreeView defaultTreeView;
|
||||
TreeView* const t = dynamic_cast<TreeView*> (comp);
|
||||
|
||||
t->setRootItemVisible (xml.getBoolAttribute ("rootVisible", defaultTreeView.isRootItemVisible()));
|
||||
t->setDefaultOpenness (xml.getBoolAttribute ("openByDefault", defaultTreeView.areItemsOpenByDefault()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
auto* t = dynamic_cast<TreeView*> (component);
|
||||
|
||||
props.add (new TreeViewRootItemProperty (t, document));
|
||||
props.add (new TreeViewRootOpennessProperty (t, document));
|
||||
|
||||
addColourProperties (t, document, props);
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* comp) override
|
||||
{
|
||||
return quotedString (comp->getName(), false);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
TreeView defaultTreeView;
|
||||
TreeView* const t = dynamic_cast<TreeView*> (component);
|
||||
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
if (defaultTreeView.isRootItemVisible() != t->isRootItemVisible())
|
||||
{
|
||||
code.constructorCode
|
||||
<< memberVariableName << "->setRootItemVisible ("
|
||||
<< CodeHelpers::boolLiteral (t->isRootItemVisible()) << ");\n";
|
||||
}
|
||||
|
||||
if (defaultTreeView.areItemsOpenByDefault() != t->areItemsOpenByDefault())
|
||||
{
|
||||
code.constructorCode
|
||||
<< memberVariableName << "->setDefaultOpenness ("
|
||||
<< CodeHelpers::boolLiteral (t->areItemsOpenByDefault()) << ");\n";
|
||||
}
|
||||
|
||||
code.constructorCode << getColourIntialisationCode (component, memberVariableName);
|
||||
code.constructorCode << "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class DemoTreeView : public TreeView
|
||||
{
|
||||
public:
|
||||
DemoTreeView()
|
||||
: TreeView ("new treeview")
|
||||
{
|
||||
setRootItem (new DemoTreeViewItem ("Demo root node", 4));
|
||||
}
|
||||
|
||||
~DemoTreeView()
|
||||
{
|
||||
deleteRootItem();
|
||||
}
|
||||
|
||||
private:
|
||||
class DemoTreeViewItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
DemoTreeViewItem (const String& name_, const int numItems)
|
||||
: name (name_)
|
||||
{
|
||||
for (int i = 0; i < numItems; ++i)
|
||||
addSubItem (new DemoTreeViewItem ("Demo sub-node " + String (i), numItems - 1));
|
||||
}
|
||||
|
||||
void paintItem (Graphics& g, int width, int height) override
|
||||
{
|
||||
if (isSelected())
|
||||
g.fillAll (Colours::lightblue);
|
||||
|
||||
g.setColour (Colours::black);
|
||||
g.setFont ((float) height * 0.7f);
|
||||
g.drawText (name, 4, 0, width - 4, height, Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
bool mightContainSubItems() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const String name;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TreeViewRootItemProperty : public ComponentBooleanProperty <TreeView>
|
||||
{
|
||||
public:
|
||||
TreeViewRootItemProperty (TreeView* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TreeView> ("show root item", "Root item visible", "Root item visible", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TreeviewRootChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change TreeView root item");
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return component->isRootItemVisible();
|
||||
}
|
||||
|
||||
private:
|
||||
class TreeviewRootChangeAction : public ComponentUndoableAction <TreeView>
|
||||
{
|
||||
public:
|
||||
TreeviewRootChangeAction (TreeView* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TreeView> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isRootItemVisible();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setRootItemVisible (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setRootItemVisible (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TreeViewRootOpennessProperty : public ComponentChoiceProperty <TreeView>
|
||||
{
|
||||
public:
|
||||
TreeViewRootOpennessProperty (TreeView* comp, JucerDocument& doc)
|
||||
: ComponentChoiceProperty <TreeView> ("default openness", comp, doc)
|
||||
{
|
||||
choices.add ("Items open by default");
|
||||
choices.add ("Items closed by default");
|
||||
}
|
||||
|
||||
void setIndex (int newIndex)
|
||||
{
|
||||
document.perform (new TreeviewOpennessChangeAction (component, *document.getComponentLayout(), newIndex == 0),
|
||||
"Change TreeView openness");
|
||||
}
|
||||
|
||||
int getIndex() const
|
||||
{
|
||||
return component->areItemsOpenByDefault() ? 0 : 1;
|
||||
}
|
||||
|
||||
private:
|
||||
class TreeviewOpennessChangeAction : public ComponentUndoableAction <TreeView>
|
||||
{
|
||||
public:
|
||||
TreeviewOpennessChangeAction (TreeView* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TreeView> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->areItemsOpenByDefault();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setDefaultOpenness (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setDefaultOpenness (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class TreeViewHandler : public ComponentTypeHandler
|
||||
{
|
||||
public:
|
||||
TreeViewHandler()
|
||||
: ComponentTypeHandler ("TreeView", "juce::TreeView", typeid (DemoTreeView), 150, 150)
|
||||
{
|
||||
registerColour (juce::TreeView::backgroundColourId, "background", "backgroundColour");
|
||||
registerColour (juce::TreeView::linesColourId, "lines", "linecol");
|
||||
}
|
||||
|
||||
Component* createNewComponent (JucerDocument*) override
|
||||
{
|
||||
return new DemoTreeView();
|
||||
}
|
||||
|
||||
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
TreeView* const t = dynamic_cast<TreeView*> (comp);
|
||||
XmlElement* const e = ComponentTypeHandler::createXmlFor (comp, layout);
|
||||
|
||||
e->setAttribute ("rootVisible", t->isRootItemVisible());
|
||||
e->setAttribute ("openByDefault", t->areItemsOpenByDefault());
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
|
||||
{
|
||||
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
|
||||
return false;
|
||||
|
||||
TreeView defaultTreeView;
|
||||
TreeView* const t = dynamic_cast<TreeView*> (comp);
|
||||
|
||||
t->setRootItemVisible (xml.getBoolAttribute ("rootVisible", defaultTreeView.isRootItemVisible()));
|
||||
t->setDefaultOpenness (xml.getBoolAttribute ("openByDefault", defaultTreeView.areItemsOpenByDefault()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void getEditableProperties (Component* component, JucerDocument& document,
|
||||
Array<PropertyComponent*>& props, bool multipleSelected) override
|
||||
{
|
||||
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
|
||||
|
||||
if (multipleSelected)
|
||||
return;
|
||||
|
||||
auto* t = dynamic_cast<TreeView*> (component);
|
||||
|
||||
props.add (new TreeViewRootItemProperty (t, document));
|
||||
props.add (new TreeViewRootOpennessProperty (t, document));
|
||||
|
||||
addColourProperties (t, document, props);
|
||||
}
|
||||
|
||||
String getCreationParameters (GeneratedCode&, Component* comp) override
|
||||
{
|
||||
return quotedString (comp->getName(), false);
|
||||
}
|
||||
|
||||
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
|
||||
{
|
||||
TreeView defaultTreeView;
|
||||
TreeView* const t = dynamic_cast<TreeView*> (component);
|
||||
|
||||
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
|
||||
|
||||
if (defaultTreeView.isRootItemVisible() != t->isRootItemVisible())
|
||||
{
|
||||
code.constructorCode
|
||||
<< memberVariableName << "->setRootItemVisible ("
|
||||
<< CodeHelpers::boolLiteral (t->isRootItemVisible()) << ");\n";
|
||||
}
|
||||
|
||||
if (defaultTreeView.areItemsOpenByDefault() != t->areItemsOpenByDefault())
|
||||
{
|
||||
code.constructorCode
|
||||
<< memberVariableName << "->setDefaultOpenness ("
|
||||
<< CodeHelpers::boolLiteral (t->areItemsOpenByDefault()) << ");\n";
|
||||
}
|
||||
|
||||
code.constructorCode << getColourIntialisationCode (component, memberVariableName);
|
||||
code.constructorCode << "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class DemoTreeView : public TreeView
|
||||
{
|
||||
public:
|
||||
DemoTreeView()
|
||||
: TreeView ("new treeview")
|
||||
{
|
||||
setRootItem (new DemoTreeViewItem ("Demo root node", 4));
|
||||
}
|
||||
|
||||
~DemoTreeView()
|
||||
{
|
||||
deleteRootItem();
|
||||
}
|
||||
|
||||
private:
|
||||
class DemoTreeViewItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
DemoTreeViewItem (const String& name_, const int numItems)
|
||||
: name (name_)
|
||||
{
|
||||
for (int i = 0; i < numItems; ++i)
|
||||
addSubItem (new DemoTreeViewItem ("Demo sub-node " + String (i), numItems - 1));
|
||||
}
|
||||
|
||||
void paintItem (Graphics& g, int width, int height) override
|
||||
{
|
||||
if (isSelected())
|
||||
g.fillAll (Colours::lightblue);
|
||||
|
||||
g.setColour (Colours::black);
|
||||
g.setFont ((float) height * 0.7f);
|
||||
g.drawText (name, 4, 0, width - 4, height, Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
bool mightContainSubItems() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const String name;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TreeViewRootItemProperty : public ComponentBooleanProperty <TreeView>
|
||||
{
|
||||
public:
|
||||
TreeViewRootItemProperty (TreeView* comp, JucerDocument& doc)
|
||||
: ComponentBooleanProperty <TreeView> ("show root item", "Root item visible", "Root item visible", comp, doc)
|
||||
{
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.perform (new TreeviewRootChangeAction (component, *document.getComponentLayout(), newState),
|
||||
"Change TreeView root item");
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return component->isRootItemVisible();
|
||||
}
|
||||
|
||||
private:
|
||||
class TreeviewRootChangeAction : public ComponentUndoableAction <TreeView>
|
||||
{
|
||||
public:
|
||||
TreeviewRootChangeAction (TreeView* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TreeView> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->isRootItemVisible();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setRootItemVisible (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setRootItemVisible (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TreeViewRootOpennessProperty : public ComponentChoiceProperty <TreeView>
|
||||
{
|
||||
public:
|
||||
TreeViewRootOpennessProperty (TreeView* comp, JucerDocument& doc)
|
||||
: ComponentChoiceProperty <TreeView> ("default openness", comp, doc)
|
||||
{
|
||||
choices.add ("Items open by default");
|
||||
choices.add ("Items closed by default");
|
||||
}
|
||||
|
||||
void setIndex (int newIndex)
|
||||
{
|
||||
document.perform (new TreeviewOpennessChangeAction (component, *document.getComponentLayout(), newIndex == 0),
|
||||
"Change TreeView openness");
|
||||
}
|
||||
|
||||
int getIndex() const
|
||||
{
|
||||
return component->areItemsOpenByDefault() ? 0 : 1;
|
||||
}
|
||||
|
||||
private:
|
||||
class TreeviewOpennessChangeAction : public ComponentUndoableAction <TreeView>
|
||||
{
|
||||
public:
|
||||
TreeviewOpennessChangeAction (TreeView* const comp, ComponentLayout& l, const bool newState_)
|
||||
: ComponentUndoableAction <TreeView> (comp, l),
|
||||
newState (newState_)
|
||||
{
|
||||
oldState = comp->areItemsOpenByDefault();
|
||||
}
|
||||
|
||||
bool perform()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setDefaultOpenness (newState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo()
|
||||
{
|
||||
showCorrectTab();
|
||||
getComponent()->setDefaultOpenness (oldState);
|
||||
changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool newState, oldState;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,411 +1,411 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../../Application/jucer_Headers.h"
|
||||
#include "jucer_ButtonDocument.h"
|
||||
#include "../jucer_UtilityFunctions.h"
|
||||
|
||||
//==============================================================================
|
||||
static const int normalOff = 0;
|
||||
static const int overOff = 1;
|
||||
static const int downOff = 2;
|
||||
static const int normalOn = 3;
|
||||
static const int overOn = 4;
|
||||
static const int downOn = 5;
|
||||
static const int background = 6;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
ButtonDocument::ButtonDocument (SourceCodeDocument* c)
|
||||
: JucerDocument (c)
|
||||
{
|
||||
paintStatesEnabled [normalOff] = true;
|
||||
paintStatesEnabled [overOff] = true;
|
||||
paintStatesEnabled [downOff] = true;
|
||||
paintStatesEnabled [normalOn] = false;
|
||||
paintStatesEnabled [overOn] = false;
|
||||
paintStatesEnabled [downOn] = false;
|
||||
paintStatesEnabled [background] = false;
|
||||
|
||||
parentClasses = "public juce::Button";
|
||||
|
||||
for (int i = 7; --i >= 0;)
|
||||
{
|
||||
paintRoutines[i].reset (new PaintRoutine());
|
||||
paintRoutines[i]->setDocument (this);
|
||||
paintRoutines[i]->setBackgroundColour (Colours::transparentBlack);
|
||||
}
|
||||
}
|
||||
|
||||
ButtonDocument::~ButtonDocument()
|
||||
{
|
||||
}
|
||||
|
||||
static const char* const stateNames[] =
|
||||
{
|
||||
"normal", "over", "down",
|
||||
"normal on", "over on", "down on",
|
||||
"common background"
|
||||
};
|
||||
|
||||
static int stateNameToIndex (const String& name)
|
||||
{
|
||||
for (int i = 7; --i >= 0;)
|
||||
if (name.equalsIgnoreCase (stateNames[i]))
|
||||
return i;
|
||||
|
||||
jassertfalse;
|
||||
return normalOff;
|
||||
}
|
||||
|
||||
|
||||
int ButtonDocument::getNumPaintRoutines() const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = 7; --i >= 0;)
|
||||
if (paintStatesEnabled [i])
|
||||
++n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
StringArray ButtonDocument::getPaintRoutineNames() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
if (paintStatesEnabled [i])
|
||||
s.add (stateNames [i]);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
PaintRoutine* ButtonDocument::getPaintRoutine (const int index) const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
if (paintStatesEnabled [i])
|
||||
{
|
||||
if (index == n)
|
||||
return paintRoutines[i].get();
|
||||
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
void ButtonDocument::setStatePaintRoutineEnabled (const int index, bool b)
|
||||
{
|
||||
jassert (index > 0 && index < 7);
|
||||
|
||||
if (paintStatesEnabled [index] != b)
|
||||
{
|
||||
paintStatesEnabled [index] = b;
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
bool ButtonDocument::isStatePaintRoutineEnabled (const int index) const
|
||||
{
|
||||
return paintStatesEnabled [index];
|
||||
}
|
||||
|
||||
int ButtonDocument::chooseBestEnabledPaintRoutine (int paintRoutineWanted) const
|
||||
{
|
||||
switch (paintRoutineWanted)
|
||||
{
|
||||
case normalOff: return normalOff;
|
||||
case overOff: return paintStatesEnabled [overOff] ? overOff : normalOff;
|
||||
case downOff: return paintStatesEnabled [downOff] ? downOff : chooseBestEnabledPaintRoutine (overOff);
|
||||
case normalOn: return paintStatesEnabled [normalOn] ? normalOn : normalOff;
|
||||
case overOn: return paintStatesEnabled [overOn] ? overOn : (paintStatesEnabled [normalOn] ? normalOn : chooseBestEnabledPaintRoutine (overOff));
|
||||
case downOn: return paintStatesEnabled [downOn] ? downOn : ((paintStatesEnabled [overOn] || paintStatesEnabled [normalOn])
|
||||
? chooseBestEnabledPaintRoutine (overOn)
|
||||
: chooseBestEnabledPaintRoutine (downOff));
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
|
||||
return normalOff;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String ButtonDocument::getTypeName() const
|
||||
{
|
||||
return "Button";
|
||||
}
|
||||
|
||||
JucerDocument* ButtonDocument::createCopy()
|
||||
{
|
||||
auto newOne = new ButtonDocument (cpp);
|
||||
newOne->resources = resources;
|
||||
newOne->loadFromXml (*createXml());
|
||||
return newOne;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> ButtonDocument::createXml() const
|
||||
{
|
||||
auto doc = JucerDocument::createXml();
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
auto e = paintRoutines[i]->createXml();
|
||||
e->setAttribute ("buttonState", stateNames [i]);
|
||||
e->setAttribute ("enabled", paintStatesEnabled [i]);
|
||||
|
||||
doc->addChildElement (e);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
bool ButtonDocument::loadFromXml (const XmlElement& xml)
|
||||
{
|
||||
if (JucerDocument::loadFromXml (xml))
|
||||
{
|
||||
for (int i = 7; --i >= 0;)
|
||||
paintStatesEnabled [i] = false;
|
||||
|
||||
for (auto* e : xml.getChildWithTagNameIterator (PaintRoutine::xmlTagName))
|
||||
{
|
||||
const int stateIndex = stateNameToIndex (e->getStringAttribute ("buttonState"));
|
||||
|
||||
paintRoutines [stateIndex]->loadFromXml (*e);
|
||||
paintStatesEnabled [stateIndex] = e->getBoolAttribute ("enabled", stateIndex < normalOn);
|
||||
}
|
||||
|
||||
changed();
|
||||
getUndoManager().clearUndoHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ButtonDocument::getOptionalMethods (StringArray& baseClasses,
|
||||
StringArray& returnValues,
|
||||
StringArray& methods,
|
||||
StringArray& initialContents) const
|
||||
{
|
||||
JucerDocument::getOptionalMethods (baseClasses, returnValues, methods, initialContents);
|
||||
|
||||
addMethod ("juce::Button", "void", "clicked()", "", baseClasses, returnValues, methods, initialContents);
|
||||
addMethod ("juce::Button", "void", "buttonStateChanged()", "", baseClasses, returnValues, methods, initialContents);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ButtonStatePaintEnabledProperty : public BooleanPropertyComponent,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
ButtonStatePaintEnabledProperty (const String& name, ButtonDocument& doc, const int stateMethod_)
|
||||
: BooleanPropertyComponent (name, "enabled", "disabled"),
|
||||
document (doc),
|
||||
stateMethod (stateMethod_)
|
||||
{
|
||||
document.addChangeListener (this);
|
||||
}
|
||||
|
||||
~ButtonStatePaintEnabledProperty()
|
||||
{
|
||||
document.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.setStatePaintRoutineEnabled (stateMethod, newState);
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return document.isStatePaintRoutineEnabled (stateMethod);
|
||||
}
|
||||
|
||||
private:
|
||||
void changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
ButtonDocument& document;
|
||||
const int stateMethod;
|
||||
};
|
||||
|
||||
void ButtonDocument::addExtraClassProperties (PropertyPanel& panel)
|
||||
{
|
||||
Array <PropertyComponent*> props;
|
||||
|
||||
for (int i = 1; i < 7; ++i)
|
||||
props.add (new ButtonStatePaintEnabledProperty (stateNames[i], *this, i));
|
||||
|
||||
panel.addSection ("Button paint routines", props);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ButtonTestComponent : public Button
|
||||
{
|
||||
public:
|
||||
ButtonTestComponent (ButtonDocument* const doc, const bool fillBackground)
|
||||
: Button (String()),
|
||||
document (doc),
|
||||
alwaysFillBackground (fillBackground)
|
||||
{
|
||||
setClickingTogglesState (true);
|
||||
}
|
||||
|
||||
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
|
||||
{
|
||||
if (document->paintStatesEnabled [background])
|
||||
{
|
||||
document->paintRoutines [background]->fillWithBackground (g, alwaysFillBackground);
|
||||
document->paintRoutines [background]->drawElements (g, getLocalBounds());
|
||||
}
|
||||
|
||||
const int stateIndex
|
||||
= getToggleState()
|
||||
? (isButtonDown ? document->chooseBestEnabledPaintRoutine (downOn)
|
||||
: (isMouseOverButton ? document->chooseBestEnabledPaintRoutine (overOn)
|
||||
: document->chooseBestEnabledPaintRoutine (normalOn)))
|
||||
: (isButtonDown ? document->chooseBestEnabledPaintRoutine (downOff)
|
||||
: (isMouseOverButton ? document->chooseBestEnabledPaintRoutine (overOff)
|
||||
: normalOff));
|
||||
|
||||
document->paintRoutines [stateIndex]->fillWithBackground (g, ! document->paintStatesEnabled [background]);
|
||||
document->paintRoutines [stateIndex]->drawElements (g, getLocalBounds());
|
||||
}
|
||||
|
||||
private:
|
||||
ButtonDocument* const document;
|
||||
const bool alwaysFillBackground;
|
||||
};
|
||||
|
||||
Component* ButtonDocument::createTestComponent (const bool alwaysFillBackground)
|
||||
{
|
||||
return new ButtonTestComponent (this, alwaysFillBackground);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ButtonDocument::fillInGeneratedCode (GeneratedCode& code) const
|
||||
{
|
||||
JucerDocument::fillInGeneratedCode (code);
|
||||
|
||||
code.parentClassInitialiser = "Button (" + quotedString (code.componentName, false) + ")";
|
||||
code.removeCallback ("void", "paint (Graphics& g)");
|
||||
}
|
||||
|
||||
void ButtonDocument::fillInPaintCode (GeneratedCode& code) const
|
||||
{
|
||||
jassert (paintStatesEnabled [normalOff]);
|
||||
String paintCode [7];
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
if (paintStatesEnabled [i])
|
||||
paintRoutines[i]->fillInGeneratedCode (code, paintCode [i]);
|
||||
|
||||
String& s = code.getCallbackCode ("public juce::Button",
|
||||
"void",
|
||||
"paintButton (juce::Graphics& g, bool isMouseOverButton, bool isButtonDown)",
|
||||
false);
|
||||
|
||||
int numPaintRoutines = getNumPaintRoutines();
|
||||
|
||||
if (paintStatesEnabled [background])
|
||||
{
|
||||
s << paintCode [background] << "\n";
|
||||
--numPaintRoutines;
|
||||
}
|
||||
|
||||
if (numPaintRoutines == 1)
|
||||
{
|
||||
s << paintCode [normalOff];
|
||||
}
|
||||
else if (numPaintRoutines == downOff && (paintStatesEnabled [overOff] || paintStatesEnabled [downOff] || paintStatesEnabled [normalOn]))
|
||||
{
|
||||
if (paintStatesEnabled [normalOn])
|
||||
{
|
||||
s << "if (getToggleState())\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [normalOn], 4, false).trimEnd();
|
||||
}
|
||||
else if (paintStatesEnabled [overOff])
|
||||
{
|
||||
s << "if (isButtonDown || isMouseOverButton)\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [overOff], 4, false).trimEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
s << "if (isButtonDown)\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [downOff], 4, false).trimEnd();
|
||||
}
|
||||
|
||||
s << "\n}\nelse\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [normalOff], 4, false).trimEnd()
|
||||
<< "\n}\n";
|
||||
}
|
||||
else if (numPaintRoutines == normalOn && paintStatesEnabled [overOff] && paintStatesEnabled [downOff])
|
||||
{
|
||||
s << "if (isButtonDown)\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [downOff], 4, false).trimEnd()
|
||||
<< "\n}\nelse if (isMouseOverButton)\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [overOff], 4, false).trimEnd()
|
||||
<< "\n}\nelse\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [normalOff], 4, false).trimEnd()
|
||||
<< "\n}\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (paintStatesEnabled [normalOn] || paintStatesEnabled [overOn] || paintStatesEnabled [downOn])
|
||||
{
|
||||
s << "switch (getToggleState() ? (isButtonDown ? "
|
||||
<< chooseBestEnabledPaintRoutine (downOn) << " : (isMouseOverButton ? "
|
||||
<< chooseBestEnabledPaintRoutine (overOn) << " : "
|
||||
<< chooseBestEnabledPaintRoutine (normalOn) << "))\n : (isButtonDown ? "
|
||||
<< chooseBestEnabledPaintRoutine (downOff) << " : (isMouseOverButton ? "
|
||||
<< chooseBestEnabledPaintRoutine (overOff) << " : 0)))\n{\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
s << "switch (isButtonDown ? " << chooseBestEnabledPaintRoutine (downOff)
|
||||
<< " : (isMouseOverButton ? " << chooseBestEnabledPaintRoutine (overOff)
|
||||
<< " : 0))\n{\n";
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
if (paintStatesEnabled [i])
|
||||
{
|
||||
s << "case " << i << ":\n {\n "
|
||||
<< CodeHelpers::indent (paintCode [i], 8, false).trimEnd()
|
||||
<< "\n break;\n }\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
s << "default:\n break;\n}\n";
|
||||
}
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../../Application/jucer_Headers.h"
|
||||
#include "jucer_ButtonDocument.h"
|
||||
#include "../jucer_UtilityFunctions.h"
|
||||
|
||||
//==============================================================================
|
||||
static const int normalOff = 0;
|
||||
static const int overOff = 1;
|
||||
static const int downOff = 2;
|
||||
static const int normalOn = 3;
|
||||
static const int overOn = 4;
|
||||
static const int downOn = 5;
|
||||
static const int background = 6;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
ButtonDocument::ButtonDocument (SourceCodeDocument* c)
|
||||
: JucerDocument (c)
|
||||
{
|
||||
paintStatesEnabled [normalOff] = true;
|
||||
paintStatesEnabled [overOff] = true;
|
||||
paintStatesEnabled [downOff] = true;
|
||||
paintStatesEnabled [normalOn] = false;
|
||||
paintStatesEnabled [overOn] = false;
|
||||
paintStatesEnabled [downOn] = false;
|
||||
paintStatesEnabled [background] = false;
|
||||
|
||||
parentClasses = "public juce::Button";
|
||||
|
||||
for (int i = 7; --i >= 0;)
|
||||
{
|
||||
paintRoutines[i].reset (new PaintRoutine());
|
||||
paintRoutines[i]->setDocument (this);
|
||||
paintRoutines[i]->setBackgroundColour (Colours::transparentBlack);
|
||||
}
|
||||
}
|
||||
|
||||
ButtonDocument::~ButtonDocument()
|
||||
{
|
||||
}
|
||||
|
||||
static const char* const stateNames[] =
|
||||
{
|
||||
"normal", "over", "down",
|
||||
"normal on", "over on", "down on",
|
||||
"common background"
|
||||
};
|
||||
|
||||
static int stateNameToIndex (const String& name)
|
||||
{
|
||||
for (int i = 7; --i >= 0;)
|
||||
if (name.equalsIgnoreCase (stateNames[i]))
|
||||
return i;
|
||||
|
||||
jassertfalse;
|
||||
return normalOff;
|
||||
}
|
||||
|
||||
|
||||
int ButtonDocument::getNumPaintRoutines() const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = 7; --i >= 0;)
|
||||
if (paintStatesEnabled [i])
|
||||
++n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
StringArray ButtonDocument::getPaintRoutineNames() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
if (paintStatesEnabled [i])
|
||||
s.add (stateNames [i]);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
PaintRoutine* ButtonDocument::getPaintRoutine (const int index) const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
if (paintStatesEnabled [i])
|
||||
{
|
||||
if (index == n)
|
||||
return paintRoutines[i].get();
|
||||
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
void ButtonDocument::setStatePaintRoutineEnabled (const int index, bool b)
|
||||
{
|
||||
jassert (index > 0 && index < 7);
|
||||
|
||||
if (paintStatesEnabled [index] != b)
|
||||
{
|
||||
paintStatesEnabled [index] = b;
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
bool ButtonDocument::isStatePaintRoutineEnabled (const int index) const
|
||||
{
|
||||
return paintStatesEnabled [index];
|
||||
}
|
||||
|
||||
int ButtonDocument::chooseBestEnabledPaintRoutine (int paintRoutineWanted) const
|
||||
{
|
||||
switch (paintRoutineWanted)
|
||||
{
|
||||
case normalOff: return normalOff;
|
||||
case overOff: return paintStatesEnabled [overOff] ? overOff : normalOff;
|
||||
case downOff: return paintStatesEnabled [downOff] ? downOff : chooseBestEnabledPaintRoutine (overOff);
|
||||
case normalOn: return paintStatesEnabled [normalOn] ? normalOn : normalOff;
|
||||
case overOn: return paintStatesEnabled [overOn] ? overOn : (paintStatesEnabled [normalOn] ? normalOn : chooseBestEnabledPaintRoutine (overOff));
|
||||
case downOn: return paintStatesEnabled [downOn] ? downOn : ((paintStatesEnabled [overOn] || paintStatesEnabled [normalOn])
|
||||
? chooseBestEnabledPaintRoutine (overOn)
|
||||
: chooseBestEnabledPaintRoutine (downOff));
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
|
||||
return normalOff;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String ButtonDocument::getTypeName() const
|
||||
{
|
||||
return "Button";
|
||||
}
|
||||
|
||||
JucerDocument* ButtonDocument::createCopy()
|
||||
{
|
||||
auto newOne = new ButtonDocument (cpp);
|
||||
newOne->resources = resources;
|
||||
newOne->loadFromXml (*createXml());
|
||||
return newOne;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> ButtonDocument::createXml() const
|
||||
{
|
||||
auto doc = JucerDocument::createXml();
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
auto e = paintRoutines[i]->createXml();
|
||||
e->setAttribute ("buttonState", stateNames [i]);
|
||||
e->setAttribute ("enabled", paintStatesEnabled [i]);
|
||||
|
||||
doc->addChildElement (e);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
bool ButtonDocument::loadFromXml (const XmlElement& xml)
|
||||
{
|
||||
if (JucerDocument::loadFromXml (xml))
|
||||
{
|
||||
for (int i = 7; --i >= 0;)
|
||||
paintStatesEnabled [i] = false;
|
||||
|
||||
for (auto* e : xml.getChildWithTagNameIterator (PaintRoutine::xmlTagName))
|
||||
{
|
||||
const int stateIndex = stateNameToIndex (e->getStringAttribute ("buttonState"));
|
||||
|
||||
paintRoutines [stateIndex]->loadFromXml (*e);
|
||||
paintStatesEnabled [stateIndex] = e->getBoolAttribute ("enabled", stateIndex < normalOn);
|
||||
}
|
||||
|
||||
changed();
|
||||
getUndoManager().clearUndoHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ButtonDocument::getOptionalMethods (StringArray& baseClasses,
|
||||
StringArray& returnValues,
|
||||
StringArray& methods,
|
||||
StringArray& initialContents) const
|
||||
{
|
||||
JucerDocument::getOptionalMethods (baseClasses, returnValues, methods, initialContents);
|
||||
|
||||
addMethod ("juce::Button", "void", "clicked()", "", baseClasses, returnValues, methods, initialContents);
|
||||
addMethod ("juce::Button", "void", "buttonStateChanged()", "", baseClasses, returnValues, methods, initialContents);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ButtonStatePaintEnabledProperty : public BooleanPropertyComponent,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
ButtonStatePaintEnabledProperty (const String& name, ButtonDocument& doc, const int stateMethod_)
|
||||
: BooleanPropertyComponent (name, "enabled", "disabled"),
|
||||
document (doc),
|
||||
stateMethod (stateMethod_)
|
||||
{
|
||||
document.addChangeListener (this);
|
||||
}
|
||||
|
||||
~ButtonStatePaintEnabledProperty()
|
||||
{
|
||||
document.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void setState (bool newState)
|
||||
{
|
||||
document.setStatePaintRoutineEnabled (stateMethod, newState);
|
||||
}
|
||||
|
||||
bool getState() const
|
||||
{
|
||||
return document.isStatePaintRoutineEnabled (stateMethod);
|
||||
}
|
||||
|
||||
private:
|
||||
void changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
ButtonDocument& document;
|
||||
const int stateMethod;
|
||||
};
|
||||
|
||||
void ButtonDocument::addExtraClassProperties (PropertyPanel& panel)
|
||||
{
|
||||
Array <PropertyComponent*> props;
|
||||
|
||||
for (int i = 1; i < 7; ++i)
|
||||
props.add (new ButtonStatePaintEnabledProperty (stateNames[i], *this, i));
|
||||
|
||||
panel.addSection ("Button paint routines", props);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ButtonTestComponent : public Button
|
||||
{
|
||||
public:
|
||||
ButtonTestComponent (ButtonDocument* const doc, const bool fillBackground)
|
||||
: Button (String()),
|
||||
document (doc),
|
||||
alwaysFillBackground (fillBackground)
|
||||
{
|
||||
setClickingTogglesState (true);
|
||||
}
|
||||
|
||||
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
|
||||
{
|
||||
if (document->paintStatesEnabled [background])
|
||||
{
|
||||
document->paintRoutines [background]->fillWithBackground (g, alwaysFillBackground);
|
||||
document->paintRoutines [background]->drawElements (g, getLocalBounds());
|
||||
}
|
||||
|
||||
const int stateIndex
|
||||
= getToggleState()
|
||||
? (isButtonDown ? document->chooseBestEnabledPaintRoutine (downOn)
|
||||
: (isMouseOverButton ? document->chooseBestEnabledPaintRoutine (overOn)
|
||||
: document->chooseBestEnabledPaintRoutine (normalOn)))
|
||||
: (isButtonDown ? document->chooseBestEnabledPaintRoutine (downOff)
|
||||
: (isMouseOverButton ? document->chooseBestEnabledPaintRoutine (overOff)
|
||||
: normalOff));
|
||||
|
||||
document->paintRoutines [stateIndex]->fillWithBackground (g, ! document->paintStatesEnabled [background]);
|
||||
document->paintRoutines [stateIndex]->drawElements (g, getLocalBounds());
|
||||
}
|
||||
|
||||
private:
|
||||
ButtonDocument* const document;
|
||||
const bool alwaysFillBackground;
|
||||
};
|
||||
|
||||
Component* ButtonDocument::createTestComponent (const bool alwaysFillBackground)
|
||||
{
|
||||
return new ButtonTestComponent (this, alwaysFillBackground);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ButtonDocument::fillInGeneratedCode (GeneratedCode& code) const
|
||||
{
|
||||
JucerDocument::fillInGeneratedCode (code);
|
||||
|
||||
code.parentClassInitialiser = "Button (" + quotedString (code.componentName, false) + ")";
|
||||
code.removeCallback ("void", "paint (Graphics& g)");
|
||||
}
|
||||
|
||||
void ButtonDocument::fillInPaintCode (GeneratedCode& code) const
|
||||
{
|
||||
jassert (paintStatesEnabled [normalOff]);
|
||||
String paintCode [7];
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
if (paintStatesEnabled [i])
|
||||
paintRoutines[i]->fillInGeneratedCode (code, paintCode [i]);
|
||||
|
||||
String& s = code.getCallbackCode ("public juce::Button",
|
||||
"void",
|
||||
"paintButton (juce::Graphics& g, bool isMouseOverButton, bool isButtonDown)",
|
||||
false);
|
||||
|
||||
int numPaintRoutines = getNumPaintRoutines();
|
||||
|
||||
if (paintStatesEnabled [background])
|
||||
{
|
||||
s << paintCode [background] << "\n";
|
||||
--numPaintRoutines;
|
||||
}
|
||||
|
||||
if (numPaintRoutines == 1)
|
||||
{
|
||||
s << paintCode [normalOff];
|
||||
}
|
||||
else if (numPaintRoutines == downOff && (paintStatesEnabled [overOff] || paintStatesEnabled [downOff] || paintStatesEnabled [normalOn]))
|
||||
{
|
||||
if (paintStatesEnabled [normalOn])
|
||||
{
|
||||
s << "if (getToggleState())\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [normalOn], 4, false).trimEnd();
|
||||
}
|
||||
else if (paintStatesEnabled [overOff])
|
||||
{
|
||||
s << "if (isButtonDown || isMouseOverButton)\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [overOff], 4, false).trimEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
s << "if (isButtonDown)\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [downOff], 4, false).trimEnd();
|
||||
}
|
||||
|
||||
s << "\n}\nelse\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [normalOff], 4, false).trimEnd()
|
||||
<< "\n}\n";
|
||||
}
|
||||
else if (numPaintRoutines == normalOn && paintStatesEnabled [overOff] && paintStatesEnabled [downOff])
|
||||
{
|
||||
s << "if (isButtonDown)\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [downOff], 4, false).trimEnd()
|
||||
<< "\n}\nelse if (isMouseOverButton)\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [overOff], 4, false).trimEnd()
|
||||
<< "\n}\nelse\n{\n "
|
||||
<< CodeHelpers::indent (paintCode [normalOff], 4, false).trimEnd()
|
||||
<< "\n}\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (paintStatesEnabled [normalOn] || paintStatesEnabled [overOn] || paintStatesEnabled [downOn])
|
||||
{
|
||||
s << "switch (getToggleState() ? (isButtonDown ? "
|
||||
<< chooseBestEnabledPaintRoutine (downOn) << " : (isMouseOverButton ? "
|
||||
<< chooseBestEnabledPaintRoutine (overOn) << " : "
|
||||
<< chooseBestEnabledPaintRoutine (normalOn) << "))\n : (isButtonDown ? "
|
||||
<< chooseBestEnabledPaintRoutine (downOff) << " : (isMouseOverButton ? "
|
||||
<< chooseBestEnabledPaintRoutine (overOff) << " : 0)))\n{\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
s << "switch (isButtonDown ? " << chooseBestEnabledPaintRoutine (downOff)
|
||||
<< " : (isMouseOverButton ? " << chooseBestEnabledPaintRoutine (overOff)
|
||||
<< " : 0))\n{\n";
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
if (paintStatesEnabled [i])
|
||||
{
|
||||
s << "case " << i << ":\n {\n "
|
||||
<< CodeHelpers::indent (paintCode [i], 8, false).trimEnd()
|
||||
<< "\n break;\n }\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
s << "default:\n break;\n}\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_JucerDocument.h"
|
||||
|
||||
//==============================================================================
|
||||
class ButtonDocument : public JucerDocument
|
||||
{
|
||||
public:
|
||||
ButtonDocument (SourceCodeDocument* cpp);
|
||||
~ButtonDocument();
|
||||
|
||||
//==============================================================================
|
||||
String getTypeName() const;
|
||||
|
||||
JucerDocument* createCopy();
|
||||
Component* createTestComponent (const bool alwaysFillBackground);
|
||||
|
||||
int getNumPaintRoutines() const;
|
||||
StringArray getPaintRoutineNames() const;
|
||||
PaintRoutine* getPaintRoutine (const int index) const;
|
||||
|
||||
void setStatePaintRoutineEnabled (const int index, bool b);
|
||||
bool isStatePaintRoutineEnabled (const int index) const;
|
||||
|
||||
int chooseBestEnabledPaintRoutine (int paintRoutineWanted) const;
|
||||
|
||||
ComponentLayout* getComponentLayout() const { return nullptr; }
|
||||
|
||||
void addExtraClassProperties (PropertyPanel&);
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<XmlElement> createXml() const;
|
||||
bool loadFromXml (const XmlElement&);
|
||||
|
||||
void fillInGeneratedCode (GeneratedCode& code) const;
|
||||
void fillInPaintCode (GeneratedCode& code) const;
|
||||
|
||||
void getOptionalMethods (StringArray& baseClasses,
|
||||
StringArray& returnValues,
|
||||
StringArray& methods,
|
||||
StringArray& initialContents) const;
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<PaintRoutine> paintRoutines[7];
|
||||
bool paintStatesEnabled [7];
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_JucerDocument.h"
|
||||
|
||||
//==============================================================================
|
||||
class ButtonDocument : public JucerDocument
|
||||
{
|
||||
public:
|
||||
ButtonDocument (SourceCodeDocument* cpp);
|
||||
~ButtonDocument();
|
||||
|
||||
//==============================================================================
|
||||
String getTypeName() const;
|
||||
|
||||
JucerDocument* createCopy();
|
||||
Component* createTestComponent (const bool alwaysFillBackground);
|
||||
|
||||
int getNumPaintRoutines() const;
|
||||
StringArray getPaintRoutineNames() const;
|
||||
PaintRoutine* getPaintRoutine (const int index) const;
|
||||
|
||||
void setStatePaintRoutineEnabled (const int index, bool b);
|
||||
bool isStatePaintRoutineEnabled (const int index) const;
|
||||
|
||||
int chooseBestEnabledPaintRoutine (int paintRoutineWanted) const;
|
||||
|
||||
ComponentLayout* getComponentLayout() const { return nullptr; }
|
||||
|
||||
void addExtraClassProperties (PropertyPanel&);
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<XmlElement> createXml() const;
|
||||
bool loadFromXml (const XmlElement&);
|
||||
|
||||
void fillInGeneratedCode (GeneratedCode& code) const;
|
||||
void fillInPaintCode (GeneratedCode& code) const;
|
||||
|
||||
void getOptionalMethods (StringArray& baseClasses,
|
||||
StringArray& returnValues,
|
||||
StringArray& methods,
|
||||
StringArray& initialContents) const;
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<PaintRoutine> paintRoutines[7];
|
||||
bool paintStatesEnabled [7];
|
||||
};
|
||||
|
||||
@@ -1,170 +1,170 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../../Application/jucer_Headers.h"
|
||||
#include "jucer_ComponentDocument.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
ComponentDocument::ComponentDocument (SourceCodeDocument* c)
|
||||
: JucerDocument (c)
|
||||
{
|
||||
components.reset (new ComponentLayout());
|
||||
components->setDocument (this);
|
||||
|
||||
backgroundGraphics.reset (new PaintRoutine());
|
||||
backgroundGraphics->setDocument (this);
|
||||
}
|
||||
|
||||
ComponentDocument::~ComponentDocument()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String ComponentDocument::getTypeName() const
|
||||
{
|
||||
return "Component";
|
||||
}
|
||||
|
||||
JucerDocument* ComponentDocument::createCopy()
|
||||
{
|
||||
auto newOne = new ComponentDocument (cpp);
|
||||
|
||||
newOne->resources = resources;
|
||||
newOne->loadFromXml (*createXml());
|
||||
|
||||
return newOne;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> ComponentDocument::createXml() const
|
||||
{
|
||||
auto doc = JucerDocument::createXml();
|
||||
|
||||
doc->addChildElement (backgroundGraphics->createXml());
|
||||
components->addToXml (*doc);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
bool ComponentDocument::loadFromXml (const XmlElement& xml)
|
||||
{
|
||||
if (JucerDocument::loadFromXml (xml))
|
||||
{
|
||||
components->clearComponents();
|
||||
|
||||
for (auto* e : xml.getChildIterator())
|
||||
{
|
||||
if (e->hasTagName (PaintRoutine::xmlTagName))
|
||||
backgroundGraphics->loadFromXml (*e);
|
||||
else
|
||||
components->addComponentFromXml (*e, false);
|
||||
}
|
||||
|
||||
changed();
|
||||
getUndoManager().clearUndoHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ComponentDocument::applyCustomPaintSnippets (StringArray& snippets)
|
||||
{
|
||||
backgroundGraphics->applyCustomPaintSnippets (snippets);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class NormalTestComponent : public Component
|
||||
{
|
||||
public:
|
||||
NormalTestComponent (ComponentDocument* const doc, const bool fillBackground)
|
||||
: document (doc),
|
||||
alwaysFillBackground (fillBackground)
|
||||
{
|
||||
ComponentLayout* const layout = document->getComponentLayout();
|
||||
|
||||
for (int i = 0; i < layout->getNumComponents(); ++i)
|
||||
addAndMakeVisible (layout->getComponent (i));
|
||||
}
|
||||
|
||||
~NormalTestComponent() override
|
||||
{
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
removeChildComponent (i);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
document->getPaintRoutine (0)->fillWithBackground (g, alwaysFillBackground);
|
||||
document->getPaintRoutine (0)->drawElements (g, getLocalBounds());
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
if (! getBounds().isEmpty())
|
||||
{
|
||||
int numTimesToTry = 10;
|
||||
|
||||
while (--numTimesToTry >= 0)
|
||||
{
|
||||
bool anyCompsMoved = false;
|
||||
|
||||
for (int i = 0; i < getNumChildComponents(); ++i)
|
||||
{
|
||||
Component* comp = getChildComponent (i);
|
||||
|
||||
if (ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*comp))
|
||||
{
|
||||
const Rectangle<int> newBounds (type->getComponentPosition (comp)
|
||||
.getRectangle (getLocalBounds(),
|
||||
document->getComponentLayout()));
|
||||
|
||||
anyCompsMoved = anyCompsMoved || (comp->getBounds() != newBounds);
|
||||
comp->setBounds (newBounds);
|
||||
}
|
||||
}
|
||||
|
||||
// repeat this loop until they've all stopped shuffling (might require a few
|
||||
// loops for all the relative positioned comps to settle down)
|
||||
if (! anyCompsMoved)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ComponentDocument* const document;
|
||||
const bool alwaysFillBackground;
|
||||
};
|
||||
|
||||
Component* ComponentDocument::createTestComponent (const bool alwaysFillBackground)
|
||||
{
|
||||
return new NormalTestComponent (this, alwaysFillBackground);
|
||||
}
|
||||
|
||||
void ComponentDocument::fillInGeneratedCode (GeneratedCode& code) const
|
||||
{
|
||||
JucerDocument::fillInGeneratedCode (code);
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../../Application/jucer_Headers.h"
|
||||
#include "jucer_ComponentDocument.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
ComponentDocument::ComponentDocument (SourceCodeDocument* c)
|
||||
: JucerDocument (c)
|
||||
{
|
||||
components.reset (new ComponentLayout());
|
||||
components->setDocument (this);
|
||||
|
||||
backgroundGraphics.reset (new PaintRoutine());
|
||||
backgroundGraphics->setDocument (this);
|
||||
}
|
||||
|
||||
ComponentDocument::~ComponentDocument()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String ComponentDocument::getTypeName() const
|
||||
{
|
||||
return "Component";
|
||||
}
|
||||
|
||||
JucerDocument* ComponentDocument::createCopy()
|
||||
{
|
||||
auto newOne = new ComponentDocument (cpp);
|
||||
|
||||
newOne->resources = resources;
|
||||
newOne->loadFromXml (*createXml());
|
||||
|
||||
return newOne;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> ComponentDocument::createXml() const
|
||||
{
|
||||
auto doc = JucerDocument::createXml();
|
||||
|
||||
doc->addChildElement (backgroundGraphics->createXml());
|
||||
components->addToXml (*doc);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
bool ComponentDocument::loadFromXml (const XmlElement& xml)
|
||||
{
|
||||
if (JucerDocument::loadFromXml (xml))
|
||||
{
|
||||
components->clearComponents();
|
||||
|
||||
for (auto* e : xml.getChildIterator())
|
||||
{
|
||||
if (e->hasTagName (PaintRoutine::xmlTagName))
|
||||
backgroundGraphics->loadFromXml (*e);
|
||||
else
|
||||
components->addComponentFromXml (*e, false);
|
||||
}
|
||||
|
||||
changed();
|
||||
getUndoManager().clearUndoHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ComponentDocument::applyCustomPaintSnippets (StringArray& snippets)
|
||||
{
|
||||
backgroundGraphics->applyCustomPaintSnippets (snippets);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class NormalTestComponent : public Component
|
||||
{
|
||||
public:
|
||||
NormalTestComponent (ComponentDocument* const doc, const bool fillBackground)
|
||||
: document (doc),
|
||||
alwaysFillBackground (fillBackground)
|
||||
{
|
||||
ComponentLayout* const layout = document->getComponentLayout();
|
||||
|
||||
for (int i = 0; i < layout->getNumComponents(); ++i)
|
||||
addAndMakeVisible (layout->getComponent (i));
|
||||
}
|
||||
|
||||
~NormalTestComponent() override
|
||||
{
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
removeChildComponent (i);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
document->getPaintRoutine (0)->fillWithBackground (g, alwaysFillBackground);
|
||||
document->getPaintRoutine (0)->drawElements (g, getLocalBounds());
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
if (! getBounds().isEmpty())
|
||||
{
|
||||
int numTimesToTry = 10;
|
||||
|
||||
while (--numTimesToTry >= 0)
|
||||
{
|
||||
bool anyCompsMoved = false;
|
||||
|
||||
for (int i = 0; i < getNumChildComponents(); ++i)
|
||||
{
|
||||
Component* comp = getChildComponent (i);
|
||||
|
||||
if (ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*comp))
|
||||
{
|
||||
const Rectangle<int> newBounds (type->getComponentPosition (comp)
|
||||
.getRectangle (getLocalBounds(),
|
||||
document->getComponentLayout()));
|
||||
|
||||
anyCompsMoved = anyCompsMoved || (comp->getBounds() != newBounds);
|
||||
comp->setBounds (newBounds);
|
||||
}
|
||||
}
|
||||
|
||||
// repeat this loop until they've all stopped shuffling (might require a few
|
||||
// loops for all the relative positioned comps to settle down)
|
||||
if (! anyCompsMoved)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ComponentDocument* const document;
|
||||
const bool alwaysFillBackground;
|
||||
};
|
||||
|
||||
Component* ComponentDocument::createTestComponent (const bool alwaysFillBackground)
|
||||
{
|
||||
return new NormalTestComponent (this, alwaysFillBackground);
|
||||
}
|
||||
|
||||
void ComponentDocument::fillInGeneratedCode (GeneratedCode& code) const
|
||||
{
|
||||
JucerDocument::fillInGeneratedCode (code);
|
||||
}
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_JucerDocument.h"
|
||||
|
||||
//==============================================================================
|
||||
class ComponentDocument : public JucerDocument
|
||||
{
|
||||
public:
|
||||
ComponentDocument (SourceCodeDocument* cpp);
|
||||
~ComponentDocument();
|
||||
|
||||
//==============================================================================
|
||||
String getTypeName() const;
|
||||
|
||||
JucerDocument* createCopy();
|
||||
Component* createTestComponent (const bool alwaysFillBackground);
|
||||
|
||||
int getNumPaintRoutines() const { return 1; }
|
||||
StringArray getPaintRoutineNames() const { return StringArray ("Graphics"); }
|
||||
PaintRoutine* getPaintRoutine (const int index) const { return index == 0 ? backgroundGraphics.get() : nullptr; }
|
||||
|
||||
ComponentLayout* getComponentLayout() const { return components.get(); }
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<XmlElement> createXml() const;
|
||||
bool loadFromXml (const XmlElement& xml);
|
||||
|
||||
void fillInGeneratedCode (GeneratedCode& code) const;
|
||||
void applyCustomPaintSnippets (StringArray&);
|
||||
|
||||
private:
|
||||
std::unique_ptr<ComponentLayout> components;
|
||||
std::unique_ptr<PaintRoutine> backgroundGraphics;
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_JucerDocument.h"
|
||||
|
||||
//==============================================================================
|
||||
class ComponentDocument : public JucerDocument
|
||||
{
|
||||
public:
|
||||
ComponentDocument (SourceCodeDocument* cpp);
|
||||
~ComponentDocument();
|
||||
|
||||
//==============================================================================
|
||||
String getTypeName() const;
|
||||
|
||||
JucerDocument* createCopy();
|
||||
Component* createTestComponent (const bool alwaysFillBackground);
|
||||
|
||||
int getNumPaintRoutines() const { return 1; }
|
||||
StringArray getPaintRoutineNames() const { return StringArray ("Graphics"); }
|
||||
PaintRoutine* getPaintRoutine (const int index) const { return index == 0 ? backgroundGraphics.get() : nullptr; }
|
||||
|
||||
ComponentLayout* getComponentLayout() const { return components.get(); }
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<XmlElement> createXml() const;
|
||||
bool loadFromXml (const XmlElement& xml);
|
||||
|
||||
void fillInGeneratedCode (GeneratedCode& code) const;
|
||||
void applyCustomPaintSnippets (StringArray&);
|
||||
|
||||
private:
|
||||
std::unique_ptr<ComponentLayout> components;
|
||||
std::unique_ptr<PaintRoutine> backgroundGraphics;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +1,80 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_PaintRoutine.h"
|
||||
#include "../jucer_JucerDocument.h"
|
||||
#include "jucer_StrokeType.h"
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for paint elements that have a fill colour and stroke.
|
||||
|
||||
*/
|
||||
class ColouredElement : public PaintElement
|
||||
{
|
||||
public:
|
||||
ColouredElement (PaintRoutine* owner,
|
||||
const String& name,
|
||||
const bool showOutline_,
|
||||
const bool showJointAndEnd_);
|
||||
|
||||
~ColouredElement() override;
|
||||
|
||||
//==============================================================================
|
||||
void getEditableProperties (Array<PropertyComponent*>& props, bool multipleSelected) override;
|
||||
void getColourSpecificProperties (Array<PropertyComponent*>& props);
|
||||
|
||||
//==============================================================================
|
||||
const JucerFillType& getFillType() noexcept;
|
||||
void setFillType (const JucerFillType& newType, const bool undoable);
|
||||
|
||||
bool isStrokeEnabled() const noexcept;
|
||||
void enableStroke (bool enable, const bool undoable);
|
||||
|
||||
const StrokeType& getStrokeType() noexcept;
|
||||
void setStrokeType (const PathStrokeType& newType, const bool undoable);
|
||||
void setStrokeFill (const JucerFillType& newType, const bool undoable);
|
||||
|
||||
//==============================================================================
|
||||
Rectangle<int> getCurrentBounds (const Rectangle<int>& parentArea) const override;
|
||||
void setCurrentBounds (const Rectangle<int>& newBounds, const Rectangle<int>& parentArea, const bool undoable) override;
|
||||
|
||||
void createSiblingComponents() override;
|
||||
|
||||
//==============================================================================
|
||||
void addColourAttributes (XmlElement* const e) const;
|
||||
bool loadColourAttributes (const XmlElement& xml);
|
||||
|
||||
protected:
|
||||
JucerFillType fillType;
|
||||
|
||||
bool isStrokePresent;
|
||||
const bool showOutline, showJointAndEnd;
|
||||
StrokeType strokeType;
|
||||
|
||||
void convertToNewPathElement (const Path& path);
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_PaintRoutine.h"
|
||||
#include "../jucer_JucerDocument.h"
|
||||
#include "jucer_StrokeType.h"
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for paint elements that have a fill colour and stroke.
|
||||
|
||||
*/
|
||||
class ColouredElement : public PaintElement
|
||||
{
|
||||
public:
|
||||
ColouredElement (PaintRoutine* owner,
|
||||
const String& name,
|
||||
const bool showOutline_,
|
||||
const bool showJointAndEnd_);
|
||||
|
||||
~ColouredElement() override;
|
||||
|
||||
//==============================================================================
|
||||
void getEditableProperties (Array<PropertyComponent*>& props, bool multipleSelected) override;
|
||||
void getColourSpecificProperties (Array<PropertyComponent*>& props);
|
||||
|
||||
//==============================================================================
|
||||
const JucerFillType& getFillType() noexcept;
|
||||
void setFillType (const JucerFillType& newType, const bool undoable);
|
||||
|
||||
bool isStrokeEnabled() const noexcept;
|
||||
void enableStroke (bool enable, const bool undoable);
|
||||
|
||||
const StrokeType& getStrokeType() noexcept;
|
||||
void setStrokeType (const PathStrokeType& newType, const bool undoable);
|
||||
void setStrokeFill (const JucerFillType& newType, const bool undoable);
|
||||
|
||||
//==============================================================================
|
||||
Rectangle<int> getCurrentBounds (const Rectangle<int>& parentArea) const override;
|
||||
void setCurrentBounds (const Rectangle<int>& newBounds, const Rectangle<int>& parentArea, const bool undoable) override;
|
||||
|
||||
void createSiblingComponents() override;
|
||||
|
||||
//==============================================================================
|
||||
void addColourAttributes (XmlElement* const e) const;
|
||||
bool loadColourAttributes (const XmlElement& xml);
|
||||
|
||||
protected:
|
||||
JucerFillType fillType;
|
||||
|
||||
bool isStrokePresent;
|
||||
const bool showOutline, showJointAndEnd;
|
||||
StrokeType strokeType;
|
||||
|
||||
void convertToNewPathElement (const Path& path);
|
||||
};
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
class ElementSiblingComponent : public Component,
|
||||
public ChangeListener
|
||||
{
|
||||
public:
|
||||
ElementSiblingComponent (PaintElement* const owner_)
|
||||
: owner (owner_)
|
||||
{
|
||||
setAlwaysOnTop (true);
|
||||
|
||||
owner->getDocument()->addChangeListener (this);
|
||||
}
|
||||
|
||||
~ElementSiblingComponent() override
|
||||
{
|
||||
owner->getDocument()->removeChangeListener (this);
|
||||
}
|
||||
|
||||
virtual void updatePosition() = 0;
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
protected:
|
||||
PaintElement* const owner;
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
class ElementSiblingComponent : public Component,
|
||||
public ChangeListener
|
||||
{
|
||||
public:
|
||||
ElementSiblingComponent (PaintElement* const owner_)
|
||||
: owner (owner_)
|
||||
{
|
||||
setAlwaysOnTop (true);
|
||||
|
||||
owner->getDocument()->addChangeListener (this);
|
||||
}
|
||||
|
||||
~ElementSiblingComponent() override
|
||||
{
|
||||
owner->getDocument()->removeChangeListener (this);
|
||||
}
|
||||
|
||||
virtual void updatePosition() = 0;
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
protected:
|
||||
PaintElement* const owner;
|
||||
};
|
||||
|
||||
@@ -1,427 +1,427 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_JucerDocument.h"
|
||||
#include "../jucer_UtilityFunctions.h"
|
||||
#include "../../ProjectSaving/jucer_ResourceFile.h"
|
||||
|
||||
//==============================================================================
|
||||
class JucerFillType
|
||||
{
|
||||
public:
|
||||
JucerFillType()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
JucerFillType (const JucerFillType& other)
|
||||
{
|
||||
image = Image();
|
||||
mode = other.mode;
|
||||
colour = other.colour;
|
||||
gradCol1 = other.gradCol1;
|
||||
gradCol2 = other.gradCol2;
|
||||
gradPos1 = other.gradPos1;
|
||||
gradPos2 = other.gradPos2;
|
||||
imageResourceName = other.imageResourceName;
|
||||
imageOpacity = other.imageOpacity;
|
||||
imageAnchor = other.imageAnchor;
|
||||
}
|
||||
|
||||
JucerFillType& operator= (const JucerFillType& other)
|
||||
{
|
||||
image = Image();
|
||||
mode = other.mode;
|
||||
colour = other.colour;
|
||||
gradCol1 = other.gradCol1;
|
||||
gradCol2 = other.gradCol2;
|
||||
gradPos1 = other.gradPos1;
|
||||
gradPos2 = other.gradPos2;
|
||||
imageResourceName = other.imageResourceName;
|
||||
imageOpacity = other.imageOpacity;
|
||||
imageAnchor = other.imageAnchor;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator== (const JucerFillType& other) const
|
||||
{
|
||||
return mode == other.mode
|
||||
&& colour == other.colour
|
||||
&& gradCol1 == other.gradCol1
|
||||
&& gradCol2 == other.gradCol2
|
||||
&& gradPos1 == other.gradPos1
|
||||
&& gradPos2 == other.gradPos2
|
||||
&& imageResourceName == other.imageResourceName
|
||||
&& imageOpacity == other.imageOpacity
|
||||
&& imageAnchor == other.imageAnchor;
|
||||
}
|
||||
|
||||
bool operator!= (const JucerFillType& other) const
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setFillType (Graphics& g, JucerDocument* const document, const Rectangle<int>& parentArea)
|
||||
{
|
||||
if (document == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == solidColour)
|
||||
{
|
||||
image = Image();
|
||||
g.setColour (colour);
|
||||
}
|
||||
else if (mode == imageBrush)
|
||||
{
|
||||
loadImage (document);
|
||||
|
||||
Rectangle<int> r (imageAnchor.getRectangle (parentArea, document->getComponentLayout()));
|
||||
|
||||
g.setTiledImageFill (image, r.getX(), r.getY(), (float) imageOpacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = Image();
|
||||
|
||||
Rectangle<int> r1 (gradPos1.getRectangle (parentArea, document->getComponentLayout()));
|
||||
Rectangle<int> r2 (gradPos2.getRectangle (parentArea, document->getComponentLayout()));
|
||||
|
||||
g.setGradientFill (ColourGradient (gradCol1, (float) r1.getX(), (float) r1.getY(),
|
||||
gradCol2, (float) r2.getX(), (float) r2.getY(),
|
||||
mode == radialGradient));
|
||||
}
|
||||
}
|
||||
|
||||
String generateVariablesCode (String type) const
|
||||
{
|
||||
String s;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
s << "juce::Colour " << type << "Colour = " << CodeHelpers::colourToCode (colour) << ";\n";
|
||||
break;
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
s << "juce::Colour " << type << "Colour1 = " << CodeHelpers::colourToCode (gradCol1) << ", " << type << "Colour2 = " << CodeHelpers::colourToCode (gradCol2) << ";\n";
|
||||
break;
|
||||
|
||||
case imageBrush:
|
||||
break;
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void fillInGeneratedCode (String type, RelativePositionedRectangle relativeTo, GeneratedCode& code, String& paintMethodCode) const
|
||||
{
|
||||
String s;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
s << "g.setColour (" << type << "Colour);\n";
|
||||
break;
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
{
|
||||
String x0, y0, x1, y1, w, h, x2, y2;
|
||||
positionToCode (relativeTo, code.document->getComponentLayout(), x0, y0, w, h);
|
||||
positionToCode (gradPos1, code.document->getComponentLayout(), x1, y1, w, h);
|
||||
positionToCode (gradPos2, code.document->getComponentLayout(), x2, y2, w, h);
|
||||
|
||||
s << "g.setGradientFill (juce::ColourGradient (";
|
||||
|
||||
auto indent = String::repeatedString (" ", s.length());
|
||||
|
||||
s << type << "Colour1,\n"
|
||||
<< indent << castToFloat (x1) << " - " << castToFloat (x0) << " + x,\n"
|
||||
<< indent << castToFloat (y1) << " - " << castToFloat (y0) << " + y,\n"
|
||||
<< indent << type << "Colour2,\n"
|
||||
<< indent << castToFloat (x2) << " - " << castToFloat (x0) << " + x,\n"
|
||||
<< indent << castToFloat (y2) << " - " << castToFloat (y0) << " + y,\n"
|
||||
<< indent << CodeHelpers::boolLiteral (mode == radialGradient) << "));\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case imageBrush:
|
||||
{
|
||||
auto imageVariable = "cachedImage_" + imageResourceName.replace ("::", "_") + "_" + String (code.getUniqueSuffix());
|
||||
|
||||
code.addImageResourceLoader (imageVariable, imageResourceName);
|
||||
|
||||
String x0, y0, x1, y1, w, h;
|
||||
positionToCode (relativeTo, code.document->getComponentLayout(), x0, y0, w, h);
|
||||
positionToCode (imageAnchor, code.document->getComponentLayout(), x1, y1, w, h);
|
||||
|
||||
s << "g.setTiledImageFill (";
|
||||
|
||||
const String indent (String::repeatedString (" ", s.length()));
|
||||
|
||||
s << imageVariable << ",\n"
|
||||
<< indent << x1 << " - " << x0 << " + x,\n"
|
||||
<< indent << y1 << " - " << y0 << " + y,\n"
|
||||
<< indent << CodeHelpers::floatLiteral (imageOpacity, 4) << ");\n";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
paintMethodCode += s;
|
||||
}
|
||||
|
||||
String toString() const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
return "solid: " + colour.toString();
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
return (mode == linearGradient ? "linear: "
|
||||
: " radial: ")
|
||||
+ gradPos1.toString()
|
||||
+ ", "
|
||||
+ gradPos2.toString()
|
||||
+ ", 0=" + gradCol1.toString()
|
||||
+ ", 1=" + gradCol2.toString();
|
||||
|
||||
case imageBrush:
|
||||
return "image: " + imageResourceName.replaceCharacter (':', '#')
|
||||
+ ", "
|
||||
+ String (imageOpacity)
|
||||
+ ", "
|
||||
+ imageAnchor.toString();
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void restoreFromString (const String& s)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (s.isNotEmpty())
|
||||
{
|
||||
StringArray toks;
|
||||
toks.addTokens (s, ",:", StringRef());
|
||||
toks.trim();
|
||||
|
||||
if (toks[0] == "solid")
|
||||
{
|
||||
mode = solidColour;
|
||||
colour = Colour::fromString (toks[1]);
|
||||
}
|
||||
else if (toks[0] == "linear"
|
||||
|| toks[0] == "radial")
|
||||
{
|
||||
mode = (toks[0] == "linear") ? linearGradient : radialGradient;
|
||||
|
||||
gradPos1 = RelativePositionedRectangle();
|
||||
gradPos1.rect = PositionedRectangle (toks[1]);
|
||||
gradPos2 = RelativePositionedRectangle();
|
||||
gradPos2.rect = PositionedRectangle (toks[2]);
|
||||
|
||||
gradCol1 = Colour::fromString (toks[3].fromFirstOccurrenceOf ("=", false, false));
|
||||
gradCol2 = Colour::fromString (toks[4].fromFirstOccurrenceOf ("=", false, false));
|
||||
}
|
||||
else if (toks[0] == "image")
|
||||
{
|
||||
mode = imageBrush;
|
||||
imageResourceName = toks[1].replaceCharacter ('#', ':');
|
||||
imageOpacity = toks[2].getDoubleValue();
|
||||
imageAnchor= RelativePositionedRectangle();
|
||||
imageAnchor.rect = PositionedRectangle (toks[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isOpaque() const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
return colour.isOpaque();
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
return gradCol1.isOpaque() && gradCol2.isOpaque();
|
||||
|
||||
case imageBrush:
|
||||
return image.isValid()
|
||||
&& imageOpacity >= 1.0f
|
||||
&& ! image.hasAlphaChannel();
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInvisible() const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
return colour.isTransparent();
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
return gradCol1.isTransparent() && gradCol2.isTransparent();
|
||||
|
||||
case imageBrush:
|
||||
return imageOpacity == 0.0;
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
enum FillMode
|
||||
{
|
||||
solidColour,
|
||||
linearGradient,
|
||||
radialGradient,
|
||||
imageBrush
|
||||
};
|
||||
|
||||
FillMode mode;
|
||||
Colour colour, gradCol1, gradCol2;
|
||||
|
||||
// just the x, y, of these are used
|
||||
RelativePositionedRectangle gradPos1, gradPos2;
|
||||
|
||||
String imageResourceName;
|
||||
double imageOpacity;
|
||||
RelativePositionedRectangle imageAnchor;
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
Image image;
|
||||
|
||||
void reset()
|
||||
{
|
||||
image = Image();
|
||||
mode = solidColour;
|
||||
colour = Colours::brown.withHue (Random::getSystemRandom().nextFloat());
|
||||
gradCol1 = Colours::red;
|
||||
gradCol2 = Colours::green;
|
||||
gradPos1 = RelativePositionedRectangle();
|
||||
gradPos1.rect = PositionedRectangle ("50 50");
|
||||
gradPos2 = RelativePositionedRectangle();
|
||||
gradPos2.rect = PositionedRectangle ("100 100");
|
||||
|
||||
imageResourceName.clear();
|
||||
imageOpacity = 1.0;
|
||||
imageAnchor = RelativePositionedRectangle();
|
||||
imageAnchor.rect = PositionedRectangle ("0 0");
|
||||
}
|
||||
|
||||
void loadImage (JucerDocument* const document)
|
||||
{
|
||||
if (image.isNull())
|
||||
{
|
||||
if (document != nullptr)
|
||||
{
|
||||
if (imageResourceName.contains ("::"))
|
||||
{
|
||||
if (Project* project = document->getCppDocument().getProject())
|
||||
{
|
||||
JucerResourceFile resourceFile (*project);
|
||||
|
||||
for (int i = 0; i < resourceFile.getNumFiles(); ++i)
|
||||
{
|
||||
const File& file = resourceFile.getFile(i);
|
||||
|
||||
if (imageResourceName == resourceFile.getClassName() + "::" + resourceFile.getDataVariableFor (file))
|
||||
{
|
||||
image = ImageCache::getFromFile (file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
image = document->getResources().getImageFromCache (imageResourceName);
|
||||
}
|
||||
}
|
||||
|
||||
if (image.isNull())
|
||||
{
|
||||
const int hashCode = 0x3437856f;
|
||||
image = ImageCache::getFromHashCode (hashCode);
|
||||
|
||||
if (image.isNull())
|
||||
{
|
||||
image = Image (Image::RGB, 100, 100, true);
|
||||
|
||||
Graphics g (image);
|
||||
g.fillCheckerBoard (image.getBounds().toFloat(),
|
||||
(float) image.getWidth() * 0.5f, (float) image.getHeight() * 0.5f,
|
||||
Colours::white, Colours::lightgrey);
|
||||
|
||||
g.setFont (12.0f);
|
||||
g.setColour (Colours::grey);
|
||||
g.drawText ("(image missing)", 0, 0, image.getWidth(), image.getHeight() / 2, Justification::centred, true);
|
||||
|
||||
ImageCache::addImageToCache (image, hashCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_JucerDocument.h"
|
||||
#include "../jucer_UtilityFunctions.h"
|
||||
#include "../../ProjectSaving/jucer_ResourceFile.h"
|
||||
|
||||
//==============================================================================
|
||||
class JucerFillType
|
||||
{
|
||||
public:
|
||||
JucerFillType()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
JucerFillType (const JucerFillType& other)
|
||||
{
|
||||
image = Image();
|
||||
mode = other.mode;
|
||||
colour = other.colour;
|
||||
gradCol1 = other.gradCol1;
|
||||
gradCol2 = other.gradCol2;
|
||||
gradPos1 = other.gradPos1;
|
||||
gradPos2 = other.gradPos2;
|
||||
imageResourceName = other.imageResourceName;
|
||||
imageOpacity = other.imageOpacity;
|
||||
imageAnchor = other.imageAnchor;
|
||||
}
|
||||
|
||||
JucerFillType& operator= (const JucerFillType& other)
|
||||
{
|
||||
image = Image();
|
||||
mode = other.mode;
|
||||
colour = other.colour;
|
||||
gradCol1 = other.gradCol1;
|
||||
gradCol2 = other.gradCol2;
|
||||
gradPos1 = other.gradPos1;
|
||||
gradPos2 = other.gradPos2;
|
||||
imageResourceName = other.imageResourceName;
|
||||
imageOpacity = other.imageOpacity;
|
||||
imageAnchor = other.imageAnchor;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator== (const JucerFillType& other) const
|
||||
{
|
||||
return mode == other.mode
|
||||
&& colour == other.colour
|
||||
&& gradCol1 == other.gradCol1
|
||||
&& gradCol2 == other.gradCol2
|
||||
&& gradPos1 == other.gradPos1
|
||||
&& gradPos2 == other.gradPos2
|
||||
&& imageResourceName == other.imageResourceName
|
||||
&& imageOpacity == other.imageOpacity
|
||||
&& imageAnchor == other.imageAnchor;
|
||||
}
|
||||
|
||||
bool operator!= (const JucerFillType& other) const
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setFillType (Graphics& g, JucerDocument* const document, const Rectangle<int>& parentArea)
|
||||
{
|
||||
if (document == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == solidColour)
|
||||
{
|
||||
image = Image();
|
||||
g.setColour (colour);
|
||||
}
|
||||
else if (mode == imageBrush)
|
||||
{
|
||||
loadImage (document);
|
||||
|
||||
Rectangle<int> r (imageAnchor.getRectangle (parentArea, document->getComponentLayout()));
|
||||
|
||||
g.setTiledImageFill (image, r.getX(), r.getY(), (float) imageOpacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = Image();
|
||||
|
||||
Rectangle<int> r1 (gradPos1.getRectangle (parentArea, document->getComponentLayout()));
|
||||
Rectangle<int> r2 (gradPos2.getRectangle (parentArea, document->getComponentLayout()));
|
||||
|
||||
g.setGradientFill (ColourGradient (gradCol1, (float) r1.getX(), (float) r1.getY(),
|
||||
gradCol2, (float) r2.getX(), (float) r2.getY(),
|
||||
mode == radialGradient));
|
||||
}
|
||||
}
|
||||
|
||||
String generateVariablesCode (String type) const
|
||||
{
|
||||
String s;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
s << "juce::Colour " << type << "Colour = " << CodeHelpers::colourToCode (colour) << ";\n";
|
||||
break;
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
s << "juce::Colour " << type << "Colour1 = " << CodeHelpers::colourToCode (gradCol1) << ", " << type << "Colour2 = " << CodeHelpers::colourToCode (gradCol2) << ";\n";
|
||||
break;
|
||||
|
||||
case imageBrush:
|
||||
break;
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void fillInGeneratedCode (String type, RelativePositionedRectangle relativeTo, GeneratedCode& code, String& paintMethodCode) const
|
||||
{
|
||||
String s;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
s << "g.setColour (" << type << "Colour);\n";
|
||||
break;
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
{
|
||||
String x0, y0, x1, y1, w, h, x2, y2;
|
||||
positionToCode (relativeTo, code.document->getComponentLayout(), x0, y0, w, h);
|
||||
positionToCode (gradPos1, code.document->getComponentLayout(), x1, y1, w, h);
|
||||
positionToCode (gradPos2, code.document->getComponentLayout(), x2, y2, w, h);
|
||||
|
||||
s << "g.setGradientFill (juce::ColourGradient (";
|
||||
|
||||
auto indent = String::repeatedString (" ", s.length());
|
||||
|
||||
s << type << "Colour1,\n"
|
||||
<< indent << castToFloat (x1) << " - " << castToFloat (x0) << " + x,\n"
|
||||
<< indent << castToFloat (y1) << " - " << castToFloat (y0) << " + y,\n"
|
||||
<< indent << type << "Colour2,\n"
|
||||
<< indent << castToFloat (x2) << " - " << castToFloat (x0) << " + x,\n"
|
||||
<< indent << castToFloat (y2) << " - " << castToFloat (y0) << " + y,\n"
|
||||
<< indent << CodeHelpers::boolLiteral (mode == radialGradient) << "));\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case imageBrush:
|
||||
{
|
||||
auto imageVariable = "cachedImage_" + imageResourceName.replace ("::", "_") + "_" + String (code.getUniqueSuffix());
|
||||
|
||||
code.addImageResourceLoader (imageVariable, imageResourceName);
|
||||
|
||||
String x0, y0, x1, y1, w, h;
|
||||
positionToCode (relativeTo, code.document->getComponentLayout(), x0, y0, w, h);
|
||||
positionToCode (imageAnchor, code.document->getComponentLayout(), x1, y1, w, h);
|
||||
|
||||
s << "g.setTiledImageFill (";
|
||||
|
||||
const String indent (String::repeatedString (" ", s.length()));
|
||||
|
||||
s << imageVariable << ",\n"
|
||||
<< indent << x1 << " - " << x0 << " + x,\n"
|
||||
<< indent << y1 << " - " << y0 << " + y,\n"
|
||||
<< indent << CodeHelpers::floatLiteral (imageOpacity, 4) << ");\n";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
paintMethodCode += s;
|
||||
}
|
||||
|
||||
String toString() const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
return "solid: " + colour.toString();
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
return (mode == linearGradient ? "linear: "
|
||||
: " radial: ")
|
||||
+ gradPos1.toString()
|
||||
+ ", "
|
||||
+ gradPos2.toString()
|
||||
+ ", 0=" + gradCol1.toString()
|
||||
+ ", 1=" + gradCol2.toString();
|
||||
|
||||
case imageBrush:
|
||||
return "image: " + imageResourceName.replaceCharacter (':', '#')
|
||||
+ ", "
|
||||
+ String (imageOpacity)
|
||||
+ ", "
|
||||
+ imageAnchor.toString();
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void restoreFromString (const String& s)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (s.isNotEmpty())
|
||||
{
|
||||
StringArray toks;
|
||||
toks.addTokens (s, ",:", StringRef());
|
||||
toks.trim();
|
||||
|
||||
if (toks[0] == "solid")
|
||||
{
|
||||
mode = solidColour;
|
||||
colour = Colour::fromString (toks[1]);
|
||||
}
|
||||
else if (toks[0] == "linear"
|
||||
|| toks[0] == "radial")
|
||||
{
|
||||
mode = (toks[0] == "linear") ? linearGradient : radialGradient;
|
||||
|
||||
gradPos1 = RelativePositionedRectangle();
|
||||
gradPos1.rect = PositionedRectangle (toks[1]);
|
||||
gradPos2 = RelativePositionedRectangle();
|
||||
gradPos2.rect = PositionedRectangle (toks[2]);
|
||||
|
||||
gradCol1 = Colour::fromString (toks[3].fromFirstOccurrenceOf ("=", false, false));
|
||||
gradCol2 = Colour::fromString (toks[4].fromFirstOccurrenceOf ("=", false, false));
|
||||
}
|
||||
else if (toks[0] == "image")
|
||||
{
|
||||
mode = imageBrush;
|
||||
imageResourceName = toks[1].replaceCharacter ('#', ':');
|
||||
imageOpacity = toks[2].getDoubleValue();
|
||||
imageAnchor= RelativePositionedRectangle();
|
||||
imageAnchor.rect = PositionedRectangle (toks[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isOpaque() const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
return colour.isOpaque();
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
return gradCol1.isOpaque() && gradCol2.isOpaque();
|
||||
|
||||
case imageBrush:
|
||||
return image.isValid()
|
||||
&& imageOpacity >= 1.0f
|
||||
&& ! image.hasAlphaChannel();
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInvisible() const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case solidColour:
|
||||
return colour.isTransparent();
|
||||
|
||||
case linearGradient:
|
||||
case radialGradient:
|
||||
return gradCol1.isTransparent() && gradCol2.isTransparent();
|
||||
|
||||
case imageBrush:
|
||||
return imageOpacity == 0.0;
|
||||
|
||||
default:
|
||||
jassertfalse;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
enum FillMode
|
||||
{
|
||||
solidColour,
|
||||
linearGradient,
|
||||
radialGradient,
|
||||
imageBrush
|
||||
};
|
||||
|
||||
FillMode mode;
|
||||
Colour colour, gradCol1, gradCol2;
|
||||
|
||||
// just the x, y, of these are used
|
||||
RelativePositionedRectangle gradPos1, gradPos2;
|
||||
|
||||
String imageResourceName;
|
||||
double imageOpacity;
|
||||
RelativePositionedRectangle imageAnchor;
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
Image image;
|
||||
|
||||
void reset()
|
||||
{
|
||||
image = Image();
|
||||
mode = solidColour;
|
||||
colour = Colours::brown.withHue (Random::getSystemRandom().nextFloat());
|
||||
gradCol1 = Colours::red;
|
||||
gradCol2 = Colours::green;
|
||||
gradPos1 = RelativePositionedRectangle();
|
||||
gradPos1.rect = PositionedRectangle ("50 50");
|
||||
gradPos2 = RelativePositionedRectangle();
|
||||
gradPos2.rect = PositionedRectangle ("100 100");
|
||||
|
||||
imageResourceName.clear();
|
||||
imageOpacity = 1.0;
|
||||
imageAnchor = RelativePositionedRectangle();
|
||||
imageAnchor.rect = PositionedRectangle ("0 0");
|
||||
}
|
||||
|
||||
void loadImage (JucerDocument* const document)
|
||||
{
|
||||
if (image.isNull())
|
||||
{
|
||||
if (document != nullptr)
|
||||
{
|
||||
if (imageResourceName.contains ("::"))
|
||||
{
|
||||
if (Project* project = document->getCppDocument().getProject())
|
||||
{
|
||||
JucerResourceFile resourceFile (*project);
|
||||
|
||||
for (int i = 0; i < resourceFile.getNumFiles(); ++i)
|
||||
{
|
||||
const File& file = resourceFile.getFile(i);
|
||||
|
||||
if (imageResourceName == resourceFile.getClassName() + "::" + resourceFile.getDataVariableFor (file))
|
||||
{
|
||||
image = ImageCache::getFromFile (file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
image = document->getResources().getImageFromCache (imageResourceName);
|
||||
}
|
||||
}
|
||||
|
||||
if (image.isNull())
|
||||
{
|
||||
const int hashCode = 0x3437856f;
|
||||
image = ImageCache::getFromHashCode (hashCode);
|
||||
|
||||
if (image.isNull())
|
||||
{
|
||||
image = Image (Image::RGB, 100, 100, true);
|
||||
|
||||
Graphics g (image);
|
||||
g.fillCheckerBoard (image.getBounds().toFloat(),
|
||||
(float) image.getWidth() * 0.5f, (float) image.getHeight() * 0.5f,
|
||||
Colours::white, Colours::lightgrey);
|
||||
|
||||
g.setFont (12.0f);
|
||||
g.setColour (Colours::grey);
|
||||
g.drawText ("(image missing)", 0, 0, image.getWidth(), image.getHeight() / 2, Justification::centred, true);
|
||||
|
||||
ImageCache::addImageToCache (image, hashCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_PointComponent.h"
|
||||
#include "jucer_ColouredElement.h"
|
||||
|
||||
//==============================================================================
|
||||
class GradientPointComponent : public PointComponent
|
||||
{
|
||||
public:
|
||||
GradientPointComponent (ColouredElement* const owner_,
|
||||
const bool isStroke_,
|
||||
const bool isStart_)
|
||||
: PointComponent (owner_),
|
||||
isStroke (isStroke_),
|
||||
isStart (isStart_)
|
||||
{
|
||||
}
|
||||
|
||||
RelativePositionedRectangle getPosition()
|
||||
{
|
||||
ColouredElement* e = dynamic_cast<ColouredElement*> (owner);
|
||||
|
||||
if (isStroke)
|
||||
return isStart ? e->getStrokeType().fill.gradPos1
|
||||
: e->getStrokeType().fill.gradPos2;
|
||||
|
||||
return isStart ? e->getFillType().gradPos1
|
||||
: e->getFillType().gradPos2;
|
||||
}
|
||||
|
||||
void setPosition (const RelativePositionedRectangle& newPos)
|
||||
{
|
||||
ColouredElement* e = dynamic_cast<ColouredElement*> (owner);
|
||||
|
||||
if (isStroke)
|
||||
{
|
||||
JucerFillType f (e->getStrokeType().fill);
|
||||
|
||||
if (isStart)
|
||||
f.gradPos1 = newPos;
|
||||
else
|
||||
f.gradPos2 = newPos;
|
||||
|
||||
e->setStrokeFill (f, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
JucerFillType f (e->getFillType());
|
||||
|
||||
if (isStart)
|
||||
f.gradPos1 = newPos;
|
||||
else
|
||||
f.gradPos2 = newPos;
|
||||
|
||||
e->setFillType (f, true);
|
||||
}
|
||||
}
|
||||
|
||||
void updatePosition()
|
||||
{
|
||||
PointComponent::updatePosition();
|
||||
|
||||
ColouredElement* e = dynamic_cast<ColouredElement*> (owner);
|
||||
|
||||
JucerFillType f (isStroke ? e->getStrokeType().fill
|
||||
: e->getFillType());
|
||||
|
||||
setVisible (f.mode == JucerFillType::linearGradient
|
||||
|| f.mode == JucerFillType::radialGradient);
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStroke, isStart;
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_PointComponent.h"
|
||||
#include "jucer_ColouredElement.h"
|
||||
|
||||
//==============================================================================
|
||||
class GradientPointComponent : public PointComponent
|
||||
{
|
||||
public:
|
||||
GradientPointComponent (ColouredElement* const owner_,
|
||||
const bool isStroke_,
|
||||
const bool isStart_)
|
||||
: PointComponent (owner_),
|
||||
isStroke (isStroke_),
|
||||
isStart (isStart_)
|
||||
{
|
||||
}
|
||||
|
||||
RelativePositionedRectangle getPosition()
|
||||
{
|
||||
ColouredElement* e = dynamic_cast<ColouredElement*> (owner);
|
||||
|
||||
if (isStroke)
|
||||
return isStart ? e->getStrokeType().fill.gradPos1
|
||||
: e->getStrokeType().fill.gradPos2;
|
||||
|
||||
return isStart ? e->getFillType().gradPos1
|
||||
: e->getFillType().gradPos2;
|
||||
}
|
||||
|
||||
void setPosition (const RelativePositionedRectangle& newPos)
|
||||
{
|
||||
ColouredElement* e = dynamic_cast<ColouredElement*> (owner);
|
||||
|
||||
if (isStroke)
|
||||
{
|
||||
JucerFillType f (e->getStrokeType().fill);
|
||||
|
||||
if (isStart)
|
||||
f.gradPos1 = newPos;
|
||||
else
|
||||
f.gradPos2 = newPos;
|
||||
|
||||
e->setStrokeFill (f, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
JucerFillType f (e->getFillType());
|
||||
|
||||
if (isStart)
|
||||
f.gradPos1 = newPos;
|
||||
else
|
||||
f.gradPos2 = newPos;
|
||||
|
||||
e->setFillType (f, true);
|
||||
}
|
||||
}
|
||||
|
||||
void updatePosition()
|
||||
{
|
||||
PointComponent::updatePosition();
|
||||
|
||||
ColouredElement* e = dynamic_cast<ColouredElement*> (owner);
|
||||
|
||||
JucerFillType f (isStroke ? e->getStrokeType().fill
|
||||
: e->getFillType());
|
||||
|
||||
setVisible (f.mode == JucerFillType::linearGradient
|
||||
|| f.mode == JucerFillType::radialGradient);
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStroke, isStart;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user