299 lines
9.1 KiB
C++
299 lines
9.1 KiB
C++
/**
|
|
* @file lluictrlfactory.cpp
|
|
* @brief Factory class for creating UI controls
|
|
*
|
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "linden_common.h"
|
|
|
|
#define LLUICTRLFACTORY_CPP
|
|
#include "lluictrlfactory.h"
|
|
|
|
#include "llxmlnode.h"
|
|
|
|
#include <fstream>
|
|
#include <boost/tokenizer.hpp>
|
|
|
|
// other library includes
|
|
#include "llcontrol.h"
|
|
#include "lldir.h"
|
|
#include "v4color.h"
|
|
#include "v3dmath.h"
|
|
#include "llquaternion.h"
|
|
|
|
// this library includes
|
|
#include "llpanel.h"
|
|
|
|
LLTrace::BlockTimerStatHandle FTM_WIDGET_CONSTRUCTION("Widget Construction");
|
|
LLTrace::BlockTimerStatHandle FTM_INIT_FROM_PARAMS("Widget InitFromParams");
|
|
LLTrace::BlockTimerStatHandle FTM_WIDGET_SETUP("Widget Setup");
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// UI Ctrl class for padding
|
|
class LLUICtrlLocate : public LLUICtrl
|
|
{
|
|
public:
|
|
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
|
|
{
|
|
Params()
|
|
{
|
|
name = "locate";
|
|
tab_stop = false;
|
|
}
|
|
};
|
|
|
|
LLUICtrlLocate(const Params& p) : LLUICtrl(p) {}
|
|
virtual void draw() { }
|
|
|
|
};
|
|
|
|
static LLDefaultChildRegistry::Register<LLUICtrlLocate> r1("locate");
|
|
|
|
// Build time optimization, generate this once in .cpp file
|
|
template class LLUICtrlFactory* LLSingleton<class LLUICtrlFactory>::getInstance();
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// LLUICtrlFactory()
|
|
//-----------------------------------------------------------------------------
|
|
LLUICtrlFactory::LLUICtrlFactory()
|
|
: mDummyPanel(NULL) // instantiated when first needed
|
|
{
|
|
}
|
|
|
|
LLUICtrlFactory::~LLUICtrlFactory()
|
|
{
|
|
// go ahead and leak mDummyPanel since this is static destructor time
|
|
//delete mDummyPanel;
|
|
//mDummyPanel = NULL;
|
|
}
|
|
|
|
void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block)
|
|
{
|
|
std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml");
|
|
LLXMLNodePtr root_node;
|
|
std::vector<std::string> search_paths =
|
|
gDirUtilp->findSkinnedFilenames(LLDir::XUI, filename);
|
|
|
|
if (search_paths.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// "en" version, the default-language version of the file.
|
|
std::string base_filename = search_paths.front();
|
|
if (!base_filename.empty())
|
|
{
|
|
LLUICtrlFactory::instance().pushFileName(base_filename);
|
|
|
|
if (!LLXMLNode::getLayeredXMLNode(root_node, search_paths))
|
|
{
|
|
LL_WARNS() << "Couldn't parse widget from: " << base_filename << LL_ENDL;
|
|
return;
|
|
}
|
|
LLXUIParser parser;
|
|
parser.readXUI(root_node, block, base_filename);
|
|
LLUICtrlFactory::instance().popFileName();
|
|
}
|
|
}
|
|
|
|
static LLTrace::BlockTimerStatHandle FTM_CREATE_CHILDREN("Create XUI Children");
|
|
|
|
//static
|
|
void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t& registry, LLXMLNodePtr output_node)
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_CREATE_CHILDREN);
|
|
if (node.isNull()) return;
|
|
|
|
for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling())
|
|
{
|
|
LLXMLNodePtr outputChild;
|
|
if (output_node)
|
|
{
|
|
outputChild = output_node->createChild("", FALSE);
|
|
}
|
|
|
|
if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild))
|
|
{
|
|
// child_node is not a valid child for the current parent
|
|
std::string child_name = std::string(child_node->getName()->mString);
|
|
if (LLDefaultChildRegistry::instance().getValue(child_name))
|
|
{
|
|
// This means that the registry assocaited with the parent widget does not have an entry
|
|
// for the child widget
|
|
// You might need to add something like:
|
|
// static ParentWidgetRegistry::Register<ChildWidgetType> register("child_widget_name");
|
|
// <FS:Ansariel> Print more details so we can actually fix that!
|
|
//LL_WARNS() << child_name << " is not a valid child of " << node->getName()->mString << LL_ENDL;
|
|
std::string name;
|
|
node->getAttributeString("name", name);
|
|
LL_WARNS() << child_name << " is not a valid child of " << node->getName()->mString << " for " << name << " (line no. " << node->getLineNumber() << ")" << LL_ENDL;
|
|
// </FS:Ansariel>
|
|
}
|
|
else
|
|
{
|
|
// <FS:Ansariel> Print more details so we can actually fix that!
|
|
//LL_WARNS() << "Could not create widget named " << child_node->getName()->mString << LL_ENDL;
|
|
std::string name;
|
|
node->getAttributeString("name", name);
|
|
LL_WARNS() << "Could not create widget named " << child_node->getName()->mString << " for " << name << " (line no. " << node->getLineNumber() << ")" << LL_ENDL;
|
|
// </FS:Ansariel>
|
|
}
|
|
}
|
|
|
|
if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty())
|
|
{
|
|
output_node->deleteChild(outputChild);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static LLTrace::BlockTimerStatHandle FTM_XML_PARSE("XML Reading/Parsing");
|
|
//-----------------------------------------------------------------------------
|
|
// getLayeredXMLNode()
|
|
//-----------------------------------------------------------------------------
|
|
bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root,
|
|
LLDir::ESkinConstraint constraint)
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_XML_PARSE);
|
|
std::vector<std::string> paths =
|
|
gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint);
|
|
|
|
if (paths.empty())
|
|
{
|
|
// sometimes whole path is passed in as filename
|
|
paths.push_back(xui_filename);
|
|
}
|
|
|
|
return LLXMLNode::getLayeredXMLNode(root, paths);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// saveToXML()
|
|
//-----------------------------------------------------------------------------
|
|
S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static LLTrace::BlockTimerStatHandle FTM_CREATE_FROM_XML("Create child widget");
|
|
|
|
LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t& registry, LLXMLNodePtr output_node)
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_CREATE_FROM_XML);
|
|
std::string ctrl_type = node->getName()->mString;
|
|
LLStringUtil::toLower(ctrl_type);
|
|
|
|
const LLWidgetCreatorFunc* funcp = registry.getValue(ctrl_type);
|
|
if (funcp == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (parent == NULL)
|
|
{
|
|
if (mDummyPanel == NULL)
|
|
{
|
|
LLPanel::Params p;
|
|
mDummyPanel = create<LLPanel>(p);
|
|
}
|
|
parent = mDummyPanel;
|
|
}
|
|
LLView *view = (*funcp)(node, parent, output_node);
|
|
|
|
return view;
|
|
}
|
|
|
|
std::string LLUICtrlFactory::getCurFileName()
|
|
{
|
|
return mFileNames.empty() ? "" : mFileNames.back();
|
|
}
|
|
|
|
|
|
void LLUICtrlFactory::pushFileName(const std::string& name)
|
|
{
|
|
// Here we seem to be looking for the default language file ("en") rather
|
|
// than the localized one, if any.
|
|
mFileNames.push_back(gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, name));
|
|
}
|
|
|
|
void LLUICtrlFactory::popFileName()
|
|
{
|
|
mFileNames.pop_back();
|
|
}
|
|
|
|
//static
|
|
void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group)
|
|
{
|
|
if (tab_group == S32_MAX) tab_group = parent->getLastTabGroup();
|
|
parent->addChild(view, tab_group);
|
|
}
|
|
|
|
//static
|
|
void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest)
|
|
{
|
|
dest->setName(src->getName()->mString);
|
|
}
|
|
|
|
template<typename T>
|
|
const LLInitParam::BaseBlock& get_empty_param_block()
|
|
{
|
|
static typename T::Params params;
|
|
return params;
|
|
}
|
|
|
|
// adds a widget and its param block to various registries
|
|
//static
|
|
void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& name)
|
|
{
|
|
// associate parameter block type with template .xml file
|
|
std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type);
|
|
if (existing_name != NULL)
|
|
{
|
|
if(*existing_name != name)
|
|
{
|
|
std::cerr << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << std::endl;
|
|
// forcing crash here
|
|
char* foo = 0;
|
|
*foo = 1;
|
|
}
|
|
else
|
|
{
|
|
// widget already registered this name
|
|
return;
|
|
}
|
|
}
|
|
|
|
LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, name);
|
|
//FIXME: comment this in when working on schema generation
|
|
//LLWidgetTypeRegistry::instance().defaultRegistrar().add(tag, widget_type);
|
|
//LLDefaultParamBlockRegistry::instance().defaultRegistrar().add(widget_type, &get_empty_param_block<T>);
|
|
}
|
|
|
|
|