migrating to the latest JUCE version

This commit is contained in:
2022-11-04 23:11:33 +01:00
committed by Nikolai Rodionov
parent 4257a0f8ba
commit faf8f18333
2796 changed files with 888518 additions and 784244 deletions

View File

@ -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)
};

View File

@ -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
}
};
}
}

View File

@ -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);
});
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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;
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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;
}
};

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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)
};

View File

@ -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

View File

@ -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 };

View File

@ -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;

View File

@ -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"

View File

@ -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

View File

@ -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)
};

View File

@ -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

View File

@ -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%%)
};

View File

@ -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.
}

View File

@ -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%%)
};

View File

@ -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%%)
};

View File

@ -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.
}

View File

@ -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%%)
};

View File

@ -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%%>();
}

View File

@ -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%%)
};

View File

@ -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;
}

View File

@ -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%%)
};

View File

@ -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..
}

View File

@ -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%%)
};

View File

@ -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%%();
}

View File

@ -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%%)
};

View File

@ -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]

View File

@ -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]

View File

@ -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%%)
};

View File

@ -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.
}

View File

@ -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%%)
};

View File

@ -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%%)
};

View File

@ -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;
}

View File

@ -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%%)

View File

@ -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%%)

View File

@ -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..
}

View File

@ -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%%)
};

View File

@ -1,11 +1,11 @@
/*
==============================================================================
%%filename%%
Created: %%date%%
Author: %%author%%
==============================================================================
*/
%%include_corresponding_header%%
/*
==============================================================================
%%filename%%
Created: %%date%%
Author: %%author%%
==============================================================================
*/
%%include_corresponding_header%%

View File

@ -1,11 +1,11 @@
/*
==============================================================================
%%filename%%
Created: %%date%%
Author: %%author%%
==============================================================================
*/
#pragma once
/*
==============================================================================
%%filename%%
Created: %%date%%
Author: %%author%%
==============================================================================
*/
#pragma once

View File

@ -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%%)
};

View File

@ -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%%)
};

View File

@ -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.
}

View File

@ -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%%)
};

View File

@ -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%%)
};

View File

@ -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%%

View File

@ -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;
}
}

View File

@ -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)
};

View File

@ -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

View File

@ -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

View File

@ -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)
};

View File

@ -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;
};
};
};

View File

@ -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;
};
};
};

View File

@ -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;
};
};

View File

@ -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)
};

View File

@ -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)
};

View File

@ -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;
};
};
};

View File

@ -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;
};
};
};

View File

@ -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;
};
};
};

View File

@ -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;
};
};
};

View File

@ -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;
}
};

View File

@ -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;
};
};
};

View File

@ -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;
};
};
};

View File

@ -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;
};
};
};

View File

@ -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";
}
}

View File

@ -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];
};

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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);
};

View File

@ -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;
};

View File

@ -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);
}
}
}
}
};

View File

@ -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