diff options
author | Steven Bennetts <steve@lindenlab.com> | 2009-06-21 08:04:56 +0000 |
---|---|---|
committer | Steven Bennetts <steve@lindenlab.com> | 2009-06-21 08:04:56 +0000 |
commit | 9ec432034dc3c45d7ce763eb02dae4cc7f6b8da8 (patch) | |
tree | 4a505c1e0919af52800b3ffb3eaf135e7d6f9ce6 /indra/llui/lluictrlfactory.cpp | |
parent | 351ebe9fcb76f3b99c2957004bb8493a904869ee (diff) |
merge -r 122421-124917 viewer-2.0.0-2 -> viewer-2.0.0-3
ignore-dead-branch
Diffstat (limited to 'indra/llui/lluictrlfactory.cpp')
-rw-r--r-- | indra/llui/lluictrlfactory.cpp | 1103 |
1 files changed, 828 insertions, 275 deletions
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 983cc53f69..24e4ad18e6 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -41,6 +41,8 @@ #include "llcontrol.h" #include "lldir.h" #include "v4color.h" +#include "v3dmath.h" +#include "llquaternion.h" // this library includes #include "llbutton.h" @@ -64,7 +66,6 @@ #include "llmultisliderctrl.h" #include "llspinctrl.h" #include "lltabcontainer.h" -#include "lltabcontainervertical.h" #include "lltextbox.h" #include "lltexteditor.h" #include "llui.h" @@ -77,37 +78,43 @@ const S32 VPAD = 4; const S32 FLOATER_H_MARGIN = 15; const S32 MIN_WIDGET_HEIGHT = 10; -std::vector<std::string> LLUICtrlFactory::sXUIPaths; +LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION("Widget Construction"); +LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS("Widget InitFromParams"); +LLFastTimer::DeclareTimer FTM_WIDGET_SETUP("Widget Setup"); + +//----------------------------------------------------------------------------- +// Register widgets that are purely data driven here so they get linked in +#include "llstatview.h" +static LLDefaultWidgetRegistry::Register<LLStatView> register_stat_view("stat_view"); + +//----------------------------------------------------------------------------- // UI Ctrl class for padding class LLUICtrlLocate : public LLUICtrl { public: - LLUICtrlLocate() : LLUICtrl(std::string("locate"), LLRect(0,0,0,0), FALSE, NULL, NULL) { setTabStop(FALSE); } - virtual void draw() { } - - static LLView *fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { - std::string name("pad"); - node->getAttributeString("name", name); + Params() + { + name = "locate"; + tab_stop = false; + } + }; + + LLUICtrlLocate(const Params& p) : LLUICtrl(p) {} + virtual void draw() { } - LLUICtrlLocate *new_ctrl = new LLUICtrlLocate(); - new_ctrl->setName(name); - new_ctrl->initFromXML(node, parent); - return new_ctrl; - } }; -static LLRegisterWidget<LLUICtrlLocate> r1("locate"); -static LLRegisterWidget<LLUICtrlLocate> r2("pad"); +static LLDefaultWidgetRegistry::Register<LLUICtrlLocate> r1("locate"); //----------------------------------------------------------------------------- // LLUICtrlFactory() //----------------------------------------------------------------------------- LLUICtrlFactory::LLUICtrlFactory() - : mDummyPanel(NULL) + : mDummyPanel(NULL) // instantiated when first needed { - setupPaths(); } LLUICtrlFactory::~LLUICtrlFactory() @@ -116,141 +123,140 @@ LLUICtrlFactory::~LLUICtrlFactory() mDummyPanel = NULL; } -void LLUICtrlFactory::setupPaths() +void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block) { - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "paths.xml"); + std::string filename = std::string("widgets") + gDirUtilp->getDirDelimiter() + widget_tag + ".xml"; + LLXMLNodePtr root_node; - LLXMLNodePtr root; - BOOL success = LLXMLNode::parseFile(filename, root, NULL); - sXUIPaths.clear(); - - if (success) + if (LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) { - LLXMLNodePtr path; - - for (path = root->getFirstChild(); path.notNull(); path = path->getNextSibling()) + LLXUIParser::instance().readXUI(root_node, block); + } +} + +//static +void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, LLXMLNodePtr output_node) +{ + if (node.isNull()) return; + + for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling()) + { + LLXMLNodePtr outputChild; + if (output_node) { - LLUIString path_val_ui(path->getValue()); - std::string language = LLUI::getLanguage(); - path_val_ui.setArg("[LANGUAGE]", language); + outputChild = output_node->createChild("", FALSE); + } - if (std::find(sXUIPaths.begin(), sXUIPaths.end(), path_val_ui.getString()) == sXUIPaths.end()) - { - sXUIPaths.push_back(path_val_ui.getString()); - } + if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, outputChild, viewp->getChildRegistry())) + { + std::string child_name = std::string(child_node->getName()->mString); + llwarns << "Could not create widget named " << child_node->getName()->mString << llendl; + } + + if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty()) + { + output_node->deleteChild(outputChild); } } - else // parsing failed - { - std::string slash = gDirUtilp->getDirDelimiter(); - std::string dir = "xui" + slash + "en-us"; - llwarns << "XUI::config file unable to open: " << filename << llendl; - sXUIPaths.push_back(dir); - } -} -// static -const std::vector<std::string>& LLUICtrlFactory::getXUIPaths() -{ - return sXUIPaths; } +LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing"); //----------------------------------------------------------------------------- // getLayeredXMLNode() //----------------------------------------------------------------------------- bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) { - std::string full_filename = gDirUtilp->findSkinnedFilename(sXUIPaths.front(), xui_filename); - if (full_filename.empty()) - { - llwarns << "Couldn't find UI description file: " << sXUIPaths.front() + gDirUtilp->getDirDelimiter() + xui_filename << llendl; - return false; - } + LLFastTimer timer(FTM_XML_PARSE); + return LLXMLNode::getLayeredXMLNode(xui_filename, root, LLUI::getXUIPaths()); +} + +//----------------------------------------------------------------------------- +// getLocalizedXMLNode() +//----------------------------------------------------------------------------- +bool LLUICtrlFactory::getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) +{ + LLFastTimer timer(FTM_XML_PARSE); + std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), xui_filename); if (!LLXMLNode::parseFile(full_filename, root, NULL)) { - // try filename as passed in since sometimes we load an xml file from a user-supplied path - if (!LLXMLNode::parseFile(xui_filename, root, NULL)) - { - llwarns << "Problem reading UI description file: " << xui_filename << llendl; - return false; - } + return false; } - - LLXMLNodePtr updateRoot; - - std::vector<std::string>::const_iterator itor; - - for (itor = sXUIPaths.begin(), ++itor; itor != sXUIPaths.end(); ++itor) + else { - std::string nodeName; - std::string updateName; - - std::string layer_filename = gDirUtilp->findSkinnedFilename((*itor), xui_filename); - if(layer_filename.empty()) - { - // no localized version of this file, that's ok, keep looking - continue; - } - - if (!LLXMLNode::parseFile(layer_filename, updateRoot, NULL)) - { - llwarns << "Problem reading localized UI description file: " << (*itor) + gDirUtilp->getDirDelimiter() + xui_filename << llendl; - return false; - } - - updateRoot->getAttributeString("name", updateName); - root->getAttributeString("name", nodeName); - - if (updateName == nodeName) - { - LLXMLNode::updateNode(root, updateRoot); - } + return true; } - - return true; } +static LLFastTimer::DeclareTimer BUILD_FLOATERS("Build Floaters"); //----------------------------------------------------------------------------- // buildFloater() //----------------------------------------------------------------------------- -void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filename, - const LLCallbackMap::map_t* factory_map, BOOL open) /* Flawfinder: ignore */ +void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filename, BOOL open_floater, LLXMLNodePtr output_node) { + LLFastTimer timer(BUILD_FLOATERS); LLXMLNodePtr root; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + //if exporting, only load the language being exported, + //instead of layering localized version on top of english + if (output_node) + { + if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root)) + { + llwarns << "Couldn't parse floater from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; + return; + } + } + else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) { + llwarns << "Couldn't parse floater from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; return; } // root must be called floater - if( !(root->hasName("floater") || root->hasName("multi_floater") ) ) + if( !(root->hasName("floater") || root->hasName("multi_floater")) ) { llwarns << "Root node should be named floater in: " << filename << llendl; return; } - if (factory_map) + lldebugs << "Building floater " << filename << llendl; + mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename)); { - mFactoryStack.push_front(factory_map); - } + if (!floaterp->getFactoryMap().empty()) + { + mFactoryStack.push_front(&floaterp->getFactoryMap()); + } - floaterp->initFloaterXML(root, NULL, this, open); /* Flawfinder: ignore */ + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + floaterp->getCommitCallbackRegistrar().pushScope(); + floaterp->getEnableCallbackRegistrar().pushScope(); + + floaterp->initFloaterXML(root, floaterp->getParent(), open_floater, output_node); - if (LLUI::sShowXUINames) - { - floaterp->setToolTip(filename); - } - - if (factory_map) - { - mFactoryStack.pop_front(); + if (LLUI::sShowXUINames) + { + floaterp->setToolTip(filename); + } + + floaterp->getCommitCallbackRegistrar().popScope(); + floaterp->getEnableCallbackRegistrar().popScope(); + + if (!floaterp->getFactoryMap().empty()) + { + mFactoryStack.pop_front(); + } } + mFileNames.pop_back(); +} - LLHandle<LLFloater> handle = floaterp->getHandle(); - mBuiltFloaters[handle] = filename; +LLFloater* LLUICtrlFactory::buildFloaterFromXML(const std::string& filename, BOOL open_floater) +{ + LLFloater* floater = new LLFloater(); + buildFloater(floater, filename, open_floater); + return floater; } //----------------------------------------------------------------------------- @@ -258,34 +264,33 @@ void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filen //----------------------------------------------------------------------------- S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename) { - llofstream out(filename); - if (!out.good()) - { - llwarns << "Unable to open " << filename << " for output." << llendl; - return 1; - } - - out << XML_HEADER; - - LLXMLNodePtr xml_node = viewp->getXML(); - - xml_node->writeToOstream(out); - - out.close(); return 0; } +static LLFastTimer::DeclareTimer BUILD_PANELS("Build Panels"); + //----------------------------------------------------------------------------- // buildPanel() //----------------------------------------------------------------------------- -BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, - const LLCallbackMap::map_t* factory_map) +BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, LLXMLNodePtr output_node) { + LLFastTimer timer(BUILD_PANELS); BOOL didPost = FALSE; LLXMLNodePtr root; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + //if exporting, only load the language being exported, + //instead of layering localized version on top of english + if (output_node) + { + if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root)) + { + llwarns << "Couldn't parse panel from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; + return didPost; + } + } + else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) { + llwarns << "Couldn't parse panel from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; return didPost; } @@ -296,246 +301,794 @@ BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, return didPost; } - if (factory_map) + lldebugs << "Building panel " << filename << llendl; + + mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename)); { - mFactoryStack.push_front(factory_map); + if (!panelp->getFactoryMap().empty()) + { + mFactoryStack.push_front(&panelp->getFactoryMap()); + } + + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->getCommitCallbackRegistrar().pushScope(); + panelp->getEnableCallbackRegistrar().pushScope(); + + didPost = panelp->initPanelXML(root, NULL, output_node); + + panelp->getCommitCallbackRegistrar().popScope(); + panelp->getEnableCallbackRegistrar().popScope(); + + if (LLUI::sShowXUINames) + { + panelp->setToolTip(filename); + } + + if (!panelp->getFactoryMap().empty()) + { + mFactoryStack.pop_front(); + } } + mFileNames.pop_back(); + return didPost; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +LLFastTimer::DeclareTimer FTM_CREATE_FROM_XML("Create child widget"); - didPost = panelp->initPanelXML(root, NULL, this); +LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, LLXMLNodePtr output_node, const widget_registry_t& registry) +{ + LLFastTimer timer(FTM_CREATE_FROM_XML); + std::string ctrl_type = node->getName()->mString; + LLStringUtil::toLower(ctrl_type); - if (LLUI::sShowXUINames) + const LLWidgetCreatorFunc* funcp = registry.getValue(ctrl_type); + if (funcp == NULL) { - panelp->setToolTip(filename); + return NULL; } - LLHandle<LLPanel> handle = panelp->getHandle(); - mBuiltPanels[handle] = filename; - - if (factory_map) + if (parent == NULL) { - mFactoryStack.pop_front(); + if (mDummyPanel == NULL) + { + LLPanel::Params p; + mDummyPanel = create<LLPanel>(p); + } + parent = mDummyPanel; } - - return didPost; + LLView *view = (*funcp)(node, parent, output_node); + if (LLUI::sShowXUINames && view && !filename.empty()) + { + view->setToolTip(filename); + } + + return view; } //----------------------------------------------------------------------------- -// buildMenu() +// createFactoryPanel() //----------------------------------------------------------------------------- -LLMenuGL *LLUICtrlFactory::buildMenu(const std::string &filename, LLView* parentp) +LLPanel* LLUICtrlFactory::createFactoryPanel(const std::string& name) { - // TomY TODO: Break this function into buildMenu and buildMenuBar - LLXMLNodePtr root; - LLMenuGL* menu; - - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + std::deque<const LLCallbackMap::map_t*>::iterator itor; + for (itor = mFactoryStack.begin(); itor != mFactoryStack.end(); ++itor) { - return NULL; + const LLCallbackMap::map_t* factory_map = *itor; + + // Look up this panel's name in the map. + LLCallbackMap::map_const_iter_t iter = factory_map->find( name ); + if (iter != factory_map->end()) + { + // Use the factory to create the panel, instead of using a default LLPanel. + LLPanel *ret = (LLPanel*) iter->second.mCallback( iter->second.mData ); + return ret; + } } + LLPanel::Params panel_p; + return create<LLPanel>(panel_p); +} - // root must be called panel - if( !root->hasName( "menu_bar" ) && !root->hasName( "menu" )) +//----------------------------------------------------------------------------- + +//static +BOOL LLUICtrlFactory::getAttributeColor(LLXMLNodePtr node, const std::string& name, LLColor4& color) +{ + std::string colorstring; + BOOL res = node->getAttributeString(name.c_str(), colorstring); + if (res && LLUI::sSettingGroups["color"]) { - llwarns << "Root node should be named menu bar or menu in : " << filename << llendl; - return NULL; + if (LLUI::sSettingGroups["color"]->controlExists(colorstring)) + { + color.setVec(LLUI::sSettingGroups["color"]->getColor(colorstring)); + } + else + { + res = FALSE; + } } + if (!res) + { + res = LLColor4::parseColor(colorstring, &color); + } + if (!res) + { + res = node->getAttributeColor(name.c_str(), color); + } + return res; +} - if (root->hasName("menu")) +//static +void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group) +{ + if (tab_group < 0) tab_group = parent->getLastTabGroup(); + parent->addChild(view, tab_group); +} + + +// Avoid directly using LLUI and LLDir in the template code +//static +std::string LLUICtrlFactory::findSkinnedFilename(const std::string& filename) +{ + return gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename); +} + +void LLUICtrlFactory::pushFactoryFunctions(const LLCallbackMap::map_t* map) +{ + mFactoryStack.push_back(map); +} + +void LLUICtrlFactory::popFactoryFunctions() +{ + if (!mFactoryStack.empty()) { - menu = (LLMenuGL*)LLMenuGL::fromXML(root, parentp, this); + mFactoryStack.pop_back(); } - else +} + +const widget_registry_t& LLUICtrlFactory::getWidgetRegistry(LLView* viewp) +{ + return viewp->getChildRegistry(); +} + + +// +// LLXUIParser +// +LLXUIParser::LLXUIParser() +: mLastWriteGeneration(-1), + mCurReadDepth(0) +{ + registerParserFuncs<bool>(boost::bind(&LLXUIParser::readBoolValue, this, _1), + boost::bind(&LLXUIParser::writeBoolValue, this, _1, _2)); + registerParserFuncs<std::string>(boost::bind(&LLXUIParser::readStringValue, this, _1), + boost::bind(&LLXUIParser::writeStringValue, this, _1, _2)); + registerParserFuncs<U8>(boost::bind(&LLXUIParser::readU8Value, this, _1), + boost::bind(&LLXUIParser::writeU8Value, this, _1, _2)); + registerParserFuncs<S8>(boost::bind(&LLXUIParser::readS8Value, this, _1), + boost::bind(&LLXUIParser::writeS8Value, this, _1, _2)); + registerParserFuncs<U16>(boost::bind(&LLXUIParser::readU16Value, this, _1), + boost::bind(&LLXUIParser::writeU16Value, this, _1, _2)); + registerParserFuncs<S16>(boost::bind(&LLXUIParser::readS16Value, this, _1), + boost::bind(&LLXUIParser::writeS16Value, this, _1, _2)); + registerParserFuncs<U32>(boost::bind(&LLXUIParser::readU32Value, this, _1), + boost::bind(&LLXUIParser::writeU32Value, this, _1, _2)); + registerParserFuncs<S32>(boost::bind(&LLXUIParser::readS32Value, this, _1), + boost::bind(&LLXUIParser::writeS32Value, this, _1, _2)); + registerParserFuncs<F32>(boost::bind(&LLXUIParser::readF32Value, this, _1), + boost::bind(&LLXUIParser::writeF32Value, this, _1, _2)); + registerParserFuncs<F64>(boost::bind(&LLXUIParser::readF64Value, this, _1), + boost::bind(&LLXUIParser::writeF64Value, this, _1, _2)); + registerParserFuncs<LLColor4>(boost::bind(&LLXUIParser::readColor4Value, this, _1), + boost::bind(&LLXUIParser::writeColor4Value, this, _1, _2)); + registerParserFuncs<LLUIColor>(boost::bind(&LLXUIParser::readUIColorValue, this, _1), + boost::bind(&LLXUIParser::writeUIColorValue, this, _1, _2)); + registerParserFuncs<LLUUID>(boost::bind(&LLXUIParser::readUUIDValue, this, _1), + boost::bind(&LLXUIParser::writeUUIDValue, this, _1, _2)); + registerParserFuncs<LLSD>(boost::bind(&LLXUIParser::readSDValue, this, _1), + boost::bind(&LLXUIParser::writeSDValue, this, _1, _2)); +} + +static LLFastTimer::DeclareTimer PARSE_XUI("XUI Parsing"); + +void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent) +{ + LLFastTimer timer(PARSE_XUI); + mNameStack.clear(); + mCurReadDepth = 0; + setParseSilently(silent); + + if (node.isNull()) { - menu = (LLMenuGL*)LLMenuBarGL::fromXML(root, parentp, this); + parserWarning("Invalid node"); } - - if (LLUI::sShowXUINames) + else { - menu->setToolTip(filename); + readXUIImpl(node, std::string(node->getName()->mString), block); } +} - return menu; +void LLXUIParser::writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::BaseBlock* diff_block) +{ + mLastWriteGeneration = -1; + mWriteRootNode = node; + block.serializeBlock(*this, Parser::name_stack_t(), diff_block); } -//----------------------------------------------------------------------------- -// buildMenu() -//----------------------------------------------------------------------------- -LLPieMenu *LLUICtrlFactory::buildPieMenu(const std::string &filename, LLView* parentp) +// go from a stack of names to a specific XML node +LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack) { - LLXMLNodePtr root; + name_stack_t name_stack; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) + for (name_stack_t::const_iterator it = stack.begin(); + it != stack.end(); + ++it) { - return NULL; + if (!it->first.empty()) + { + name_stack.push_back(*it); + } } - // root must be called panel - if( !root->hasName( LL_PIE_MENU_TAG )) + if (name_stack.empty() || mWriteRootNode.isNull()) return NULL; + + std::string attribute_name = name_stack.front().first; + + // heuristic to make font always attribute of parent node + bool is_font = (attribute_name == "font"); + // XML spec says that attributes have their whitespace normalized + // on parse: http://www.w3.org/TR/REC-xml/#AVNormalize + // Therefore text-oriented widgets that might have carriage returns + // have their values serialized as text contents, not the + // initial_value attribute. JC + if (attribute_name == "initial_value") { - llwarns << "Root node should be named " << LL_PIE_MENU_TAG << " in : " << filename << llendl; - return NULL; + const char* root_node_name = mWriteRootNode->getName()->mString; + if (!strcmp(root_node_name, "text") // LLTextBox + || !strcmp(root_node_name, "text_editor") + || !strcmp(root_node_name, "line_editor")) // for consistency + { + // writeStringValue will write to this node + return mWriteRootNode; + } } - std::string name("menu"); - root->getAttributeString("name", name); + for (name_stack_t::const_iterator it = ++name_stack.begin(); + it != name_stack.end(); + ++it) + { + attribute_name += "."; + attribute_name += it->first; + } - LLPieMenu *menu = new LLPieMenu(name); - parentp->addChild(menu); - menu->initXML(root, parentp, this); + // *NOTE: <string> elements for translation need to have whitespace + // preserved like "initial_value" above, however, the <string> node + // becomes an attribute of the containing floater or panel. + // Because all <string> elements must have a "name" attribute, and + // "name" is parsed first, just put the value into the last written + // child. + if (attribute_name == "string.value") + { + // The caller of will shortly call writeStringValue(), which sets + // this node's type to string, but we don't want to export type="string". + // Set the default for this node to suppress the export. + static LLXMLNodePtr default_node; + if (default_node.isNull()) + { + default_node = new LLXMLNode(); + // Force the node to have a string type + default_node->setStringValue( std::string() ); + } + mLastWrittenChild->setDefault(default_node); + // mLastWrittenChild is the "string" node part of "string.value", + // so the caller will call writeStringValue() into that node, + // setting the node text contents. + return mLastWrittenChild; + } + + LLXMLNodePtr attribute_node; - if (LLUI::sShowXUINames) + const char* attribute_cstr = attribute_name.c_str(); + if (name_stack.size() != 1 + && !is_font) { - menu->setToolTip(filename); + std::string child_node_name(mWriteRootNode->getName()->mString); + child_node_name += "."; + child_node_name += name_stack.front().first; + + LLXMLNodePtr child_node; + + if (mLastWriteGeneration == name_stack.front().second) + { + child_node = mLastWrittenChild; + } + else + { + mLastWriteGeneration = name_stack.front().second; + child_node = mWriteRootNode->createChild(child_node_name.c_str(), false); + } + + mLastWrittenChild = child_node; + + name_stack_t::const_iterator it = ++name_stack.begin(); + std::string short_attribute_name(it->first); + + for (++it; + it != name_stack.end(); + ++it) + { + short_attribute_name += "."; + short_attribute_name += it->first; + } + + if (child_node->hasAttribute(short_attribute_name.c_str())) + { + llerrs << "Attribute " << short_attribute_name << " already exists!" << llendl; + } + + attribute_node = child_node->createChild(short_attribute_name.c_str(), true); + } + else + { + if (mWriteRootNode->hasAttribute(attribute_cstr)) + { + mWriteRootNode->getAttribute(attribute_cstr, attribute_node); + } + else + { + attribute_node = mWriteRootNode->createChild(attribute_name.c_str(), true); + } } - return menu; + return attribute_node; } -//----------------------------------------------------------------------------- -// rebuild() -//----------------------------------------------------------------------------- -void LLUICtrlFactory::rebuild() + +bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, const std::string& scope, LLInitParam::BaseBlock& block) { - built_panel_t::iterator built_panel_it; - for (built_panel_it = mBuiltPanels.begin(); - built_panel_it != mBuiltPanels.end(); - ++built_panel_it) + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("."); + + bool values_parsed = false; + + // submit attributes for current node + values_parsed |= readAttributes(nodep, block); + + // treat text contents of xml node as "value" parameter + std::string text_contents = nodep->getSanitizedValue(); + if (!text_contents.empty()) { - std::string filename = built_panel_it->second; - LLPanel* panelp = built_panel_it->first.get(); - if (!panelp) + mCurReadNode = nodep; + mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration())); + // child nodes are not necessarily valid parameters (could be a child widget) + // so don't complain once we've recursed + bool silent = mCurReadDepth > 0; + block.submitValue(mNameStack, *this, silent); + mNameStack.pop_back(); + } + + // then traverse children + // child node must start with last name of parent node (our "scope") + // for example: "<button><button.param nested_param1="foo"><param.nested_param2 nested_param3="bar"/></button.param></button>" + // which equates to the following nesting: + // button + // param + // nested_param1 + // nested_param2 + // nested_param3 + mCurReadDepth++; + for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();) + { + std::string child_name(childp->getName()->mString); + S32 num_tokens_pushed = 0; + + // for non "dotted" child nodes check to see if child node maps to another widget type + // and if not, treat as a child element of the current node + // e.g. <button><rect left="10"/></button> will interpret <rect> as "button.rect" + // since there is no widget named "rect" + if (child_name.find(".") == std::string::npos) + { + mNameStack.push_back(std::make_pair(child_name, newParseGeneration())); + num_tokens_pushed++; + } + else + { + // parse out "dotted" name into individual tokens + tokenizer name_tokens(child_name, sep); + + tokenizer::iterator name_token_it = name_tokens.begin(); + if(name_token_it == name_tokens.end()) + { + childp = childp->getNextSibling(); + continue; + } + + // check for proper nesting + if(!scope.empty() && *name_token_it != scope) + { + childp = childp->getNextSibling(); + continue; + } + + // now ignore first token + ++name_token_it; + + // copy remaining tokens on to our running token list + for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push) + { + mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration())); + num_tokens_pushed++; + } + } + + // recurse and visit children XML nodes + if(readXUIImpl(childp, mNameStack.empty() ? scope : mNameStack.back().first, block)) { - continue; + // child node successfully parsed, remove from DOM + + values_parsed = true; + LLXMLNodePtr node_to_remove = childp; + childp = childp->getNextSibling(); + + nodep->deleteChild(node_to_remove); + } + else + { + childp = childp->getNextSibling(); } - llinfos << "Rebuilding UI panel " << panelp->getName() - << " from " << filename - << llendl; - BOOL visible = panelp->getVisible(); - panelp->setVisible(FALSE); - panelp->setFocus(FALSE); - panelp->deleteAllChildren(); - buildPanel(panelp, filename.c_str(), &panelp->getFactoryMap()); - panelp->setVisible(visible); + while(num_tokens_pushed-- > 0) + { + mNameStack.pop_back(); + } } + mCurReadDepth--; + return values_parsed; +} + +bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block) +{ + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("."); + + bool any_parsed = false; - built_floater_t::iterator built_floater_it; - for (built_floater_it = mBuiltFloaters.begin(); - built_floater_it != mBuiltFloaters.end(); - ++built_floater_it) + for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin(); + attribute_it != nodep->mAttributes.end(); + ++attribute_it) { - LLFloater* floaterp = built_floater_it->first.get(); - if (!floaterp) + S32 num_tokens_pushed = 0; + std::string attribute_name(attribute_it->first->mString); + mCurReadNode = attribute_it->second; + + tokenizer name_tokens(attribute_name, sep); + // copy remaining tokens on to our running token list + for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push) { - continue; + mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration())); + num_tokens_pushed++; } - std::string filename = built_floater_it->second; - llinfos << "Rebuilding UI floater " << floaterp->getName() - << " from " << filename - << llendl; - BOOL visible = floaterp->getVisible(); - floaterp->setVisible(FALSE); - floaterp->setFocus(FALSE); - floaterp->deleteAllChildren(); - gFloaterView->removeChild(floaterp); - buildFloater(floaterp, filename, &floaterp->getFactoryMap()); - floaterp->setVisible(visible); + // child nodes are not necessarily valid attributes, so don't complain once we've recursed + bool silent = mCurReadDepth > 0; + any_parsed |= block.submitValue(mNameStack, *this, silent); + + while(num_tokens_pushed-- > 0) + { + mNameStack.pop_back(); + } } + + return any_parsed; } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- +bool LLXUIParser::readBoolValue(void* val_ptr) +{ + S32 value; + bool success = mCurReadNode->getBoolValue(1, &value); + *((bool*)val_ptr) = (value != FALSE); + return success; +} -LLView *LLUICtrlFactory::createCtrlWidget(LLPanel *parent, LLXMLNodePtr node) +bool LLXUIParser::writeBoolValue(const void* val_ptr, const name_stack_t& stack) { - std::string ctrl_type = node->getName()->mString; - LLStringUtil::toLower(ctrl_type); - - LLWidgetClassRegistry::factory_func_t func = LLWidgetClassRegistry::getInstance()->getCreatorFunc(ctrl_type); + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) + { + node->setBoolValue(*((bool*)val_ptr)); + return true; + } + return false; +} - if (func == NULL) +bool LLXUIParser::readStringValue(void* val_ptr) +{ + *((std::string*)val_ptr) = mCurReadNode->getSanitizedValue(); + return true; +} + +bool LLXUIParser::writeStringValue(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) { - llwarns << "Unknown control type " << ctrl_type << llendl; - return NULL; + node->setStringValue(*((std::string*)val_ptr)); + return true; } + return false; +} - if (parent == NULL) +bool LLXUIParser::readU8Value(void* val_ptr) +{ + return mCurReadNode->getByteValue(1, (U8*)val_ptr); +} + +bool LLXUIParser::writeU8Value(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) { - if (mDummyPanel == NULL) - { - mDummyPanel = new LLPanel; - } - parent = mDummyPanel; + node->setUnsignedValue(*((U8*)val_ptr)); + return true; } - LLView *ctrl = func(node, parent, this); + return false; +} - return ctrl; +bool LLXUIParser::readS8Value(void* val_ptr) +{ + S32 value; + if(mCurReadNode->getIntValue(1, &value)) + { + *((S8*)val_ptr) = value; + return true; + } + return false; } -LLView* LLUICtrlFactory::createWidget(LLPanel *parent, LLXMLNodePtr node) +bool LLXUIParser::writeS8Value(const void* val_ptr, const name_stack_t& stack) { - LLView* view = createCtrlWidget(parent, node); + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) + { + node->setIntValue(*((S8*)val_ptr)); + return true; + } + return false; +} - S32 tab_group = parent->getLastTabGroup(); - node->getAttributeS32("tab_group", tab_group); +bool LLXUIParser::readU16Value(void* val_ptr) +{ + U32 value; + if(mCurReadNode->getUnsignedValue(1, &value)) + { + *((U16*)val_ptr) = value; + return true; + } + return false; +} - if (view) +bool LLXUIParser::writeU16Value(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) { - parent->addChild(view, tab_group); + node->setUnsignedValue(*((U16*)val_ptr)); + return true; } + return false; +} - return view; +bool LLXUIParser::readS16Value(void* val_ptr) +{ + S32 value; + if(mCurReadNode->getIntValue(1, &value)) + { + *((S16*)val_ptr) = value; + return true; + } + return false; } -//----------------------------------------------------------------------------- -// createFactoryPanel() -//----------------------------------------------------------------------------- -LLPanel* LLUICtrlFactory::createFactoryPanel(const std::string& name) +bool LLXUIParser::writeS16Value(const void* val_ptr, const name_stack_t& stack) { - std::deque<const LLCallbackMap::map_t*>::iterator itor; - for (itor = mFactoryStack.begin(); itor != mFactoryStack.end(); ++itor) + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) { - const LLCallbackMap::map_t* factory_map = *itor; + node->setIntValue(*((S16*)val_ptr)); + return true; + } + return false; +} - // Look up this panel's name in the map. - LLCallbackMap::map_const_iter_t iter = factory_map->find( name ); - if (iter != factory_map->end()) - { - // Use the factory to create the panel, instead of using a default LLPanel. - LLPanel *ret = (LLPanel*) iter->second.mCallback( iter->second.mData ); - return ret; - } +bool LLXUIParser::readU32Value(void* val_ptr) +{ + return mCurReadNode->getUnsignedValue(1, (U32*)val_ptr); +} + +bool LLXUIParser::writeU32Value(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) + { + node->setUnsignedValue(*((U32*)val_ptr)); + return true; } - return NULL; + return false; } -//----------------------------------------------------------------------------- +bool LLXUIParser::readS32Value(void* val_ptr) +{ + return mCurReadNode->getIntValue(1, (S32*)val_ptr); +} -//static -BOOL LLUICtrlFactory::getAttributeColor(LLXMLNodePtr node, const std::string& name, LLColor4& color) +bool LLXUIParser::writeS32Value(const void* val_ptr, const name_stack_t& stack) { - std::string colorstring; - BOOL res = node->getAttributeString(name.c_str(), colorstring); - if (res && LLUI::sColorsGroup) + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) { - if (LLUI::sColorsGroup->controlExists(colorstring)) - { - color.setVec(LLUI::sColorsGroup->getColor(colorstring)); - } - else - { - res = FALSE; - } + node->setIntValue(*((S32*)val_ptr)); + return true; } - if (!res) + return false; +} + +bool LLXUIParser::readF32Value(void* val_ptr) +{ + return mCurReadNode->getFloatValue(1, (F32*)val_ptr); +} + +bool LLXUIParser::writeF32Value(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) { - res = LLColor4::parseColor(colorstring, &color); - } - if (!res) + node->setFloatValue(*((F32*)val_ptr)); + return true; + } + return false; +} + +bool LLXUIParser::readF64Value(void* val_ptr) +{ + return mCurReadNode->getDoubleValue(1, (F64*)val_ptr); +} + +bool LLXUIParser::writeF64Value(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) { - res = node->getAttributeColor(name.c_str(), color); + node->setDoubleValue(*((F64*)val_ptr)); + return true; } - return res; + return false; } +bool LLXUIParser::readColor4Value(void* val_ptr) +{ + LLColor4* colorp = (LLColor4*)val_ptr; + if(mCurReadNode->getFloatValue(4, colorp->mV) >= 3) + { + return true; + } + + return false; +} + +bool LLXUIParser::writeColor4Value(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) + { + LLColor4 color = *((LLColor4*)val_ptr); + node->setFloatValue(4, color.mV); + return true; + } + return false; +} + +bool LLXUIParser::readUIColorValue(void* val_ptr) +{ + LLUIColor* param = (LLUIColor*)val_ptr; + LLColor4 color; + bool success = mCurReadNode->getFloatValue(4, color.mV) >= 3; + if (success) + { + param->set(color); + return true; + } + return false; +} + +bool LLXUIParser::writeUIColorValue(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) + { + LLUIColor color = *((LLUIColor*)val_ptr); + //RN: don't write out the color that is represented by a function + // rely on param block exporting to get the reference to the color settings + if (color.isUsingFunction()) return false; + node->setFloatValue(4, color.get().mV); + return true; + } + return false; +} + +bool LLXUIParser::readUUIDValue(void* val_ptr) +{ + LLUUID temp_id; + // LLUUID::set is destructive, so use temporary value + if (temp_id.set(mCurReadNode->getSanitizedValue())) + { + *(LLUUID*)(val_ptr) = temp_id; + return true; + } + return false; +} + +bool LLXUIParser::writeUUIDValue(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) + { + node->setStringValue(((LLUUID*)val_ptr)->asString()); + return true; + } + return false; +} + +bool LLXUIParser::readSDValue(void* val_ptr) +{ + *((LLSD*)val_ptr) = LLSD(mCurReadNode->getSanitizedValue()); + return true; +} + +bool LLXUIParser::writeSDValue(const void* val_ptr, const name_stack_t& stack) +{ + LLXMLNodePtr node = getNode(stack); + if (node.notNull()) + { + node->setStringValue(((LLSD*)val_ptr)->asString()); + return true; + } + return false; +} + +/*virtual*/ std::string LLXUIParser::getCurrentElementName() +{ + std::string full_name; + for (name_stack_t::iterator it = mNameStack.begin(); + it != mNameStack.end(); + ++it) + { + full_name += it->first + "."; // build up dotted names: "button.param.nestedparam." + } + + return full_name; +} + +void LLXUIParser::parserWarning(const std::string& message) +{ +#ifdef LL_WINDOWS + // use Visual Studo friendly formatting of output message for easy access to originating xml + llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str()); + utf16str += '\n'; + OutputDebugString(utf16str.c_str()); +#else + Parser::parserWarning(message); +#endif +} + +void LLXUIParser::parserError(const std::string& message) +{ +#ifdef LL_WINDOWS + llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str()); + utf16str += '\n'; + OutputDebugString(utf16str.c_str()); +#else + Parser::parserError(message); +#endif +} |