diff --git a/include/lgi/common/XmlTreeUi.h b/include/lgi/common/XmlTreeUi.h --- a/include/lgi/common/XmlTreeUi.h +++ b/include/lgi/common/XmlTreeUi.h @@ -1,43 +1,43 @@ /// \file /// \author Matthew Allen #ifndef _XML_TREE_UI_H_ #define _XML_TREE_UI_H_ #include "lgi/common/Variant.h" #include "lgi/common/List.h" #include "lgi/common/Tree.h" /// This class allows you to serialize data between the user interface and a /// LDom instance. Useful for storing user visible application settings in /// XML or an optons file. class LXmlTreeUi { class LXmlTreeUiPriv *d; public: + typedef LHashTbl,int> EnumMap; + LXmlTreeUi(); virtual ~LXmlTreeUi(); - typedef LListItem *(*CreateListItem)(void *User); - typedef LTreeItem *(*CreateTreeItem)(void *User); - /// Create attribute <-> UI element mapping for generic control void Map(const char *Attr, int UiIdent, int Type = GV_NULL); - /// Create attribute <-> UI element mapping for GList control - void Map(const char *Element, int UiIdent, CreateListItem Factory, const char *ChildElements, void *User = 0); - /// Create attribute <-> UI element mapping for LTree control - void Map(const char *Element, int UiIdent, CreateTreeItem Factory, const char *ChildElements, void *User = 0); + /// Create attribute <-> UI element mapping for LItemContainer control + void Map(const char *Attr, int UiIdent, const char *ChildElementName, std::function Callback); + /// Create attribute <-> UI element mapping for a enum and group of radio buttons. + void Map(const char *Attr, EnumMap &Mapping); /// Clear all mappings void EmptyMaps(); + /// Convert data to/from an XML tag virtual bool Convert(LDom *Tag, LViewI *ui, bool ToUI); /// Disable/enable all control void EnableAll(LViewI *ui, bool Enable); /// Empty all controls of text / value void EmptyAll(LViewI *ui); /// Returns true if the attribute is mapped bool IsMapped(const char *Attr); }; #endif \ No newline at end of file diff --git a/src/common/Text/XmlTreeUi.cpp b/src/common/Text/XmlTreeUi.cpp --- a/src/common/Text/XmlTreeUi.cpp +++ b/src/common/Text/XmlTreeUi.cpp @@ -1,432 +1,460 @@ /// \file /// \author Matthew Allen #include "lgi/common/Lgi.h" #include "lgi/common/XmlTreeUi.h" #include "lgi/common/CheckBox.h" #include "lgi/common/Button.h" #include "lgi/common/RadioGroup.h" #include "lgi/common/Slider.h" #include "lgi/common/Combo.h" #include "lgi/common/Edit.h" #include "lgi/common/Tree.h" #include struct Mapping { - int Id; - int Hint; - LXmlTreeUi::CreateListItem ListItemFactory; - LXmlTreeUi::CreateTreeItem TreeItemFactory; - LVariant ChildElements; - void *User; - - Mapping() - { - Id = 0; - Hint = GV_NULL; - ListItemFactory = 0; - TreeItemFactory = 0; - User = 0; - } + int Id = 0; + int Hint = GV_NULL; + std::function Callback; + LString ChildElementName; + LXmlTreeUi::EnumMap EnumMap; void LoadTree(LTreeNode *n, LXmlTag *t) { for (auto c: t->Children) { - LTreeItem *i = TreeItemFactory(User); + auto i = dynamic_cast(Callback()); if (i) { i->XmlIo(c, false); n->Insert(i); LoadTree(i, c); } } } void SaveTree(LTreeNode *n, LXmlTag *t) { for (LTreeItem *i = n->GetChild(); i; i = i->GetNext()) { - LXmlTag *n = new LXmlTag(ChildElements.Str()); + LXmlTag *n = new LXmlTag(ChildElementName); if (n) { i->XmlIo(n, true); t->InsertTag(n); SaveTree(i, n); } } } }; class LXmlTreeUiPriv { public: LHashTbl,Mapping*> Maps; ~LXmlTreeUiPriv() { Maps.DeleteObjects(); } }; LXmlTreeUi::LXmlTreeUi() { d = new LXmlTreeUiPriv; } LXmlTreeUi::~LXmlTreeUi() { DeleteObj(d); } void LXmlTreeUi::EmptyAll(LViewI *Ui) { if (Ui) { // for (Mapping *m=d->Maps.First(); m; m=d->Maps.Next()) for (auto m : d->Maps) { if (m.value->Hint == GV_STRING) Ui->SetCtrlName(m.value->Id, 0); } } else LAssert(!"Invalid params"); } void LXmlTreeUi::EnableAll(LViewI *Ui, bool Enable) { if (Ui) { // for (Mapping *m=d->Maps.First(); m; m=d->Maps.Next()) for (auto m : d->Maps) { Ui->SetCtrlEnabled(m.value->Id, Enable); } } else LAssert(!"Invalid params"); } bool LXmlTreeUi::IsMapped(const char *Attr) { return d->Maps.Find(Attr) != NULL; } void LXmlTreeUi::Map(const char *Attr, int UiIdent, int Type) { if (UiIdent > 0 && (Attr != NULL || Type == GV_DOM)) { Mapping *m = new Mapping; if (m) { m->Id = UiIdent; m->Hint = Type; LAssert(!d->Maps.Find(Attr)); d->Maps.Add(Attr, m); } } else LAssert(!"Invalid params"); } -void LXmlTreeUi::Map(const char *Attr, int UiIdent, CreateListItem Factory, const char *ChildElements, void *User) +void LXmlTreeUi::Map(const char *Attr, int UiIdent, const char *ChildElementName, std::function Callback) { - if (Attr && UiIdent > 0 && Factory && ChildElements) + if (Attr && UiIdent > 0 && Callback && ChildElementName) { Mapping *m = new Mapping; if (m) { m->Id = UiIdent; - m->ListItemFactory = Factory; + m->Callback = Callback; m->Hint = GV_LIST; - m->ChildElements = ChildElements; - m->User = User; + m->ChildElementName = ChildElementName; LAssert(!d->Maps.Find(Attr)); d->Maps.Add(Attr, m); } } else LAssert(!"Invalid params"); } -void LXmlTreeUi::Map(const char *Attr, int UiIdent, CreateTreeItem Factory, const char *ChildElements, void *User) -{ - if (Attr && UiIdent > 0 && Factory && ChildElements) +void LXmlTreeUi::Map(const char *Attr, LHashTbl,int> &Map) +{ + if (Attr && Map.Length() > 0) { - Mapping *m = new Mapping; + auto m = new Mapping; if (m) { - m->Id = UiIdent; - m->TreeItemFactory = Factory; - m->Hint = GV_CUSTOM; - m->ChildElements = ChildElements; - m->User = User; - + m->Hint = GV_HASHTABLE; + m->EnumMap = Map; + LAssert(!d->Maps.Find(Attr)); d->Maps.Add(Attr, m); } } else LAssert(!"Invalid params"); -} +} void LXmlTreeUi::EmptyMaps() { d->Maps.DeleteObjects(); } int GetCtrlType(LViewI *v) { if (v) { if (dynamic_cast(v) || - dynamic_cast(v) || - dynamic_cast(v)) + dynamic_cast(v) || + dynamic_cast(v)) { return GV_BOOL; } else if (dynamic_cast(v) || - dynamic_cast(v) || - dynamic_cast(v)) + dynamic_cast(v) || + dynamic_cast(v)) { return GV_INT32; } else if (!Stricmp(v->GetClass(), "LControlTree")) { return GV_DOM; } } return GV_STRING; } int GetDataType(char *str) { if (str) { bool Float = false; char16 w; ssize_t Len = strlen(str); while ((w = LgiUtf8To32((uint8_t*&)str, Len))) { if (strchr("e \t\r\n", w)) { // Doesn't really tell us anything, so ignore it. // The 'e' part is sometimes part of a number or // ignore that too. } else if (!IsDigit(w) || w > 255) { return GV_STRING; } else if (w == '.') { Float = true; } } if (Float) { return GV_DOUBLE; } return GV_INT32; } else LAssert(!"Invalid params"); return GV_NULL; } bool LXmlTreeUi::Convert(LDom *Tag, LViewI *Ui, bool ToUI) { bool Status = false; if (Ui && Tag) { LVariant v; LXmlTag *Xml = dynamic_cast(Tag); if (ToUI) { // Xml -> UI - // const char *Attr; - // for (Mapping *Map = d->Maps.First(&Attr); Map; Map = d->Maps.Next(&Attr)) for (auto Map : d->Maps) { - if (Map.value->Hint == GV_LIST) + Mapping *m = Map.value; + switch (m->Hint) { - if (Xml) + case GV_HASHTABLE: { + Tag->GetValue(Map.key, v); + if (v.Str()) + { + for (auto e: m->EnumMap) + { + auto name = e.key; + if (!Stricmp(name, v.Str())) + Ui->SetCtrlValue(e.value, true); + } + } + break; + } + case GV_LIST: + { + if (!Xml) + { + LAssert(!"Needs an xml tag."); + break; + } + LXmlTag *t = Xml->GetChildTag(Map.key); if (!t) continue; LList *Lst; - if (!Ui->GetViewById(Map.value->Id, Lst)) continue; + if (!Ui->GetViewById(m->Id, Lst)) continue; Lst->Empty(); for (auto c: t->Children) { - LListItem *i = Map.value->ListItemFactory(Map.value->User); + auto i = dynamic_cast(m->Callback()); if (i) { i->XmlIo(c, false); Lst->Insert(i); } } + break; } - } - else if (Map.value->Hint == GV_CUSTOM) - { - if (Xml) + case GV_CUSTOM: { + if (!Xml) + { + LAssert(!"Needs an xml tag."); + break; + } + LXmlTag *t = Xml->GetChildTag(Map.key); if (!t) continue; LTree *Tree; - if (!Ui->GetViewById(Map.value->Id, Tree)) continue; + if (!Ui->GetViewById(m->Id, Tree)) continue; Tree->Empty(); - Map.value->LoadTree(Tree, t); + m->LoadTree(Tree, t); + break; } - } - else if (Map.value->Hint == GV_DOM) - { - LView *ct; - if (Ui->GetViewById(Map.value->Id, ct)) + case GV_DOM: { + LView *ct; + if (!Ui->GetViewById(m->Id, ct)) + break; + LVariant Ret; LArray Args; Args[0] = new LVariant(Xml); Args[1] = new LVariant(false); auto Param = LDomPropToString(ControlSerialize); ct->CallMethod(Param, &Ret, Args); Args.DeleteObjects(); - } - } - else if (Tag->GetValue(Map.key, v)) - { - int Type = Map.value->Hint ? Map.value->Hint : GetDataType(v.Str()); - - if (Type == GV_BOOL || - Type == GV_INT32 || - Type == GV_INT64) - { - Ui->SetCtrlValue(Map.value->Id, v.CastInt32()); + break; } - else + default: { - Ui->SetCtrlName(Map.value->Id, v.Str()); + if (Tag->GetValue(Map.key, v)) + { + int Type = m->Hint ? m->Hint : GetDataType(v.Str()); + + if (Type == GV_BOOL || + Type == GV_INT32 || + Type == GV_INT64) + { + Ui->SetCtrlValue(m->Id, v.CastInt32()); + } + else + { + Ui->SetCtrlName(m->Id, v.Str()); + } + Status = true; + } + else + { + LEdit *c; + if (Ui->GetViewById(m->Id, c)) + c->Name(""); + } + break; } - Status = true; - } - else - { - LEdit *c; - if (Ui->GetViewById(Map.value->Id, c)) - c->Name(""); } } } else { // UI -> Xml - // const char *Attr; - // for (Mapping *Map = d->Maps.First(&Attr); Map; Map = d->Maps.Next(&Attr)) for (auto Map : d->Maps) { - LViewI *c = Ui->FindControl(Map.value->Id); - if (c) + Mapping *m = Map.value; + if (m->Hint == GV_HASHTABLE) { - int Type = Map.value->Hint ? Map.value->Hint : GetCtrlType(c); - - switch (Type) + for (auto e: m->EnumMap) { - case GV_LIST: - { - if (!Xml) break; - LXmlTag *Child = Xml->GetChildTag(Map.key, true); - if (!Child) break; - LList *Lst = dynamic_cast(c); - if (!Lst) break; - Child->Empty(true); - Child->SetTag(Map.key); - - List All; - Lst->GetAll(All); - for (auto i: All) - { - LXmlTag *n = new LXmlTag(Map.value->ChildElements.Str()); - if (n) - { - i->XmlIo(n, true); - Child->InsertTag(n); - } - } - break; - } - case GV_CUSTOM: // LTree + if (Ui->GetCtrlValue(e.value)) { - if (!Xml) break; - LXmlTag *Child = Xml->GetChildTag(Map.key, true); - - if (!Child) break; - LTree *Tree = dynamic_cast(c); - - if (!Tree) break; - Child->Empty(true); - Child->SetTag(Map.key); - - Map.value->SaveTree(Tree, Child); - break; - } - case GV_INT32: - case GV_BOOL: - { - Tag->SetValue(Map.key, v = c->Value()); + Tag->SetValue(Map.key, v = e.key); Status = true; - break; - } - case GV_DOM: - { - LView *ct; - if (Ui->GetViewById(Map.value->Id, ct)) - { - LVariant Ret; - LArray Args; - Args[0] = new LVariant(Xml); - Args[1] = new LVariant(true); - ct->CallMethod(LDomPropToString(ControlSerialize), &Ret, Args); - Args.DeleteObjects(); - } - break; - } - default: - { - auto Str = c->Name(); - - if (ValidStr(Str)) - v = Str; - else - v.Empty(); - - Tag->SetValue(Map.key, v); - Status = true; - break; } } } else { - v.Empty(); - Tag->SetValue(Map.key, v); + LViewI *c = Ui->FindControl(m->Id); + if (c) + { + int Type = m->Hint ? m->Hint : GetCtrlType(c); + + switch (Type) + { + case GV_LIST: + { + if (!Xml) break; + LXmlTag *Child = Xml->GetChildTag(Map.key, true); + if (!Child) break; + LList *Lst = dynamic_cast(c); + if (!Lst) break; + Child->Empty(true); + Child->SetTag(Map.key); + + List All; + Lst->GetAll(All); + for (auto i: All) + { + LXmlTag *n = new LXmlTag(m->ChildElementName); + if (n) + { + i->XmlIo(n, true); + Child->InsertTag(n); + } + } + break; + } + case GV_CUSTOM: // LTree + { + if (!Xml) break; + LXmlTag *Child = Xml->GetChildTag(Map.key, true); + + if (!Child) break; + LTree *Tree = dynamic_cast(c); + + if (!Tree) break; + Child->Empty(true); + Child->SetTag(Map.key); + + m->SaveTree(Tree, Child); + break; + } + case GV_INT32: + case GV_BOOL: + { + Tag->SetValue(Map.key, v = c->Value()); + Status = true; + break; + } + case GV_DOM: + { + LView *ct; + if (Ui->GetViewById(m->Id, ct)) + { + LVariant Ret; + LArray Args; + Args[0] = new LVariant(Xml); + Args[1] = new LVariant(true); + ct->CallMethod(LDomPropToString(ControlSerialize), &Ret, Args); + Args.DeleteObjects(); + } + break; + } + default: + { + auto Str = c->Name(); + + if (ValidStr(Str)) + v = Str; + else + v.Empty(); + + Tag->SetValue(Map.key, v); + Status = true; + break; + } + } + } + else + { + v.Empty(); + Tag->SetValue(Map.key, v); + } } } } } else LAssert(!"Invalid params"); return Status; }