1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00
JUCE/examples/Demo/Source/Demos/XMLandJSONDemo.cpp

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");