mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
386 lines
12 KiB
C++
386 lines
12 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2017 - ROLI Ltd.
|
|
|
|
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 5 End-User License
|
|
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
|
27th April 2017).
|
|
|
|
End User License Agreement: www.juce.com/juce-5-licence
|
|
Privacy Policy: www.juce.com/juce-5-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 "../JuceDemoHeader.h"
|
|
|
|
//==============================================================================
|
|
class XmlTreeItem : public TreeViewItem
|
|
{
|
|
public:
|
|
XmlTreeItem (XmlElement& x) : xml (x)
|
|
{
|
|
}
|
|
|
|
String getUniqueName() const override
|
|
{
|
|
if (xml.getTagName().isEmpty())
|
|
return "unknown";
|
|
|
|
return xml.getTagName();
|
|
}
|
|
|
|
bool mightContainSubItems() override
|
|
{
|
|
return xml.getFirstChildElement() != nullptr;
|
|
}
|
|
|
|
void paintItem (Graphics& g, int width, int height) override
|
|
{
|
|
// if this item is selected, fill it with a background colour..
|
|
if (isSelected())
|
|
g.fillAll (Colours::blue.withAlpha (0.3f));
|
|
|
|
// use a "colour" attribute in the xml tag for this node to set the text colour..
|
|
g.setColour (Colour::fromString (xml.getStringAttribute ("colour", "ff000000")));
|
|
g.setFont (height * 0.7f);
|
|
|
|
// draw the xml element's tag name..
|
|
g.drawText (xml.getTagName(),
|
|
4, 0, width - 4, height,
|
|
Justification::centredLeft, true);
|
|
}
|
|
|
|
void itemOpennessChanged (bool isNowOpen) override
|
|
{
|
|
if (isNowOpen)
|
|
{
|
|
// if we've not already done so, we'll now add the tree's sub-items. You could
|
|
// also choose to delete the existing ones and refresh them if that's more suitable
|
|
// in your app.
|
|
if (getNumSubItems() == 0)
|
|
{
|
|
// create and add sub-items to this node of the tree, corresponding to
|
|
// each sub-element in the XML..
|
|
|
|
forEachXmlChildElement (xml, child)
|
|
{
|
|
jassert (child != nullptr);
|
|
addSubItem (new XmlTreeItem (*child));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// in this case, we'll leave any sub-items in the tree when the node gets closed,
|
|
// though you could choose to delete them if that's more appropriate for
|
|
// your application.
|
|
}
|
|
}
|
|
|
|
private:
|
|
XmlElement& xml;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlTreeItem)
|
|
};
|
|
|
|
//==============================================================================
|
|
class JsonTreeItem : public TreeViewItem
|
|
{
|
|
public:
|
|
JsonTreeItem (Identifier i, var value) : identifier (i), json (value)
|
|
{
|
|
}
|
|
|
|
String getUniqueName() const override
|
|
{
|
|
return identifier.toString() + "_id";
|
|
}
|
|
|
|
bool mightContainSubItems() override
|
|
{
|
|
if (DynamicObject* obj = json.getDynamicObject())
|
|
return obj->getProperties().size() > 0;
|
|
|
|
return json.isArray();
|
|
}
|
|
|
|
void paintItem (Graphics& g, int width, int height) override
|
|
{
|
|
// if this item is selected, fill it with a background colour..
|
|
if (isSelected())
|
|
g.fillAll (Colours::blue.withAlpha (0.3f));
|
|
|
|
g.setColour (Colours::black);
|
|
g.setFont (height * 0.7f);
|
|
|
|
// draw the element's tag name..
|
|
g.drawText (getText(),
|
|
4, 0, width - 4, height,
|
|
Justification::centredLeft, true);
|
|
}
|
|
|
|
void itemOpennessChanged (bool isNowOpen) override
|
|
{
|
|
if (isNowOpen)
|
|
{
|
|
// if we've not already done so, we'll now add the tree's sub-items. You could
|
|
// also choose to delete the existing ones and refresh them if that's more suitable
|
|
// in your app.
|
|
if (getNumSubItems() == 0)
|
|
{
|
|
// create and add sub-items to this node of the tree, corresponding to
|
|
// the type of object this var represents
|
|
|
|
if (json.isArray())
|
|
{
|
|
for (int i = 0; i < json.size(); ++i)
|
|
{
|
|
var& child (json[i]);
|
|
jassert (! child.isVoid());
|
|
addSubItem (new JsonTreeItem (Identifier(), child));
|
|
}
|
|
}
|
|
else if (DynamicObject* obj = json.getDynamicObject())
|
|
{
|
|
NamedValueSet& props (obj->getProperties());
|
|
|
|
for (int i = 0; i < props.size(); ++i)
|
|
{
|
|
const Identifier id (props.getName (i));
|
|
var child (props[id]);
|
|
jassert (! child.isVoid());
|
|
addSubItem (new JsonTreeItem (id, child));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// in this case, we'll leave any sub-items in the tree when the node gets closed,
|
|
// though you could choose to delete them if that's more appropriate for
|
|
// your application.
|
|
}
|
|
}
|
|
|
|
private:
|
|
Identifier identifier;
|
|
var json;
|
|
|
|
/** Returns the text to display in the tree.
|
|
This is a little more complex for JSON than XML as nodes can be strings, objects or arrays.
|
|
*/
|
|
String getText() const
|
|
{
|
|
String text;
|
|
|
|
if (identifier.isValid())
|
|
text << identifier.toString();
|
|
|
|
if (! json.isVoid())
|
|
{
|
|
if (text.isNotEmpty() && (! json.isArray()))
|
|
text << ": ";
|
|
|
|
if (json.isObject() && (! identifier.isValid()))
|
|
text << "[Array]";
|
|
else if (! json.isArray())
|
|
text << json.toString();
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JsonTreeItem)
|
|
};
|
|
|
|
//==============================================================================
|
|
class StringsDemo : public Component,
|
|
private ComboBox::Listener,
|
|
private CodeDocument::Listener
|
|
{
|
|
public:
|
|
/** The type of database to parse. */
|
|
enum Type
|
|
{
|
|
xml,
|
|
json
|
|
};
|
|
|
|
StringsDemo()
|
|
: codeDocumentComponent (codeDocument, nullptr)
|
|
{
|
|
setOpaque (true);
|
|
|
|
addAndMakeVisible (typeBox);
|
|
typeBox.addListener (this);
|
|
typeBox.addItem ("XML", 1);
|
|
typeBox.addItem ("JSON", 2);
|
|
|
|
comboBoxLabel.setText ("Database Type:", dontSendNotification);
|
|
comboBoxLabel.attachToComponent (&typeBox, true);
|
|
|
|
addAndMakeVisible (codeDocumentComponent);
|
|
codeDocument.addListener (this);
|
|
|
|
addAndMakeVisible (resultsTree);
|
|
resultsTree.setColour (TreeView::backgroundColourId, Colours::white);
|
|
resultsTree.setDefaultOpenness (true);
|
|
|
|
addAndMakeVisible (errorMessage);
|
|
errorMessage.setReadOnly (true);
|
|
errorMessage.setMultiLine (true);
|
|
errorMessage.setCaretVisible (false);
|
|
errorMessage.setColour (TextEditor::outlineColourId, Colours::transparentWhite);
|
|
errorMessage.setColour (TextEditor::shadowColourId, Colours::transparentWhite);
|
|
|
|
typeBox.setSelectedId (1);
|
|
}
|
|
|
|
~StringsDemo()
|
|
{
|
|
resultsTree.setRootItem (nullptr);
|
|
}
|
|
|
|
void paint (Graphics& g) override
|
|
{
|
|
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
Rectangle<int> area (getLocalBounds());
|
|
typeBox.setBounds (area.removeFromTop (36).removeFromRight (150).reduced (8));
|
|
codeDocumentComponent.setBounds (area.removeFromTop(area.getHeight() / 2).reduced (8));
|
|
resultsTree.setBounds (area.reduced (8));
|
|
errorMessage.setBounds (resultsTree.getBounds());
|
|
}
|
|
|
|
private:
|
|
ComboBox typeBox;
|
|
Label comboBoxLabel;
|
|
CodeDocument codeDocument;
|
|
CodeEditorComponent codeDocumentComponent;
|
|
TreeView resultsTree;
|
|
|
|
ScopedPointer<TreeViewItem> rootItem;
|
|
ScopedPointer<XmlElement> parsedXml;
|
|
TextEditor errorMessage;
|
|
|
|
void rebuildTree()
|
|
{
|
|
ScopedPointer<XmlElement> openness;
|
|
|
|
if (rootItem != nullptr)
|
|
openness = rootItem->getOpennessState();
|
|
|
|
createNewRootNode();
|
|
|
|
if (openness != nullptr && rootItem != nullptr)
|
|
rootItem->restoreOpennessState (*openness);
|
|
}
|
|
|
|
void createNewRootNode()
|
|
{
|
|
// clear the current tree
|
|
resultsTree.setRootItem (nullptr);
|
|
rootItem = nullptr;
|
|
|
|
// try and parse the editor's contents
|
|
switch (typeBox.getSelectedItemIndex())
|
|
{
|
|
case xml: rootItem = rebuildXml(); break;
|
|
case json: rootItem = rebuildJson(); break;
|
|
default: rootItem = nullptr; break;
|
|
}
|
|
|
|
// if we have a valid TreeViewItem hide any old error messages and set our TreeView to use it
|
|
if (rootItem != nullptr)
|
|
errorMessage.clear();
|
|
|
|
errorMessage.setVisible (! errorMessage.isEmpty());
|
|
|
|
resultsTree.setRootItem (rootItem);
|
|
}
|
|
|
|
/** Parses the editors contects as XML. */
|
|
TreeViewItem* rebuildXml()
|
|
{
|
|
parsedXml = nullptr;
|
|
|
|
XmlDocument doc (codeDocument.getAllContent());
|
|
parsedXml = doc.getDocumentElement();
|
|
|
|
if (parsedXml == nullptr)
|
|
{
|
|
String error (doc.getLastParseError());
|
|
|
|
if (error.isEmpty())
|
|
error = "Unknown error";
|
|
|
|
errorMessage.setText ("Error parsing XML: " + error, dontSendNotification);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
return new XmlTreeItem (*parsedXml);
|
|
}
|
|
|
|
/** Parses the editors contects as JSON. */
|
|
TreeViewItem* rebuildJson()
|
|
{
|
|
var parsedJson;
|
|
Result result = JSON::parse (codeDocument.getAllContent(), parsedJson);
|
|
|
|
if (! result.wasOk())
|
|
{
|
|
errorMessage.setText ("Error parsing JSON: " + result.getErrorMessage());
|
|
return nullptr;
|
|
}
|
|
|
|
return new JsonTreeItem (Identifier(), parsedJson);
|
|
}
|
|
|
|
/** Clears the editor and loads some default text. */
|
|
void reset (Type type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case xml: codeDocument.replaceAllContent (BinaryData::treedemo_xml); break;
|
|
case json: codeDocument.replaceAllContent (BinaryData::juce_module_info); break;
|
|
default: codeDocument.replaceAllContent (String()); break;
|
|
}
|
|
}
|
|
|
|
void comboBoxChanged (ComboBox* box) override
|
|
{
|
|
if (box == &typeBox)
|
|
{
|
|
if (typeBox.getSelectedId() == 1)
|
|
reset (xml);
|
|
else
|
|
reset (json);
|
|
}
|
|
}
|
|
|
|
void codeDocumentTextInserted (const String&, int) override { rebuildTree(); }
|
|
void codeDocumentTextDeleted (int, int) override { rebuildTree(); }
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringsDemo)
|
|
};
|
|
|
|
|
|
// This static object will register this demo type in a global list of demos..
|
|
static JuceDemoType<StringsDemo> demo ("40 XML & JSON");
|