2297 lines
68 KiB
C++
2297 lines
68 KiB
C++
// This file is part of VSTGUI. It is subject to the license terms
|
|
// in the LICENSE file found in the top-level directory of this
|
|
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
|
|
|
|
#include "uidescription.h"
|
|
#include "uidescriptionaddonregistry.h"
|
|
#include "uidescriptionlistener.h"
|
|
#include "uiattributes.h"
|
|
#include "uiviewfactory.h"
|
|
#include "uiviewcreator.h"
|
|
#include "cstream.h"
|
|
#include "base64codec.h"
|
|
#include "uicontentprovider.h"
|
|
#include "uiviewswitchcontainer.h"
|
|
#include "icontroller.h"
|
|
#include "xmlparser.h"
|
|
#include "../lib/cfont.h"
|
|
#include "../lib/cstring.h"
|
|
#include "../lib/cframe.h"
|
|
#include "../lib/cdrawcontext.h"
|
|
#include "../lib/cgradient.h"
|
|
#include "../lib/cgraphicspath.h"
|
|
#include "../lib/cbitmap.h"
|
|
#include "../lib/cbitmapfilter.h"
|
|
#include "../lib/dispatchlist.h"
|
|
#include "../lib/platform/std_unorderedmap.h"
|
|
#include "../lib/platform/iplatformbitmap.h"
|
|
#include "../lib/platform/iplatformfont.h"
|
|
#include "detail/locale.h"
|
|
#include "detail/parsecolor.h"
|
|
#include "detail/scalefactorutils.h"
|
|
#include "detail/uidesclist.h"
|
|
#include "detail/uijsonpersistence.h"
|
|
#include "detail/uinode.h"
|
|
#include "detail/uiviewcreatorattributes.h"
|
|
#include "detail/uixmlpersistence.h"
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <deque>
|
|
|
|
namespace VSTGUI {
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/// @cond ignore
|
|
//-----------------------------------------------------------------------------
|
|
template <class T> class ScopePointer
|
|
{
|
|
public:
|
|
ScopePointer (T** pointer, T* obj) : pointer (pointer), oldObject (nullptr)
|
|
{
|
|
if (pointer)
|
|
{
|
|
oldObject = *pointer;
|
|
*pointer = obj;
|
|
}
|
|
}
|
|
~ScopePointer () noexcept
|
|
{
|
|
if (pointer)
|
|
*pointer = oldObject;
|
|
}
|
|
protected:
|
|
T** pointer;
|
|
T* oldObject;
|
|
};
|
|
|
|
|
|
/// @endcond
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static UIViewFactory* getGenericViewFactory ()
|
|
{
|
|
static UIViewFactory genericViewFactory;
|
|
return &genericViewFactory;
|
|
}
|
|
|
|
IdStringPtr IUIDescription::kCustomViewName = "custom-view-name";
|
|
|
|
//-----------------------------------------------------------------------------
|
|
struct UIDescription::Impl : ListenerProvider<Impl, UIDescriptionListener>
|
|
{
|
|
using UINode = Detail::UINode;
|
|
|
|
CResourceDescription uidescFile;
|
|
std::string filePath;
|
|
|
|
mutable IController* controller {nullptr};
|
|
IViewFactory* viewFactory {nullptr};
|
|
IContentProvider* contentProvider {nullptr};
|
|
IBitmapCreator* bitmapCreator { nullptr};
|
|
IBitmapCreator2* bitmapCreator2 { nullptr};
|
|
AttributeSaveFilterFunc attributeSaveFilterFunc {nullptr};
|
|
|
|
SharedPointer<UINode> nodes;
|
|
SharedPointer<UIDescription> sharedResources;
|
|
|
|
mutable std::deque<IController*> subControllerStack;
|
|
|
|
Optional<UINode*> variableBaseNode;
|
|
|
|
UINode* getVariableBaseNode ()
|
|
{
|
|
if (!variableBaseNode)
|
|
{
|
|
if (nodes)
|
|
variableBaseNode = nodes->getChildren ().findChildNode (Detail::MainNodeNames::kVariable);
|
|
}
|
|
return *variableBaseNode;
|
|
}
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UIDescription::UIDescription (const CResourceDescription& uidescFile, IViewFactory* _viewFactory)
|
|
{
|
|
impl = std::unique_ptr<Impl> (new Impl);
|
|
impl->uidescFile = uidescFile;
|
|
impl->viewFactory = _viewFactory;
|
|
if (uidescFile.type == CResourceDescription::kStringType && uidescFile.u.name != nullptr)
|
|
setFilePath (uidescFile.u.name);
|
|
if (impl->viewFactory == nullptr)
|
|
impl->viewFactory = getGenericViewFactory ();
|
|
UIDescriptionAddOnRegistry::forEach (
|
|
[&] (auto& addOn) { impl->viewFactory = addOn.getViewFactory (this, impl->viewFactory); });
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UIDescription::UIDescription (IContentProvider* contentProvider, IViewFactory* _viewFactory)
|
|
{
|
|
impl = std::unique_ptr<Impl> (new Impl);
|
|
impl->viewFactory = _viewFactory;
|
|
impl->contentProvider = contentProvider;
|
|
if (impl->viewFactory == nullptr)
|
|
impl->viewFactory = getGenericViewFactory ();
|
|
UIDescriptionAddOnRegistry::forEach (
|
|
[&] (auto& addOn) { impl->viewFactory = addOn.getViewFactory (this, impl->viewFactory); });
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UIDescription::~UIDescription () noexcept
|
|
{
|
|
UIDescriptionAddOnRegistry::forEach ([&] (auto& addOn) { addOn.onDestroy (this); });
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void UIDescription::setFilePath (UTF8StringPtr path)
|
|
{
|
|
impl->filePath = path;
|
|
impl->uidescFile.u.name = impl->filePath.data (); // make sure that xmlFile.u.name points to valid memory
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UTF8StringPtr UIDescription::getFilePath () const
|
|
{
|
|
return impl->filePath.data ();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const CResourceDescription& UIDescription::getUIDescFile () const
|
|
{
|
|
return impl->uidescFile;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::addDefaultNodes ()
|
|
{
|
|
if (impl->sharedResources)
|
|
return;
|
|
UINode* fontsNode = getBaseNode (Detail::MainNodeNames::kFont);
|
|
if (fontsNode)
|
|
{
|
|
struct DefaultFont {
|
|
UTF8StringPtr name;
|
|
CFontRef font;
|
|
};
|
|
|
|
const DefaultFont defaultFonts [] = {
|
|
{ "~ SystemFont", kSystemFont },
|
|
{ "~ NormalFontVeryBig", kNormalFontVeryBig },
|
|
{ "~ NormalFontBig", kNormalFontBig },
|
|
{ "~ NormalFont", kNormalFont },
|
|
{ "~ NormalFontSmall", kNormalFontSmall },
|
|
{ "~ NormalFontSmaller", kNormalFontSmaller },
|
|
{ "~ NormalFontVerySmall", kNormalFontVerySmall },
|
|
{ "~ SymbolFont", kSymbolFont },
|
|
{ nullptr, nullptr },
|
|
};
|
|
int32_t i = 0;
|
|
while (defaultFonts[i].name != nullptr)
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute ("name", defaultFonts[i].name);
|
|
Detail::UIFontNode* node = new Detail::UIFontNode ("font", attr);
|
|
node->setFont (defaultFonts[i].font);
|
|
node->noExport (true);
|
|
fontsNode->getChildren ().add (node);
|
|
i++;
|
|
}
|
|
}
|
|
UINode* colorsNode = getBaseNode (Detail::MainNodeNames::kColor);
|
|
if (colorsNode)
|
|
{
|
|
struct DefaultColor {
|
|
UTF8StringPtr name;
|
|
CColor color;
|
|
};
|
|
|
|
const DefaultColor defaultColors [] = {
|
|
{ "~ BlackCColor", kBlackCColor },
|
|
{ "~ WhiteCColor", kWhiteCColor },
|
|
{ "~ GreyCColor", kGreyCColor },
|
|
{ "~ RedCColor", kRedCColor },
|
|
{ "~ GreenCColor", kGreenCColor },
|
|
{ "~ BlueCColor", kBlueCColor },
|
|
{ "~ YellowCColor", kYellowCColor },
|
|
{ "~ CyanCColor", kCyanCColor },
|
|
{ "~ MagentaCColor", kMagentaCColor },
|
|
{ "~ TransparentCColor", kTransparentCColor },
|
|
{ nullptr, kBlackCColor }
|
|
};
|
|
|
|
int32_t i = 0;
|
|
while (defaultColors[i].name != nullptr)
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute ("name", defaultColors[i].name);
|
|
std::string colorStr;
|
|
UIViewCreator::colorToString (defaultColors[i].color, colorStr, nullptr);
|
|
attr->setAttribute ("rgba", colorStr);
|
|
Detail::UIColorNode* node = new Detail::UIColorNode ("color", attr);
|
|
node->noExport (true);
|
|
colorsNode->getChildren ().add (node);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::parsed () const
|
|
{
|
|
return impl->nodes != nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::setContentProvider (IContentProvider* provider)
|
|
{
|
|
impl->contentProvider = provider;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::parse ()
|
|
{
|
|
if (parsed ())
|
|
return true;
|
|
|
|
static auto parseUIDesc = [] (IContentProvider* contentProvider) -> SharedPointer<UINode> {
|
|
if (auto nodes = Detail::UIJsonDescReader::read (*contentProvider))
|
|
return nodes;
|
|
#if VSTGUI_ENABLE_XML_PARSER
|
|
Detail::UIXMLParser parser;
|
|
if (auto nodes = parser.parse (contentProvider))
|
|
return nodes;
|
|
#endif
|
|
return nullptr;
|
|
};
|
|
|
|
if (impl->contentProvider)
|
|
{
|
|
if ((impl->nodes = parseUIDesc (impl->contentProvider)))
|
|
{
|
|
postParsing ();
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CResourceInputStream resInputStream;
|
|
if (resInputStream.open (impl->uidescFile))
|
|
{
|
|
InputStreamContentProvider contentProvider (resInputStream);
|
|
if ((impl->nodes = parseUIDesc (&contentProvider)))
|
|
{
|
|
postParsing ();
|
|
return true;
|
|
}
|
|
}
|
|
else if (impl->uidescFile.type == CResourceDescription::kStringType)
|
|
{
|
|
CFileStream fileStream;
|
|
if (fileStream.open (impl->uidescFile.u.name, CFileStream::kReadMode))
|
|
{
|
|
InputStreamContentProvider contentProvider (fileStream);
|
|
if ((impl->nodes = parseUIDesc (&contentProvider)))
|
|
{
|
|
postParsing ();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!impl->nodes)
|
|
{
|
|
impl->nodes = makeOwned<UINode> ("vstgui-ui-description");
|
|
addDefaultNodes ();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::postParsing ()
|
|
{
|
|
addDefaultNodes ();
|
|
UIDescriptionAddOnRegistry::forEach ([&] (auto& addOn) { addOn.afterParsing (this); });
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::setController (IController* inController) const
|
|
{
|
|
impl->controller = inController;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
IController* UIDescription::getController () const
|
|
{
|
|
return impl->controller;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const IViewFactory* UIDescription::getViewFactory () const
|
|
{
|
|
return impl->viewFactory;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::registerListener (UIDescriptionListener* listener)
|
|
{
|
|
impl->registerListener (listener);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::unregisterListener (UIDescriptionListener* listener)
|
|
{
|
|
impl->unregisterListener (listener);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::setBitmapCreator (IBitmapCreator* creator)
|
|
{
|
|
impl->bitmapCreator = creator;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void UIDescription::setBitmapCreator2 (IBitmapCreator2* creator)
|
|
{
|
|
impl->bitmapCreator2 = creator;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static void FreeNodePlatformResources (Detail::UINode* node)
|
|
{
|
|
for (auto& child : node->getChildren ())
|
|
{
|
|
child->freePlatformResources ();
|
|
FreeNodePlatformResources (child);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::freePlatformResources ()
|
|
{
|
|
if (impl->nodes)
|
|
FreeNodePlatformResources (impl->nodes);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
auto UIDescription::getRootNode () const -> SharedPointer<UINode>
|
|
{
|
|
return impl->nodes;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::saveWindowsRCFile (UTF8StringPtr filename)
|
|
{
|
|
if (impl->sharedResources)
|
|
return true;
|
|
bool result = false;
|
|
UINode* bitmapNodes = getBaseNode (Detail::MainNodeNames::kBitmap);
|
|
if (bitmapNodes && !bitmapNodes->getChildren().empty ())
|
|
{
|
|
CFileStream stream;
|
|
if (stream.open (filename, CFileStream::kWriteMode|CFileStream::kTruncateMode))
|
|
{
|
|
for (auto& childNode : bitmapNodes->getChildren ())
|
|
{
|
|
UIAttributes* attr = childNode->getAttributes ();
|
|
if (attr)
|
|
{
|
|
const std::string* path = attr->getAttributeValue ("path");
|
|
if (path && !path->empty ())
|
|
{
|
|
stream << *path;
|
|
stream << "\t PNG \"";
|
|
stream << *path;
|
|
stream << "\"\r";
|
|
}
|
|
}
|
|
}
|
|
result = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static std::string moveOldFile (UTF8StringPtr filename)
|
|
{
|
|
FILE* file = fopen (filename, "r");
|
|
if (file)
|
|
{
|
|
fclose (file);
|
|
std::string newName = filename;
|
|
newName += ".old";
|
|
if (std::rename (filename, newName.c_str ()) == 0)
|
|
return newName;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::save (UTF8StringPtr filename, int32_t flags, AttributeSaveFilterFunc func)
|
|
{
|
|
std::string oldName = moveOldFile (filename);
|
|
bool result = false;
|
|
CFileStream stream;
|
|
if (stream.open (filename, CFileStream::kWriteMode|CFileStream::kTruncateMode))
|
|
{
|
|
result = saveToStream (stream, flags, func);
|
|
}
|
|
if (result && flags & kWriteWindowsResourceFile)
|
|
{
|
|
std::string rcFileName (filename);
|
|
size_t extPos = rcFileName.find_last_of ('.');
|
|
if (extPos != std::string::npos)
|
|
{
|
|
rcFileName.erase (extPos+1);
|
|
rcFileName += "rc";
|
|
saveWindowsRCFile (rcFileName.c_str ());
|
|
}
|
|
}
|
|
if (result && oldName.empty () == false)
|
|
std::remove (oldName.c_str ());
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::saveToStream (OutputStream& stream, int32_t flags, AttributeSaveFilterFunc func)
|
|
{
|
|
impl->attributeSaveFilterFunc = func;
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->beforeUIDescSave (this);
|
|
});
|
|
impl->attributeSaveFilterFunc = nullptr;
|
|
if (!impl->sharedResources)
|
|
{
|
|
UINode* bitmapNodes = getBaseNode (Detail::MainNodeNames::kBitmap);
|
|
if (bitmapNodes)
|
|
{
|
|
for (auto& childNode : bitmapNodes->getChildren ())
|
|
{
|
|
if (auto* bitmapNode = dynamic_cast<Detail::UIBitmapNode*> (childNode))
|
|
{
|
|
if (flags & kWriteImagesIntoUIDescFile)
|
|
{
|
|
if (!(flags & kDoNotVerifyImageData) || !bitmapNode->hasXMLData ())
|
|
bitmapNode->createXMLData (impl->filePath);
|
|
}
|
|
else
|
|
bitmapNode->removeXMLData ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
impl->nodes->getAttributes ()->setAttribute ("version", "1");
|
|
|
|
UIDescriptionAddOnRegistry::forEach ([&] (auto& addOn) { addOn.beforeSaving (this); });
|
|
|
|
BufferedOutputStream bufferedStream (stream);
|
|
if (flags & kWriteAsXML)
|
|
{
|
|
#if VSTGUI_ENABLE_XML_PARSER
|
|
Detail::UIXMLDescWriter writer;
|
|
return writer.write (bufferedStream, impl->nodes);
|
|
#else
|
|
#if DEBUG
|
|
DebugPrint ("XML not available.");
|
|
#endif
|
|
return false;
|
|
#endif
|
|
}
|
|
return Detail::UIJsonDescWriter::write (bufferedStream, impl->nodes);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::setSharedResources (const SharedPointer<UIDescription>& resources)
|
|
{
|
|
impl->sharedResources = resources;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const SharedPointer<UIDescription>& UIDescription::getSharedResources () const
|
|
{
|
|
return impl->sharedResources;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
Detail::UINode* UIDescription::findNodeForView (CView* view) const
|
|
{
|
|
CView* parentView = view;
|
|
std::string templateName;
|
|
while (parentView && getTemplateNameFromView (parentView, templateName) == false)
|
|
parentView = parentView->getParentView ();
|
|
if (parentView)
|
|
{
|
|
UINode* node = nullptr;
|
|
for (const auto& itNode : impl->nodes->getChildren ())
|
|
{
|
|
if (itNode->getName () == Detail::MainNodeNames::kTemplate)
|
|
{
|
|
const std::string* nodeName = itNode->getAttributes ()->getAttributeValue ("name");
|
|
if (nodeName && *nodeName == templateName)
|
|
{
|
|
node = itNode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (node)
|
|
{
|
|
while (view != parentView)
|
|
{
|
|
if (view == parentView)
|
|
return node;
|
|
CViewContainer* container = parentView->asViewContainer ();
|
|
vstgui_assert (container != nullptr);
|
|
auto nodeIterator = node->getChildren ().begin ();
|
|
CViewContainer* childContainer = nullptr;
|
|
ViewIterator it (container);
|
|
while (*it && nodeIterator != node->getChildren ().end ())
|
|
{
|
|
if (*it == view)
|
|
{
|
|
node = *nodeIterator;
|
|
parentView = view;
|
|
break;
|
|
}
|
|
childContainer = (*it)->asViewContainer ();
|
|
if (childContainer && childContainer->isChild (view, true))
|
|
{
|
|
break;
|
|
}
|
|
childContainer = nullptr;
|
|
++nodeIterator;
|
|
++it;
|
|
}
|
|
if (childContainer)
|
|
{
|
|
node = *nodeIterator;
|
|
parentView = childContainer;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (view == parentView)
|
|
return node;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::storeViews (const std::list<CView*>& views, OutputStream& stream,
|
|
SharedPointer<UIAttributes> customData) const
|
|
{
|
|
auto nodeList = makeOwned<Detail::UIDescList> (false);
|
|
for (auto& view : views)
|
|
{
|
|
UINode* node = findNodeForView (view);
|
|
if (node)
|
|
{
|
|
nodeList->add (node);
|
|
}
|
|
else
|
|
{
|
|
#if VSTGUI_LIVE_EDITING
|
|
if (auto* factory = dynamic_cast<IViewFactoryEditingSupport*> (impl->viewFactory))
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
if (factory->getAttributesForView (view, const_cast<UIDescription*> (this), *attr) == false)
|
|
return false;
|
|
UINode* newNode = new UINode ("view", attr);
|
|
nodeList->add (newNode);
|
|
newNode->forget ();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
if (!nodeList->empty ())
|
|
{
|
|
if (customData)
|
|
{
|
|
UINode* customNode = new UINode (Detail::MainNodeNames::kCustom, customData);
|
|
nodeList->add (customNode);
|
|
customNode->forget ();
|
|
}
|
|
UINode baseNode ("vstgui-ui-description-view-list", nodeList);
|
|
return Detail::UIJsonDescWriter::write (stream, &baseNode, false);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::restoreViews (InputStream& stream, std::list<SharedPointer<CView> >& views, UIAttributes** customData)
|
|
{
|
|
InputStreamContentProvider contentProvider (stream);
|
|
if (auto baseNode = Detail::UIJsonDescReader::read (contentProvider))
|
|
{
|
|
Detail::UIDescList& children = baseNode->getChildren ();
|
|
for (auto& childNode : children)
|
|
{
|
|
if (childNode->getName() == Detail::MainNodeNames::kCustom)
|
|
{
|
|
if (customData)
|
|
{
|
|
*customData = childNode->getAttributes ();
|
|
(*customData)->remember ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CView* view = createViewFromNode (childNode);
|
|
if (view)
|
|
{
|
|
views.emplace_back (view);
|
|
view->forget ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return views.empty () == false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CView* UIDescription::createViewFromNode (UINode* node) const
|
|
{
|
|
const auto* templateName = node->getAttributes ()->getAttributeValue (Detail::MainNodeNames::kTemplate);
|
|
if (templateName)
|
|
{
|
|
CView* view = createView (templateName->c_str (), impl->controller);
|
|
if (view)
|
|
impl->viewFactory->applyAttributeValues (view, *node->getAttributes (), this);
|
|
return view;
|
|
}
|
|
|
|
IController* subController = nullptr;
|
|
CView* result = nullptr;
|
|
if (impl->controller)
|
|
{
|
|
const auto* subControllerName = node->getAttributes ()->getAttributeValue (UIViewCreator::kAttrSubController);
|
|
if (subControllerName)
|
|
{
|
|
subController = impl->controller->createSubController (subControllerName->c_str (), this);
|
|
if (subController)
|
|
{
|
|
impl->subControllerStack.emplace_back (impl->controller);
|
|
setController (subController);
|
|
}
|
|
}
|
|
result = impl->controller->createView (*node->getAttributes (), this);
|
|
if (result && impl->viewFactory)
|
|
{
|
|
const std::string* viewClass = node->getAttributes ()->getAttributeValue (UIViewCreator::kAttrClass);
|
|
if (viewClass)
|
|
impl->viewFactory->applyCustomViewAttributeValues (result, viewClass->c_str (), *node->getAttributes (), this);
|
|
}
|
|
}
|
|
if (result == nullptr && impl->viewFactory)
|
|
{
|
|
result = impl->viewFactory->createView (*node->getAttributes (), this);
|
|
if (result == nullptr)
|
|
{
|
|
result = new CViewContainer (CRect (0, 0, 0, 0));
|
|
impl->viewFactory->applyCustomViewAttributeValues (result, "CViewContainer", *node->getAttributes (), this);
|
|
}
|
|
}
|
|
if (result && node->hasChildren ())
|
|
{
|
|
CViewContainer* viewContainer = result->asViewContainer ();
|
|
for (const auto& itNode : node->getChildren ())
|
|
{
|
|
if (viewContainer)
|
|
{
|
|
if (itNode->getName () == "view")
|
|
{
|
|
CView* childView = createViewFromNode (itNode);
|
|
if (childView)
|
|
{
|
|
if (!viewContainer->addView (childView))
|
|
childView->forget ();
|
|
}
|
|
}
|
|
}
|
|
if (itNode->getName () == "attribute")
|
|
{
|
|
const std::string* attrName = itNode->getAttributes ()->getAttributeValue ("id");
|
|
const std::string* attrValue = itNode->getAttributes ()->getAttributeValue ("value");
|
|
if (attrName && attrValue)
|
|
{
|
|
CViewAttributeID attrId = 0;
|
|
if (attrName->size () == 4)
|
|
{
|
|
char c1 = (*attrName)[0];
|
|
char c2 = (*attrName)[1];
|
|
char c3 = (*attrName)[2];
|
|
char c4 = (*attrName)[3];
|
|
attrId = ((((size_t)c1) << 24) | (((size_t)c2) << 16) | (((size_t)c3) << 8) | (((size_t)c4) << 0));
|
|
}
|
|
else
|
|
attrId = (CViewAttributeID)strtol (attrName->c_str (), nullptr, 10);
|
|
if (attrId)
|
|
result->setAttribute (attrId, static_cast<uint32_t> (attrValue->size () + 1), attrValue->c_str ());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (result && impl->controller)
|
|
result = impl->controller->verifyView (result, *node->getAttributes (), this);
|
|
if (subController)
|
|
{
|
|
if (result)
|
|
result->setAttribute (kCViewControllerAttribute, subController);
|
|
setController (impl->subControllerStack.back ());
|
|
impl->subControllerStack.pop_back ();
|
|
if (result == nullptr)
|
|
{
|
|
auto obj = dynamic_cast<IReference*> (subController);
|
|
if (obj)
|
|
obj->forget ();
|
|
else
|
|
delete subController;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CViewAttributeID UIDescription::kTemplateNameAttributeID = 'uitl';
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CView* UIDescription::createView (UTF8StringPtr name, IController* _controller) const
|
|
{
|
|
if (impl->nodes == nullptr)
|
|
return {};
|
|
|
|
IUIDescriptionAddOn::CreateTemplateViewFunc f =
|
|
[this] (UTF8StringPtr name, IController* _controller) mutable -> CView* {
|
|
ScopePointer<IController> sp (&impl->controller, _controller);
|
|
for (const auto& itNode : impl->nodes->getChildren ())
|
|
{
|
|
if (itNode->getName () == Detail::MainNodeNames::kTemplate)
|
|
{
|
|
const std::string* nodeName = itNode->getAttributes ()->getAttributeValue ("name");
|
|
if (nodeName && *nodeName == name)
|
|
{
|
|
CView* view = createViewFromNode (itNode);
|
|
if (view)
|
|
view->setAttribute (kTemplateNameAttributeID,
|
|
static_cast<uint32_t> (strlen (name) + 1), name);
|
|
return view;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
};
|
|
|
|
UIDescriptionAddOnRegistry::forEach (
|
|
[&] (auto& addOn) { f = std::move (addOn.onCreateTemplateView (this, f)); });
|
|
return f (name, _controller);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::getTemplateNameFromView (CView* view, std::string& templateName) const
|
|
{
|
|
bool result = false;
|
|
uint32_t attrSize = 0;
|
|
if (view->getAttributeSize (kTemplateNameAttributeID, attrSize))
|
|
{
|
|
char* str = new char[attrSize];
|
|
if (view->getAttribute (kTemplateNameAttributeID, attrSize, str, attrSize))
|
|
{
|
|
templateName = str;
|
|
result = true;
|
|
}
|
|
delete [] str;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const UIAttributes* UIDescription::getViewAttributes (UTF8StringPtr name) const
|
|
{
|
|
if (impl->nodes)
|
|
{
|
|
for (const auto& itNode : impl->nodes->getChildren ())
|
|
{
|
|
if (itNode->getName () == Detail::MainNodeNames::kTemplate)
|
|
{
|
|
const std::string* nodeName = itNode->getAttributes ()->getAttributeValue ("name");
|
|
if (nodeName && *nodeName == name)
|
|
return itNode->getAttributes ();
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
Detail::UINode* UIDescription::getBaseNode (UTF8StringPtr name, bool create) const
|
|
{
|
|
UTF8StringView nameView (name);
|
|
nameView.calculateByteCount ();
|
|
if (impl->sharedResources)
|
|
{
|
|
if (nameView == Detail::MainNodeNames::kBitmap || nameView == Detail::MainNodeNames::kFont || nameView == Detail::MainNodeNames::kColor || nameView == Detail::MainNodeNames::kGradient)
|
|
{
|
|
return impl->sharedResources->getBaseNode (name);
|
|
}
|
|
}
|
|
if (impl->nodes)
|
|
{
|
|
UINode* node = impl->nodes->getChildren ().findChildNode (nameView);
|
|
if (node)
|
|
return node;
|
|
|
|
if (create)
|
|
{
|
|
node = new UINode (name);
|
|
impl->nodes->getChildren ().add (node);
|
|
return node;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
Detail::UINode* UIDescription::findChildNodeByNameAttribute (UINode* node, UTF8StringPtr nameAttribute) const
|
|
{
|
|
if (node)
|
|
return node->getChildren ().findChildNodeWithAttributeValue ("name", nameAttribute);
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
int32_t UIDescription::getTagForName (UTF8StringPtr name) const
|
|
{
|
|
int32_t tag = -1;
|
|
if (auto* controlTagNode = dynamic_cast<Detail::UIControlTagNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kControlTag), name)))
|
|
{
|
|
tag = controlTagNode->getTag ();
|
|
if (tag == -1)
|
|
{
|
|
const std::string* tagStr = controlTagNode->getTagString ();
|
|
if (tagStr)
|
|
{
|
|
double value;
|
|
if (calculateStringValue (tagStr->c_str (), value))
|
|
{
|
|
tag = (int32_t)value;
|
|
controlTagNode->setTag (tag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (impl->controller)
|
|
tag = impl->controller->getTagForName (name, tag);
|
|
return tag;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::hasColorName (UTF8StringPtr name) const
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIColorNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kColor), name));
|
|
return node ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::hasTagName (UTF8StringPtr name) const
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIControlTagNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kControlTag), name));
|
|
return node ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::hasFontName (UTF8StringPtr name) const
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIFontNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kFont), name));
|
|
return node ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::hasBitmapName (UTF8StringPtr name) const
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIBitmapNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kBitmap), name));
|
|
return node ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::hasGradientName (UTF8StringPtr name) const
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIGradientNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kGradient), name));
|
|
return node ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
IControlListener* UIDescription::getControlListener (UTF8StringPtr name) const
|
|
{
|
|
if (impl->controller)
|
|
return impl->controller->getControlListener (name);
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CBitmap* UIDescription::getBitmap (UTF8StringPtr name) const
|
|
{
|
|
auto* bitmapNode = dynamic_cast<Detail::UIBitmapNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kBitmap), name));
|
|
if (bitmapNode)
|
|
{
|
|
CBitmap* bitmap = bitmapNode->getBitmap (impl->filePath);
|
|
if (impl->bitmapCreator && bitmap && bitmap->getPlatformBitmap () == nullptr)
|
|
{
|
|
auto platformBitmap = impl->bitmapCreator->createBitmap (*bitmapNode->getAttributes ());
|
|
if (platformBitmap)
|
|
{
|
|
double scaleFactor;
|
|
if (Detail::decodeScaleFactorFromName (name, scaleFactor))
|
|
platformBitmap->setScaleFactor (scaleFactor);
|
|
bitmap->setPlatformBitmap (platformBitmap);
|
|
}
|
|
}
|
|
if (impl->bitmapCreator2 && bitmap && bitmap->getPlatformBitmap () == nullptr)
|
|
{
|
|
if (auto b = impl->bitmapCreator2->createBitmap (*bitmapNode->getAttributes (), this))
|
|
{
|
|
bitmap->setPlatformBitmap (b->getPlatformBitmap ());
|
|
auto it = b->begin ();
|
|
++it;
|
|
while (it != b->end ())
|
|
{
|
|
bitmap->addBitmap (*it);
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
if (bitmap && bitmapNode->getFilterProcessed () == false)
|
|
{
|
|
std::list<SharedPointer<BitmapFilter::IFilter> > filters;
|
|
for (auto& childNode : bitmapNode->getChildren ())
|
|
{
|
|
const std::string* filterName = nullptr;
|
|
if (childNode->getName () == "filter" && (filterName = childNode->getAttributes ()->getAttributeValue ("name")))
|
|
{
|
|
auto filter = owned (BitmapFilter::Factory::getInstance().createFilter (filterName->c_str ()));
|
|
if (filter == nullptr)
|
|
continue;
|
|
filters.emplace_back (filter);
|
|
for (auto& propertyNode : childNode->getChildren ())
|
|
{
|
|
if (propertyNode->getName () != "property")
|
|
continue;
|
|
const std::string* propName = propertyNode->getAttributes ()->getAttributeValue ("name");
|
|
if (propName == nullptr)
|
|
continue;
|
|
switch (filter->getProperty (propName->c_str ()).getType ())
|
|
{
|
|
case BitmapFilter::Property::kInteger:
|
|
{
|
|
int32_t intValue;
|
|
if (propertyNode->getAttributes ()->getIntegerAttribute ("value", intValue))
|
|
filter->setProperty (propName->c_str (), intValue);
|
|
break;
|
|
}
|
|
case BitmapFilter::Property::kFloat:
|
|
{
|
|
double floatValue;
|
|
if (propertyNode->getAttributes ()->getDoubleAttribute ("value", floatValue))
|
|
filter->setProperty (propName->c_str (), floatValue);
|
|
break;
|
|
}
|
|
case BitmapFilter::Property::kPoint:
|
|
{
|
|
CPoint pointValue;
|
|
if (propertyNode->getAttributes ()->getPointAttribute ("value", pointValue))
|
|
filter->setProperty (propName->c_str (), pointValue);
|
|
break;
|
|
}
|
|
case BitmapFilter::Property::kRect:
|
|
{
|
|
CRect rectValue;
|
|
if (propertyNode->getAttributes ()->getRectAttribute ("value", rectValue))
|
|
filter->setProperty (propName->c_str (), rectValue);
|
|
break;
|
|
}
|
|
case BitmapFilter::Property::kColor:
|
|
{
|
|
const std::string* colorString = propertyNode->getAttributes()->getAttributeValue ("value");
|
|
if (colorString)
|
|
{
|
|
CColor color;
|
|
if (getColor (colorString->c_str (), color))
|
|
filter->setProperty(propName->c_str (), color);
|
|
}
|
|
break;
|
|
}
|
|
case BitmapFilter::Property::kTransformMatrix:
|
|
{
|
|
// TODO
|
|
break;
|
|
}
|
|
case BitmapFilter::Property::kObject: // objects can not be stored/restored
|
|
case BitmapFilter::Property::kUnknown:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (auto& filter : filters)
|
|
{
|
|
filter->setProperty (BitmapFilter::Standard::Property::kInputBitmap, bitmap);
|
|
if (filter->run ())
|
|
{
|
|
auto obj = filter->getProperty (BitmapFilter::Standard::Property::kOutputBitmap).getObject ();
|
|
if (auto* outputBitmap = dynamic_cast<CBitmap*>(obj))
|
|
{
|
|
bitmap->setPlatformBitmap (outputBitmap->getPlatformBitmap ());
|
|
}
|
|
}
|
|
}
|
|
bitmapNode->setFilterProcessed ();
|
|
}
|
|
if (bitmap && bitmapNode->getScaledBitmapsAdded () == false)
|
|
{
|
|
double scaleFactor;
|
|
auto decoded = Detail::decodeScaleFactorFromName (bitmap->getResourceDescription ().u.name, scaleFactor);
|
|
if (!decoded || scaleFactor == 1.)
|
|
{
|
|
std::string bitmapName = name;
|
|
if (decoded)
|
|
bitmapName = Detail::removeScaleFactorFromName (bitmapName);
|
|
// find scaled versions for this bitmap
|
|
UINode* bitmapsNode = getBaseNode (Detail::MainNodeNames::kBitmap);
|
|
for (auto& it : bitmapsNode->getChildren ())
|
|
{
|
|
auto* childNode = dynamic_cast<Detail::UIBitmapNode*>(it);
|
|
if (childNode == nullptr || childNode == bitmapNode)
|
|
continue;
|
|
const std::string* childNodeBitmapName = childNode->getAttributes()->getAttributeValue ("name");
|
|
if (childNodeBitmapName == nullptr)
|
|
continue;
|
|
std::string nameWithoutScaleFactor = Detail::removeScaleFactorFromName (*childNodeBitmapName);
|
|
if (nameWithoutScaleFactor == bitmapName)
|
|
{
|
|
childNode->setScaledBitmapsAdded ();
|
|
CBitmap* childBitmap = getBitmap (childNodeBitmapName->c_str ());
|
|
if (childBitmap && childBitmap->getPlatformBitmap ())
|
|
bitmap->addBitmap (childBitmap->getPlatformBitmap ());
|
|
}
|
|
}
|
|
}
|
|
bitmapNode->setScaledBitmapsAdded ();
|
|
}
|
|
return bitmap;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CFontRef UIDescription::getFont (UTF8StringPtr name) const
|
|
{
|
|
auto* fontNode = dynamic_cast<Detail::UIFontNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kFont), name));
|
|
if (fontNode)
|
|
return fontNode->getFont ();
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::getColor (UTF8StringPtr name, CColor& color) const
|
|
{
|
|
auto* colorNode = dynamic_cast<Detail::UIColorNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kColor), name));
|
|
if (colorNode)
|
|
{
|
|
color = colorNode->getColor ();
|
|
return true;
|
|
}
|
|
if (Detail::parseColor (name, color))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CGradient* UIDescription::getGradient (UTF8StringPtr name) const
|
|
{
|
|
auto* gradientNode = dynamic_cast<Detail::UIGradientNode*> (findChildNodeByNameAttribute (getBaseNode(Detail::MainNodeNames::kGradient), name));
|
|
if (gradientNode)
|
|
return gradientNode->getGradient ();
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
template<typename NodeType, typename ObjType, typename CompareFunction> UTF8StringPtr UIDescription::lookupName (const ObjType& obj, IdStringPtr mainNodeName, CompareFunction compare) const
|
|
{
|
|
UINode* baseNode = getBaseNode (mainNodeName);
|
|
if (baseNode)
|
|
{
|
|
auto& children = baseNode->getChildren ();
|
|
for (const auto& itNode : children)
|
|
{
|
|
auto* node = dynamic_cast<NodeType*>(itNode);
|
|
if (node && compare (this, node, obj))
|
|
{
|
|
const std::string* name = node->getAttributes ()->getAttributeValue ("name");
|
|
return name ? name->c_str () : nullptr;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UTF8StringPtr UIDescription::lookupColorName (const CColor& color) const
|
|
{
|
|
return lookupName<Detail::UIColorNode> (color, Detail::MainNodeNames::kColor, [] (const UIDescription* desc, Detail::UIColorNode* node, const CColor& color) {
|
|
return node->getColor() == color;
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UTF8StringPtr UIDescription::lookupFontName (const CFontRef font) const
|
|
{
|
|
return font ? lookupName<Detail::UIFontNode> (font, Detail::MainNodeNames::kFont, [] (const UIDescription* desc, Detail::UIFontNode* node, const CFontRef& font) {
|
|
return node->getFont () && node->getFont () == font;
|
|
}) : nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UTF8StringPtr UIDescription::lookupBitmapName (const CBitmap* bitmap) const
|
|
{
|
|
return bitmap ? lookupName<Detail::UIBitmapNode> (bitmap, Detail::MainNodeNames::kBitmap, [] (const UIDescription* desc, Detail::UIBitmapNode* node, const CBitmap* bitmap) {
|
|
return node->getBitmap (desc->impl->filePath) == bitmap;
|
|
}) : nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UTF8StringPtr UIDescription::lookupGradientName (const CGradient* gradient) const
|
|
{
|
|
return gradient ? lookupName<Detail::UIGradientNode> (gradient, Detail::MainNodeNames::kGradient, [] (const UIDescription* desc, Detail::UIGradientNode* node, const CGradient* gradient) {
|
|
return node->getGradient() == gradient || (node->getGradient () && gradient->getColorStops () == node->getGradient ()->getColorStops ());
|
|
}) : nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UTF8StringPtr UIDescription::lookupControlTagName (const int32_t tag) const
|
|
{
|
|
return lookupName<Detail::UIControlTagNode> (tag, Detail::MainNodeNames::kControlTag, [] (const UIDescription* desc, Detail::UIControlTagNode* node, const int32_t tag) {
|
|
int32_t nodeTag = node->getTag ();
|
|
if (nodeTag == -1 && node->getTagString ())
|
|
{
|
|
double v;
|
|
if (desc->calculateStringValue (node->getTagString ()->c_str (), v))
|
|
nodeTag = (int32_t)v;
|
|
}
|
|
return nodeTag == tag;
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
template<typename NodeType>
|
|
void UIDescription::changeNodeName (UTF8StringPtr oldName, UTF8StringPtr newName, IdStringPtr mainNodeName)
|
|
{
|
|
UINode* mainNode = getBaseNode (mainNodeName);
|
|
auto* node = dynamic_cast<NodeType*> (findChildNodeByNameAttribute(mainNode, oldName));
|
|
if (node)
|
|
{
|
|
node->getAttributes ()->setAttribute ("name", newName);
|
|
mainNode->childAttributeChanged (node, "name", oldName);
|
|
mainNode->sortChildren ();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeColorName (UTF8StringPtr oldName, UTF8StringPtr newName)
|
|
{
|
|
changeNodeName<Detail::UIColorNode> (oldName, newName, Detail::MainNodeNames::kColor);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescColorChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeTagName (UTF8StringPtr oldName, UTF8StringPtr newName)
|
|
{
|
|
changeNodeName<Detail::UIControlTagNode> (oldName, newName, Detail::MainNodeNames::kControlTag);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescTagChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeFontName (UTF8StringPtr oldName, UTF8StringPtr newName)
|
|
{
|
|
changeNodeName<Detail::UIFontNode> (oldName, newName, Detail::MainNodeNames::kFont);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescFontChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeBitmapName (UTF8StringPtr oldName, UTF8StringPtr newName)
|
|
{
|
|
changeNodeName<Detail::UIBitmapNode> (oldName, newName, Detail::MainNodeNames::kBitmap);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescBitmapChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeGradientName (UTF8StringPtr oldName, UTF8StringPtr newName)
|
|
{
|
|
changeNodeName<Detail::UIGradientNode> (oldName, newName, Detail::MainNodeNames::kGradient);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescGradientChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeColor (UTF8StringPtr name, const CColor& newColor)
|
|
{
|
|
UINode* colorsNode = getBaseNode (Detail::MainNodeNames::kColor);
|
|
auto* node = dynamic_cast<Detail::UIColorNode*> (findChildNodeByNameAttribute (colorsNode, name));
|
|
if (node)
|
|
{
|
|
if (!node->noExport ())
|
|
{
|
|
node->setColor (newColor);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescColorChanged (this);
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (colorsNode)
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute ("name", name);
|
|
std::string colorStr;
|
|
UIViewCreator::colorToString (newColor, colorStr, nullptr);
|
|
attr->setAttribute ("rgba", colorStr);
|
|
auto* newNode = new Detail::UIColorNode ("color", attr);
|
|
colorsNode->getChildren ().add (newNode);
|
|
colorsNode->sortChildren ();
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescColorChanged (this);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeFont (UTF8StringPtr name, CFontRef newFont)
|
|
{
|
|
UINode* fontsNode = getBaseNode (Detail::MainNodeNames::kFont);
|
|
auto* node = dynamic_cast<Detail::UIFontNode*> (findChildNodeByNameAttribute (fontsNode, name));
|
|
if (node)
|
|
{
|
|
if (!node->noExport ())
|
|
{
|
|
node->setFont (newFont);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescFontChanged (this);
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fontsNode)
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute ("name", name);
|
|
auto* newNode = new Detail::UIFontNode ("font", attr);
|
|
newNode->setFont (newFont);
|
|
fontsNode->getChildren ().add (newNode);
|
|
fontsNode->sortChildren ();
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescFontChanged (this);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeGradient (UTF8StringPtr name, CGradient* newGradient)
|
|
{
|
|
UINode* gradientsNode = getBaseNode (Detail::MainNodeNames::kGradient);
|
|
auto* node = dynamic_cast<Detail::UIGradientNode*> (findChildNodeByNameAttribute (gradientsNode, name));
|
|
if (node)
|
|
{
|
|
if (!node->noExport ())
|
|
{
|
|
node->setGradient (newGradient);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescGradientChanged (this);
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (gradientsNode)
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute ("name", name);
|
|
auto* newNode = new Detail::UIGradientNode ("gradient", attr);
|
|
newNode->setGradient (newGradient);
|
|
gradientsNode->getChildren ().add (newNode);
|
|
gradientsNode->sortChildren ();
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescGradientChanged (this);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeBitmap (UTF8StringPtr name, UTF8StringPtr newName, const CRect* nineparttiledOffset)
|
|
{
|
|
UINode* bitmapsNode = getBaseNode (Detail::MainNodeNames::kBitmap);
|
|
auto* node = dynamic_cast<Detail::UIBitmapNode*> (findChildNodeByNameAttribute (bitmapsNode, name));
|
|
if (node)
|
|
{
|
|
if (!node->noExport ())
|
|
{
|
|
node->setBitmap (newName);
|
|
node->setNinePartTiledOffset (nineparttiledOffset);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescBitmapChanged (this);
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bitmapsNode)
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute ("name", name);
|
|
auto* newNode = new Detail::UIBitmapNode ("bitmap", attr);
|
|
if (nineparttiledOffset)
|
|
newNode->setNinePartTiledOffset (nineparttiledOffset);
|
|
newNode->setBitmap (newName);
|
|
bitmapsNode->getChildren ().add (newNode);
|
|
bitmapsNode->sortChildren ();
|
|
impl->forEachListener (
|
|
[this] (UIDescriptionListener* l) { l->onUIDescBitmapChanged (this); });
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeMultiFrameBitmap (UTF8StringPtr name, UTF8StringPtr newName,
|
|
const CMultiFrameBitmapDescription* desc)
|
|
{
|
|
UINode* bitmapsNode = getBaseNode (Detail::MainNodeNames::kBitmap);
|
|
auto* node =
|
|
dynamic_cast<Detail::UIBitmapNode*> (findChildNodeByNameAttribute (bitmapsNode, name));
|
|
if (node)
|
|
{
|
|
if (!node->noExport ())
|
|
{
|
|
node->setBitmap (newName);
|
|
node->setMultiFrameDesc (desc);
|
|
impl->forEachListener (
|
|
[this] (UIDescriptionListener* l) { l->onUIDescBitmapChanged (this); });
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bitmapsNode)
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute ("name", name);
|
|
auto* newNode = new Detail::UIBitmapNode ("bitmap", attr);
|
|
if (desc)
|
|
newNode->setMultiFrameDesc (desc);
|
|
newNode->setBitmap (newName);
|
|
bitmapsNode->getChildren ().add (newNode);
|
|
bitmapsNode->sortChildren ();
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescBitmapChanged (this);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeBitmapFilters (UTF8StringPtr bitmapName, const std::list<SharedPointer<UIAttributes> >& filters)
|
|
{
|
|
auto* bitmapNode = dynamic_cast<Detail::UIBitmapNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kBitmap), bitmapName));
|
|
if (bitmapNode)
|
|
{
|
|
bitmapNode->getChildren().removeAll ();
|
|
for (const auto& filter : filters)
|
|
{
|
|
const std::string* filterName = filter->getAttributeValue ("name");
|
|
if (filterName == nullptr)
|
|
continue;
|
|
UINode* filterNode = new UINode ("filter");
|
|
filterNode->getAttributes ()->setAttribute ("name", *filterName);
|
|
for (auto& it2 : *filter)
|
|
{
|
|
if (it2.first == "name")
|
|
continue;
|
|
UINode* propertyNode = new UINode ("property");
|
|
propertyNode->getAttributes ()->setAttribute("name", it2.first);
|
|
propertyNode->getAttributes ()->setAttribute("value", it2.second);
|
|
filterNode->getChildren ().add (propertyNode);
|
|
}
|
|
bitmapNode->getChildren ().add (filterNode);
|
|
}
|
|
bitmapNode->invalidBitmap ();
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescBitmapChanged (this);
|
|
});
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::collectBitmapFilters (UTF8StringPtr bitmapName, std::list<SharedPointer<UIAttributes> >& filters) const
|
|
{
|
|
auto* bitmapNode = dynamic_cast<Detail::UIBitmapNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kBitmap), bitmapName));
|
|
if (bitmapNode)
|
|
{
|
|
for (auto& childNode : bitmapNode->getChildren ())
|
|
{
|
|
if (childNode->getName () == "filter")
|
|
{
|
|
const std::string* filterName = childNode->getAttributes ()->getAttributeValue ("name");
|
|
if (filterName == nullptr)
|
|
continue;
|
|
auto attributes = makeOwned<UIAttributes> ();
|
|
attributes->setAttribute ("name", *filterName);
|
|
for (auto& it2 : childNode->getChildren ())
|
|
{
|
|
if (it2->getName () == "property")
|
|
{
|
|
const std::string* name = it2->getAttributes ()->getAttributeValue ("name");
|
|
const std::string* value = it2->getAttributes ()->getAttributeValue ("value");
|
|
if (name && value)
|
|
{
|
|
attributes->setAttribute (*name, *value);
|
|
}
|
|
}
|
|
}
|
|
filters.emplace_back (attributes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static void removeChildNode (Detail::UINode* baseNode, UTF8StringPtr nodeName)
|
|
{
|
|
auto& children = baseNode->getChildren ();
|
|
for (const auto& itNode : children)
|
|
{
|
|
const std::string* name = itNode->getAttributes ()->getAttributeValue ("name");
|
|
if (name && *name == nodeName)
|
|
{
|
|
if (!itNode->noExport ())
|
|
children.remove (itNode);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::removeNode (UTF8StringPtr name, IdStringPtr mainNodeName)
|
|
{
|
|
UINode* node = getBaseNode (mainNodeName);
|
|
if (node)
|
|
{
|
|
removeChildNode (node, name);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::removeColor (UTF8StringPtr name)
|
|
{
|
|
removeNode (name, Detail::MainNodeNames::kColor);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescColorChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::removeTag (UTF8StringPtr name)
|
|
{
|
|
removeNode (name, Detail::MainNodeNames::kControlTag);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescTagChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::removeFont (UTF8StringPtr name)
|
|
{
|
|
removeNode (name, Detail::MainNodeNames::kFont);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescFontChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::removeBitmap (UTF8StringPtr name)
|
|
{
|
|
removeNode (name, Detail::MainNodeNames::kBitmap);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescBitmapChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::removeGradient (UTF8StringPtr name)
|
|
{
|
|
removeNode (name, Detail::MainNodeNames::kGradient);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescGradientChanged (this);
|
|
});
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::changeAlternativeFontNames (UTF8StringPtr name, UTF8StringPtr alternativeFonts)
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIFontNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kFont), name));
|
|
if (node)
|
|
{
|
|
node->setAlternativeFontNames (alternativeFonts);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescFontChanged (this);
|
|
});
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::getAlternativeFontNames (UTF8StringPtr name, std::string& alternativeFonts) const
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIFontNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kFont), name));
|
|
if (node)
|
|
{
|
|
if (node->getAlternativeFontNames (alternativeFonts))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::collectTemplateViewNames (std::list<const std::string*>& names) const
|
|
{
|
|
if (!impl->nodes)
|
|
return;
|
|
for (const auto& itNode : impl->nodes->getChildren ())
|
|
{
|
|
if (itNode->getName () == Detail::MainNodeNames::kTemplate)
|
|
{
|
|
const std::string* nodeName = itNode->getAttributes ()->getAttributeValue ("name");
|
|
if (nodeName)
|
|
names.emplace_back (nodeName);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
template<typename NodeType> void UIDescription::collectNamesFromNode (IdStringPtr mainNodeName, std::list<const std::string*>& names) const
|
|
{
|
|
if (UINode* node = getBaseNode (mainNodeName))
|
|
{
|
|
auto& children = node->getChildren ();
|
|
for (const auto& itNode : children)
|
|
{
|
|
if (auto* nodeType = dynamic_cast<NodeType*>(itNode))
|
|
{
|
|
const std::string* name = nodeType->getAttributes ()->getAttributeValue ("name");
|
|
if (name)
|
|
names.emplace_back (name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::collectColorNames (std::list<const std::string*>& names) const
|
|
{
|
|
collectNamesFromNode<Detail::UIColorNode> (Detail::MainNodeNames::kColor, names);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::collectFontNames (std::list<const std::string*>& names) const
|
|
{
|
|
collectNamesFromNode<Detail::UIFontNode> (Detail::MainNodeNames::kFont, names);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::collectBitmapNames (std::list<const std::string*>& names) const
|
|
{
|
|
collectNamesFromNode<Detail::UIBitmapNode> (Detail::MainNodeNames::kBitmap, names);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::collectGradientNames (std::list<const std::string*>& names) const
|
|
{
|
|
collectNamesFromNode<Detail::UIGradientNode> (Detail::MainNodeNames::kGradient, names);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::collectControlTagNames (std::list<const std::string*>& names) const
|
|
{
|
|
collectNamesFromNode<Detail::UIControlTagNode> (Detail::MainNodeNames::kControlTag, names);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::updateAttributesForView (UINode* node, CView* view, bool deep)
|
|
{
|
|
bool result = false;
|
|
#if VSTGUI_LIVE_EDITING
|
|
auto* factory = dynamic_cast<IViewFactoryEditingSupport*> (impl->viewFactory);
|
|
std::list<std::string> attributeNames;
|
|
CViewContainer* container = view->asViewContainer ();
|
|
if (factory->getAttributeNamesForView (view, attributeNames))
|
|
{
|
|
for (auto& name : attributeNames)
|
|
{
|
|
if (impl->attributeSaveFilterFunc &&
|
|
impl->attributeSaveFilterFunc (view, name) == false)
|
|
continue;
|
|
std::string value;
|
|
if (impl->viewFactory->getAttributeValue (view, name, value, this))
|
|
node->getAttributes ()->setAttribute (name, std::move (value));
|
|
}
|
|
node->getAttributes ()->setAttribute (UIViewCreator::kAttrClass,
|
|
IViewFactory::getViewName (view));
|
|
result = true;
|
|
}
|
|
if (deep && container && dynamic_cast<UIViewSwitchContainer*> (container) == nullptr)
|
|
{
|
|
ViewIterator it (container);
|
|
while (*it)
|
|
{
|
|
CView* subView = *it;
|
|
std::string subTemplateName;
|
|
if (getTemplateNameFromView (subView, subTemplateName))
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute (Detail::MainNodeNames::kTemplate, subTemplateName);
|
|
UINode* subNode = new UINode ("view", attr);
|
|
node->getChildren ().add (subNode);
|
|
updateAttributesForView (subNode, subView, false);
|
|
CRect r = subView->getViewSize ();
|
|
CRect r2 (r);
|
|
r.offset (-r.left, -r.top);
|
|
subView->setViewSize (r);
|
|
subView->setMouseableArea (r);
|
|
updateViewDescription (subTemplateName.c_str (), subView);
|
|
subView->setViewSize (r2);
|
|
subView->setMouseableArea (r2);
|
|
}
|
|
else
|
|
{
|
|
// check if subview is created via UIDescription
|
|
// if it is, it's just added to this node
|
|
UINode* subNode = new UINode ("view");
|
|
if (updateAttributesForView (subNode, subView))
|
|
{
|
|
node->getChildren ().add (subNode);
|
|
}
|
|
else
|
|
{
|
|
// if it is not, we check if it has children. This can happen per example for a CScrollView for its container view.
|
|
if (!subNode->getChildren ().empty ())
|
|
{
|
|
for (auto& childNode : subNode->getChildren ())
|
|
{
|
|
childNode->remember ();
|
|
node->getChildren ().add (childNode);
|
|
}
|
|
}
|
|
subNode->forget ();
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::updateViewDescription (UTF8StringPtr name, CView* view)
|
|
{
|
|
#if VSTGUI_LIVE_EDITING
|
|
bool doIt = true;
|
|
impl->forEachListener ([&] (UIDescriptionListener* l) {
|
|
if (!l->doUIDescTemplateUpdate (this, name))
|
|
doIt = false;
|
|
});
|
|
if (!doIt)
|
|
return;
|
|
|
|
if (impl->viewFactory && impl->nodes)
|
|
{
|
|
UINode* node = nullptr;
|
|
for (auto& childNode : impl->nodes->getChildren ())
|
|
{
|
|
if (childNode->getName () == Detail::MainNodeNames::kTemplate)
|
|
{
|
|
const std::string* nodeName = childNode->getAttributes ()->getAttributeValue ("name");
|
|
if (*nodeName == name)
|
|
{
|
|
node = childNode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (node == nullptr)
|
|
{
|
|
node = new UINode (Detail::MainNodeNames::kTemplate);
|
|
}
|
|
node->getChildren ().removeAll ();
|
|
updateAttributesForView (node, view);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::addNewTemplate (UTF8StringPtr name, const SharedPointer<UIAttributes>& attr)
|
|
{
|
|
#if VSTGUI_LIVE_EDITING
|
|
vstgui_assert (impl->nodes);
|
|
UINode* templateNode = findChildNodeByNameAttribute (impl->nodes, name);
|
|
if (templateNode == nullptr)
|
|
{
|
|
auto* newNode = new UINode (Detail::MainNodeNames::kTemplate, attr);
|
|
attr->setAttribute ("name", name);
|
|
impl->nodes->getChildren ().add (newNode);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescTemplateChanged (this);
|
|
});
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::removeTemplate (UTF8StringPtr name)
|
|
{
|
|
#if VSTGUI_LIVE_EDITING
|
|
UINode* templateNode = findChildNodeByNameAttribute (impl->nodes, name);
|
|
if (templateNode)
|
|
{
|
|
impl->nodes->getChildren ().remove (templateNode);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescTemplateChanged (this);
|
|
});
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::changeTemplateName (UTF8StringPtr name, UTF8StringPtr newName)
|
|
{
|
|
#if VSTGUI_LIVE_EDITING
|
|
UINode* templateNode = findChildNodeByNameAttribute (impl->nodes, name);
|
|
if (templateNode)
|
|
{
|
|
templateNode->getAttributes()->setAttribute ("name", newName);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescTemplateChanged (this);
|
|
});
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::duplicateTemplate (UTF8StringPtr name, UTF8StringPtr duplicateName)
|
|
{
|
|
#if VSTGUI_LIVE_EDITING
|
|
UINode* templateNode = findChildNodeByNameAttribute (impl->nodes, name);
|
|
if (templateNode)
|
|
{
|
|
auto* duplicate = new UINode (*templateNode);
|
|
vstgui_assert (duplicate);
|
|
if (duplicate)
|
|
{
|
|
duplicate->getAttributes()->setAttribute ("name", duplicateName);
|
|
impl->nodes->getChildren ().add (duplicate);
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescTemplateChanged (this);
|
|
});
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::setCustomAttributes (UTF8StringPtr name, const SharedPointer<UIAttributes>& attr)
|
|
{
|
|
UINode* customNode = findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kCustom), name);
|
|
if (customNode)
|
|
return false;
|
|
UINode* parent = getBaseNode (Detail::MainNodeNames::kCustom);
|
|
vstgui_assert (parent != nullptr);
|
|
if (!parent)
|
|
return false;
|
|
attr->setAttribute ("name", name);
|
|
customNode = new UINode ("attributes", attr);
|
|
parent->getChildren ().add (customNode);
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
SharedPointer<UIAttributes> UIDescription::getCustomAttributes (UTF8StringPtr name) const
|
|
{
|
|
auto node =
|
|
findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kCustom, false), name);
|
|
return node ? node->getAttributes () : nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
SharedPointer<UIAttributes> UIDescription::getCustomAttributes (UTF8StringPtr name, bool create)
|
|
{
|
|
auto attributes = getCustomAttributes (name);
|
|
if (attributes)
|
|
return attributes;
|
|
if (create)
|
|
{
|
|
auto attrs = makeOwned<UIAttributes> ();
|
|
if (setCustomAttributes (name, attrs))
|
|
return attrs;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
UIDescription::FocusDrawing UIDescription::getFocusDrawingSettings () const
|
|
{
|
|
FocusDrawing fd;
|
|
auto attributes = getCustomAttributes ("FocusDrawing");
|
|
if (attributes)
|
|
{
|
|
attributes->getBooleanAttribute ("enabled", fd.enabled);
|
|
attributes->getDoubleAttribute ("width", fd.width);
|
|
if (auto colorAttr = attributes->getAttributeValue ("color"))
|
|
fd.colorName = *colorAttr;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void UIDescription::setFocusDrawingSettings (const FocusDrawing& fd)
|
|
{
|
|
auto attributes = getCustomAttributes ("FocusDrawing", true);
|
|
if (!attributes)
|
|
return;
|
|
attributes->setBooleanAttribute ("enabled", fd.enabled);
|
|
attributes->setDoubleAttribute ("width", fd.width);
|
|
attributes->setAttribute ("color", fd.colorName.getString ());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::getControlTagString (UTF8StringPtr tagName, std::string& tagString) const
|
|
{
|
|
auto* controlTagNode = dynamic_cast<Detail::UIControlTagNode*> (findChildNodeByNameAttribute (getBaseNode (Detail::MainNodeNames::kControlTag), tagName));
|
|
if (controlTagNode)
|
|
{
|
|
const std::string* tagStr = controlTagNode->getTagString ();
|
|
if (tagStr)
|
|
{
|
|
tagString = *tagStr;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::changeControlTagString (UTF8StringPtr tagName, const std::string& newTagString, bool create)
|
|
{
|
|
UINode* tagsNode = getBaseNode (Detail::MainNodeNames::kControlTag);
|
|
if (auto* controlTagNode =
|
|
dynamic_cast<Detail::UIControlTagNode*> (findChildNodeByNameAttribute (tagsNode, tagName)))
|
|
{
|
|
if (create)
|
|
return false;
|
|
controlTagNode->setTagString (newTagString);
|
|
impl->forEachListener ([this](UIDescriptionListener* l) { l->onUIDescTagChanged (this); });
|
|
return true;
|
|
}
|
|
if (create)
|
|
{
|
|
if (tagsNode)
|
|
{
|
|
auto attr = makeOwned<UIAttributes> ();
|
|
attr->setAttribute ("name", tagName);
|
|
auto* node = new Detail::UIControlTagNode ("control-tag", attr);
|
|
node->setTagString (newTagString);
|
|
tagsNode->getChildren ().add (node);
|
|
tagsNode->sortChildren ();
|
|
impl->forEachListener ([this] (UIDescriptionListener* l) {
|
|
l->onUIDescTagChanged (this);
|
|
});
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::getVariable (UTF8StringPtr name, double& value) const
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIVariableNode*> (
|
|
findChildNodeByNameAttribute (impl->getVariableBaseNode (), name));
|
|
if (node)
|
|
{
|
|
if (node->getType () == Detail::UIVariableNode::kNumber)
|
|
{
|
|
value = node->getNumber ();
|
|
return true;
|
|
}
|
|
if (node->getType () == Detail::UIVariableNode::kString)
|
|
{
|
|
double v;
|
|
if (calculateStringValue (node->getString ().c_str (), v))
|
|
{
|
|
value = v;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::getVariable (UTF8StringPtr name, std::string& value) const
|
|
{
|
|
auto* node = dynamic_cast<Detail::UIVariableNode*> (findChildNodeByNameAttribute (impl->getVariableBaseNode (), name));
|
|
if (node)
|
|
{
|
|
value = node->getString ();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace UIDescriptionPrivate {
|
|
|
|
using Locale = Detail::Locale;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
class StringToken : public std::string
|
|
{
|
|
public:
|
|
enum Type {
|
|
kString,
|
|
kAdd,
|
|
kSubtract,
|
|
kMulitply,
|
|
kDivide,
|
|
kOpenParenthesis,
|
|
kCloseParenthesis,
|
|
kResult
|
|
};
|
|
|
|
explicit StringToken (const std::string& str) : std::string (str), type (kString), result (0) {}
|
|
StringToken (const StringToken& token) = default;
|
|
StringToken (Type type, double value = 0) : type (type), result (value) {}
|
|
|
|
Type type;
|
|
double result;
|
|
};
|
|
|
|
using StringTokenList = std::list<StringToken>;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static bool tokenizeString (std::string& str, StringTokenList& tokens)
|
|
{
|
|
UTF8CodePointIterator<std::string::const_iterator> start (str.begin ());
|
|
UTF8CodePointIterator<std::string::const_iterator> end (str.end ());
|
|
auto iterator = start;
|
|
while (iterator != end)
|
|
{
|
|
auto codePoint = *iterator;
|
|
if (isspace (codePoint))
|
|
{
|
|
if (start != iterator)
|
|
tokens.emplace_back (std::string {start.base (), iterator.base ()});
|
|
start = iterator;
|
|
++start;
|
|
}
|
|
else
|
|
{
|
|
switch (codePoint)
|
|
{
|
|
case '+':
|
|
{
|
|
if (start != iterator)
|
|
tokens.emplace_back (std::string {start.base (), iterator.base ()});
|
|
tokens.emplace_back (StringToken::kAdd);
|
|
start = iterator;
|
|
++start;
|
|
break;
|
|
}
|
|
case '-':
|
|
{
|
|
if (start != iterator)
|
|
tokens.emplace_back (std::string {start.base (), iterator.base ()});
|
|
tokens.emplace_back (StringToken::kSubtract);
|
|
start = iterator;
|
|
++start;
|
|
break;
|
|
}
|
|
case '*':
|
|
{
|
|
if (start != iterator)
|
|
tokens.emplace_back (std::string {start.base (), iterator.base ()});
|
|
tokens.emplace_back (StringToken::kMulitply);
|
|
start = iterator;
|
|
++start;
|
|
break;
|
|
}
|
|
case '/':
|
|
{
|
|
if (start != iterator)
|
|
tokens.emplace_back (std::string {start.base (), iterator.base ()});
|
|
tokens.emplace_back (StringToken::kDivide);
|
|
start = iterator;
|
|
++start;
|
|
break;
|
|
}
|
|
case '(':
|
|
{
|
|
if (start != iterator)
|
|
tokens.emplace_back (std::string {start.base (), iterator.base ()});
|
|
tokens.emplace_back (StringToken::kOpenParenthesis);
|
|
start = iterator;
|
|
++start;
|
|
break;
|
|
}
|
|
case ')':
|
|
{
|
|
if (start != iterator)
|
|
tokens.emplace_back (std::string {start.base (), iterator.base ()});
|
|
tokens.emplace_back (StringToken::kCloseParenthesis);
|
|
start = iterator;
|
|
++start;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
++iterator;
|
|
}
|
|
if (start != iterator)
|
|
tokens.emplace_back (std::string {start.base (), iterator.base ()});
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static bool computeTokens (StringTokenList& tokens, double& result)
|
|
{
|
|
int32_t openCount = 0;
|
|
StringTokenList::iterator openPosition = tokens.end ();
|
|
// first check parentheses
|
|
for (StringTokenList::iterator it = tokens.begin (); it != tokens.end (); ++it)
|
|
{
|
|
if ((*it).type == StringToken::kOpenParenthesis)
|
|
{
|
|
openCount++;
|
|
if (openCount == 1)
|
|
openPosition = it;
|
|
}
|
|
else if ((*it).type == StringToken::kCloseParenthesis)
|
|
{
|
|
openCount--;
|
|
if (openCount == 0)
|
|
{
|
|
StringTokenList tmp (++openPosition, it);
|
|
double value = 0;
|
|
if (computeTokens (tmp, value))
|
|
{
|
|
--openPosition;
|
|
++it;
|
|
tokens.erase (openPosition, it);
|
|
tokens.insert (it, StringToken (StringToken::kResult, value));
|
|
if (it == tokens.end ())
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// now multiply and divide
|
|
StringTokenList::iterator prevToken = tokens.begin ();
|
|
for (StringTokenList::iterator it = tokens.begin (); it != tokens.end (); ++it)
|
|
{
|
|
if (prevToken != it)
|
|
{
|
|
if ((*it).type == StringToken::kMulitply)
|
|
{
|
|
if ((*prevToken).type == StringToken::kResult)
|
|
{
|
|
++it;
|
|
if ((*it).type == StringToken::kResult)
|
|
{
|
|
double value = (*prevToken).result * (*it).result;
|
|
++it;
|
|
tokens.erase (prevToken, it);
|
|
tokens.insert (it, StringToken (StringToken::kResult, value));
|
|
--it;
|
|
prevToken = it;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if ((*it).type == StringToken::kDivide)
|
|
{
|
|
if ((*prevToken).type == StringToken::kResult)
|
|
{
|
|
++it;
|
|
if ((*it).type == StringToken::kResult)
|
|
{
|
|
double value = (*prevToken).result / (*it).result;
|
|
++it;
|
|
tokens.erase (prevToken, it);
|
|
tokens.insert (it, StringToken (StringToken::kResult, value));
|
|
--it;
|
|
prevToken = it;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prevToken = it;
|
|
}
|
|
}
|
|
}
|
|
// now add and subtract
|
|
int32_t lastType = -1;
|
|
for (StringTokenList::const_iterator it = tokens.begin (); it != tokens.end (); ++it)
|
|
{
|
|
if ((*it).type == UIDescriptionPrivate::StringToken::kResult)
|
|
{
|
|
double value = (*it).result;
|
|
if (lastType == -1)
|
|
result = value;
|
|
else if (lastType == StringToken::kAdd)
|
|
result += value;
|
|
else if (lastType == StringToken::kSubtract)
|
|
result -= value;
|
|
else
|
|
{
|
|
#if DEBUG
|
|
DebugPrint ("Wrong Expression: %d\n", (*it).type);
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
else if (!(lastType == -1 || lastType == UIDescriptionPrivate::StringToken::kResult))
|
|
{
|
|
#if DEBUG
|
|
DebugPrint ("Wrong Expression: %d\n", (*it).type);
|
|
#endif
|
|
return false;
|
|
}
|
|
lastType = (*it).type;
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // UIDescriptionPrivate
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool UIDescription::calculateStringValue (UTF8StringPtr _str, double& result) const
|
|
{
|
|
UIDescriptionPrivate::Locale localeResetter;
|
|
|
|
char* endPtr = nullptr;
|
|
result = strtod (_str, &endPtr);
|
|
if (endPtr == _str + strlen (_str))
|
|
return true;
|
|
std::string str (_str);
|
|
UIDescriptionPrivate::StringTokenList tokens;
|
|
if (!UIDescriptionPrivate::tokenizeString (str, tokens))
|
|
{
|
|
#if DEBUG
|
|
DebugPrint("TokenizeString failed :%s\n", _str);
|
|
#endif
|
|
return false;
|
|
}
|
|
// first make substituation
|
|
for (auto& token : tokens)
|
|
{
|
|
if (token.type == UIDescriptionPrivate::StringToken::kString)
|
|
{
|
|
const char* tokenStr = token.c_str ();
|
|
double value = strtod (tokenStr, &endPtr);
|
|
if (endPtr != tokenStr + token.length ())
|
|
{
|
|
// if it is not pure numeric try to substitute the string with a control tag or variable
|
|
if (token.find ("tag.") == 0)
|
|
{
|
|
value = getTagForName (token.c_str () + 4);
|
|
if (value == -1)
|
|
{
|
|
#if DEBUG
|
|
DebugPrint("Tag not found :%s\n", tokenStr);
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
else if (token.find ("var.") == 0)
|
|
{
|
|
double v;
|
|
if (getVariable (token.c_str () + 4, v))
|
|
{
|
|
value = v;
|
|
}
|
|
else
|
|
{
|
|
#if DEBUG
|
|
DebugPrint("Variable not found :%s\n", tokenStr);
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if DEBUG
|
|
DebugPrint("Substitution failed :%s\n", tokenStr);
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
token.result = value;
|
|
token.type = UIDescriptionPrivate::StringToken::kResult;
|
|
}
|
|
}
|
|
result = 0;
|
|
return UIDescriptionPrivate::computeTokens (tokens, result);
|
|
}
|
|
|
|
} // VSTGUI
|