Automated merge with http://pdp47.lindenlab.com/cgi-bin/hgwebdir.cgi/brad/viewer-development/
commit
89258376ff
1
.hgtags
1
.hgtags
|
|
@ -52,6 +52,7 @@ a82e5b1e22c7f90e3c7977d146b80588f004ed0d 2.5.0-start
|
|||
345b17e7cf630db77e840b4fe3451bd476d750a3 76f586a8e22b
|
||||
0000000000000000000000000000000000000000 76f586a8e22b
|
||||
54d772d8687c69b1d773f6ce14bbc7bdc9d6c05f 2.5.0-beta2
|
||||
b542f8134a2bb5dd054ff4e509a44b2ee463b1bf nat-eventapi2-base
|
||||
7076e22f9f43f479a4ea75eac447a36364bead5a DRTVWR-5_2.2.0-beta1
|
||||
9822eb3e25f7fe0c28ffd8aba45c507caa383cbc DRTVWR-3_2.2.0-beta2
|
||||
b0cd7e150009809a0b5b0a9d5785cd4bb230413a DRTVWR-7_2.2.0-beta3
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@ if (LL_TESTS)
|
|||
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
|
||||
|
||||
# *TODO - reenable these once tcmalloc libs no longer break the build.
|
||||
#ADD_BUILD_TEST(llallocator llcommon)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,354 @@
|
|||
#include "llevents.h"
|
||||
#include "llerror.h"
|
||||
#include "llsdutil.h"
|
||||
#include "stringize.h"
|
||||
#include <memory> // std::auto_ptr
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDArgsSource
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
|
||||
* if the consumer requests more elements than the array contains.
|
||||
*/
|
||||
class LL_COMMON_API LLSDArgsSource
|
||||
{
|
||||
public:
|
||||
LLSDArgsSource(const std::string function, const LLSD& args);
|
||||
~LLSDArgsSource();
|
||||
|
||||
LLSD next();
|
||||
|
||||
void done() const;
|
||||
|
||||
private:
|
||||
std::string _function;
|
||||
LLSD _args;
|
||||
LLSD::Integer _index;
|
||||
};
|
||||
|
||||
LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
|
||||
_function(function),
|
||||
_args(args),
|
||||
_index(0)
|
||||
{
|
||||
if (! (_args.isUndefined() || _args.isArray()))
|
||||
{
|
||||
LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
|
||||
<< _args << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
LLSDArgsSource::~LLSDArgsSource()
|
||||
{
|
||||
done();
|
||||
}
|
||||
|
||||
LLSD LLSDArgsSource::next()
|
||||
{
|
||||
if (_index >= _args.size())
|
||||
{
|
||||
LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
|
||||
<< _args.size() << " provided: " << _args << LL_ENDL;
|
||||
}
|
||||
return _args[_index++];
|
||||
}
|
||||
|
||||
void LLSDArgsSource::done() const
|
||||
{
|
||||
if (_index < _args.size())
|
||||
{
|
||||
LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
|
||||
<< " of the " << _args.size() << " arguments provided: "
|
||||
<< _args << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDArgsMapper
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* From a formal parameters description and a map of arguments, construct an
|
||||
* arguments array.
|
||||
*
|
||||
* That is, given:
|
||||
* - an LLSD array of length n containing parameter-name strings,
|
||||
* corresponding to the arguments of a function of interest
|
||||
* - an LLSD collection specifying default parameter values, either:
|
||||
* - an LLSD array of length m <= n, matching the rightmost m params, or
|
||||
* - an LLSD map explicitly stating default name=value pairs
|
||||
* - an LLSD map of parameter names and actual values for a particular
|
||||
* function call
|
||||
* construct an LLSD array of actual argument values for this function call.
|
||||
*
|
||||
* The parameter-names array and the defaults collection describe the function
|
||||
* being called. The map might vary with every call, providing argument values
|
||||
* for the described parameters.
|
||||
*
|
||||
* The array of parameter names must match the number of parameters expected
|
||||
* by the function of interest.
|
||||
*
|
||||
* If you pass a map of default parameter values, it provides default values
|
||||
* as you might expect. It is an error to specify a default value for a name
|
||||
* not listed in the parameters array.
|
||||
*
|
||||
* If you pass an array of default parameter values, it is mapped to the
|
||||
* rightmost m of the n parameter names. It is an error if the default-values
|
||||
* array is longer than the parameter-names array. Consider the following
|
||||
* parameter names: ["a", "b", "c", "d"].
|
||||
*
|
||||
* - An empty array of default values (or an isUndefined() value) asserts that
|
||||
* every one of the above parameter names is required.
|
||||
* - An array of four default values [1, 2, 3, 4] asserts that every one of
|
||||
* the above parameters is optional. If the current parameter map is empty,
|
||||
* they will be passed to the function as [1, 2, 3, 4].
|
||||
* - An array of two default values [11, 12] asserts that parameters "a" and
|
||||
* "b" are required, while "c" and "d" are optional, having default values
|
||||
* "c"=11 and "d"=12.
|
||||
*
|
||||
* The arguments array is constructed as follows:
|
||||
*
|
||||
* - Arguments-map keys not found in the parameter-names array are ignored.
|
||||
* - Entries from the map provide values for an improper subset of the
|
||||
* parameters named in the parameter-names array. This results in a
|
||||
* tentative values array with "holes." (size of map) + (number of holes) =
|
||||
* (size of names array)
|
||||
* - Holes are filled with the default values.
|
||||
* - Any remaining holes constitute an error.
|
||||
*/
|
||||
class LL_COMMON_API LLSDArgsMapper
|
||||
{
|
||||
public:
|
||||
/// Accept description of function: function name, param names, param
|
||||
/// default values
|
||||
LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
|
||||
|
||||
/// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
|
||||
LLSD map(const LLSD& argsmap) const;
|
||||
|
||||
private:
|
||||
static std::string formatlist(const LLSD&);
|
||||
|
||||
// The function-name string is purely descriptive. We want error messages
|
||||
// to be able to indicate which function's LLSDArgsMapper has the problem.
|
||||
std::string _function;
|
||||
// Store the names array pretty much as given.
|
||||
LLSD _names;
|
||||
// Though we're handed an array of name strings, it's more useful to us to
|
||||
// store it as a map from name string to position index. Of course that's
|
||||
// easy to generate from the incoming names array, but why do it more than
|
||||
// once?
|
||||
typedef std::map<LLSD::String, LLSD::Integer> IndexMap;
|
||||
IndexMap _indexes;
|
||||
// Generated array of default values, aligned with the array of param names.
|
||||
LLSD _defaults;
|
||||
// Indicate whether we have a default value for each param.
|
||||
typedef std::vector<char> FilledVector;
|
||||
FilledVector _has_dft;
|
||||
};
|
||||
|
||||
LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
|
||||
const LLSD& names, const LLSD& defaults):
|
||||
_function(function),
|
||||
_names(names),
|
||||
_has_dft(names.size())
|
||||
{
|
||||
if (! (_names.isUndefined() || _names.isArray()))
|
||||
{
|
||||
LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
|
||||
}
|
||||
LLSD::Integer nparams(_names.size());
|
||||
// From _names generate _indexes.
|
||||
for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni)
|
||||
{
|
||||
_indexes[_names[ni]] = ni;
|
||||
}
|
||||
|
||||
// Presize _defaults() array so we don't have to resize it more than once.
|
||||
// All entries are initialized to LLSD(); but since _has_dft is still all
|
||||
// 0, they're all "holes" for now.
|
||||
if (nparams)
|
||||
{
|
||||
_defaults[nparams - 1] = LLSD();
|
||||
}
|
||||
|
||||
if (defaults.isUndefined() || defaults.isArray())
|
||||
{
|
||||
LLSD::Integer ndefaults = defaults.size();
|
||||
// defaults is a (possibly empty) array. Right-align it with names.
|
||||
if (ndefaults > nparams)
|
||||
{
|
||||
LL_ERRS("LLSDArgsMapper") << function << " names array " << names
|
||||
<< " shorter than defaults array " << defaults << LL_ENDL;
|
||||
}
|
||||
|
||||
// Offset by which we slide defaults array right to right-align with
|
||||
// _names array
|
||||
LLSD::Integer offset = nparams - ndefaults;
|
||||
// Fill rightmost _defaults entries from defaults, and mark them as
|
||||
// filled
|
||||
for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i)
|
||||
{
|
||||
_defaults[i + offset] = defaults[i];
|
||||
_has_dft[i + offset] = 1;
|
||||
}
|
||||
}
|
||||
else if (defaults.isMap())
|
||||
{
|
||||
// defaults is a map. Use it to populate the _defaults array.
|
||||
LLSD bogus;
|
||||
for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap());
|
||||
mi != mend; ++mi)
|
||||
{
|
||||
IndexMap::const_iterator ixit(_indexes.find(mi->first));
|
||||
if (ixit == _indexes.end())
|
||||
{
|
||||
bogus.append(mi->first);
|
||||
continue;
|
||||
}
|
||||
|
||||
LLSD::Integer pos = ixit->second;
|
||||
// Store default value at that position in the _defaults array.
|
||||
_defaults[pos] = mi->second;
|
||||
// Don't forget to record the fact that we've filled this
|
||||
// position.
|
||||
_has_dft[pos] = 1;
|
||||
}
|
||||
if (bogus.size())
|
||||
{
|
||||
LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
|
||||
<< formatlist(bogus) << LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
|
||||
<< defaults << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
|
||||
{
|
||||
if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
|
||||
{
|
||||
LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
|
||||
<< argsmap << LL_ENDL;
|
||||
}
|
||||
// Initialize the args array. Indexing a non-const LLSD array grows it
|
||||
// to appropriate size, but we don't want to resize this one on each
|
||||
// new operation. Just make it as big as we need before we start
|
||||
// stuffing values into it.
|
||||
LLSD args(LLSD::emptyArray());
|
||||
if (_defaults.size() == 0)
|
||||
{
|
||||
// If this function requires no arguments, fast exit. (Don't try to
|
||||
// assign to args[-1].)
|
||||
return args;
|
||||
}
|
||||
args[_defaults.size() - 1] = LLSD();
|
||||
|
||||
// Get a vector of chars to indicate holes. It's tempting to just scan
|
||||
// for LLSD::isUndefined() values after filling the args array from
|
||||
// the map, but it's plausible for caller to explicitly pass
|
||||
// isUndefined() as the value of some parameter name. That's legal
|
||||
// since isUndefined() has well-defined conversions (default value)
|
||||
// for LLSD data types. So use a whole separate array for detecting
|
||||
// holes. (Avoid std::vector<bool> which is known to be odd -- can we
|
||||
// iterate?)
|
||||
FilledVector filled(args.size());
|
||||
|
||||
if (argsmap.isArray())
|
||||
{
|
||||
// Fill args from array. If there are too many args in passed array,
|
||||
// ignore the rest.
|
||||
LLSD::Integer size(argsmap.size());
|
||||
if (size > args.size())
|
||||
{
|
||||
// We don't just use std::min() because we want to sneak in this
|
||||
// warning if caller passes too many args.
|
||||
LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size()
|
||||
<< " params, ignoring last " << (size - args.size())
|
||||
<< " of passed " << size << ": " << argsmap << LL_ENDL;
|
||||
size = args.size();
|
||||
}
|
||||
for (LLSD::Integer i(0); i < size; ++i)
|
||||
{
|
||||
// Copy the actual argument from argsmap
|
||||
args[i] = argsmap[i];
|
||||
// Note that it's been filled
|
||||
filled[i] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// argsmap is in fact a map. Walk the map.
|
||||
for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap());
|
||||
mi != mend; ++mi)
|
||||
{
|
||||
// mi->first is a parameter-name string, with mi->second its
|
||||
// value. Look up the name's position index in _indexes.
|
||||
IndexMap::const_iterator ixit(_indexes.find(mi->first));
|
||||
if (ixit == _indexes.end())
|
||||
{
|
||||
// Allow for a map containing more params than were passed in
|
||||
// our names array. Caller typically receives a map containing
|
||||
// the function name, cruft such as reqid, etc. Ignore keys
|
||||
// not defined in _indexes.
|
||||
LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring "
|
||||
<< mi->first << "=" << mi->second << LL_ENDL;
|
||||
continue;
|
||||
}
|
||||
LLSD::Integer pos = ixit->second;
|
||||
// Store the value at that position in the args array.
|
||||
args[pos] = mi->second;
|
||||
// Don't forget to record the fact that we've filled this
|
||||
// position.
|
||||
filled[pos] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill any remaining holes from _defaults.
|
||||
LLSD unfilled(LLSD::emptyArray());
|
||||
for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i)
|
||||
{
|
||||
if (! filled[i])
|
||||
{
|
||||
// If there's no default value for this parameter, that's an
|
||||
// error.
|
||||
if (! _has_dft[i])
|
||||
{
|
||||
unfilled.append(_names[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = _defaults[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// If any required args -- args without defaults -- were left unfilled
|
||||
// by argsmap, that's a problem.
|
||||
if (unfilled.size())
|
||||
{
|
||||
LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
|
||||
<< formatlist(unfilled) << " from " << argsmap << LL_ENDL;
|
||||
}
|
||||
|
||||
// done
|
||||
return args;
|
||||
}
|
||||
|
||||
std::string LLSDArgsMapper::formatlist(const LLSD& list)
|
||||
{
|
||||
std::ostringstream out;
|
||||
const char* delim = "";
|
||||
for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray());
|
||||
li != lend; ++li)
|
||||
{
|
||||
out << delim << li->asString();
|
||||
delim = ", ";
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
|
||||
mDesc(desc),
|
||||
|
|
@ -52,12 +400,181 @@ LLEventDispatcher::~LLEventDispatcher()
|
|||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* DispatchEntry subclass used for callables accepting(const LLSD&)
|
||||
*/
|
||||
struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
|
||||
{
|
||||
LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
|
||||
DispatchEntry(desc),
|
||||
mFunc(func),
|
||||
mRequired(required)
|
||||
{}
|
||||
|
||||
Callable mFunc;
|
||||
LLSD mRequired;
|
||||
|
||||
virtual void call(const std::string& desc, const LLSD& event) const
|
||||
{
|
||||
// Validate the syntax of the event itself.
|
||||
std::string mismatch(llsd_matches(mRequired, event));
|
||||
if (! mismatch.empty())
|
||||
{
|
||||
LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
|
||||
}
|
||||
// Event syntax looks good, go for it!
|
||||
mFunc(event);
|
||||
}
|
||||
|
||||
virtual LLSD addMetadata(LLSD meta) const
|
||||
{
|
||||
meta["required"] = mRequired;
|
||||
return meta;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* DispatchEntry subclass for passing LLSD to functions accepting
|
||||
* arbitrary argument types (convertible via LLSDParam)
|
||||
*/
|
||||
struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
|
||||
{
|
||||
ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
|
||||
DispatchEntry(desc),
|
||||
mInvoker(func)
|
||||
{}
|
||||
|
||||
invoker_function mInvoker;
|
||||
|
||||
virtual void call(const std::string& desc, const LLSD& event) const
|
||||
{
|
||||
LLSDArgsSource src(desc, event);
|
||||
mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* DispatchEntry subclass for dispatching LLSD::Array to functions accepting
|
||||
* arbitrary argument types (convertible via LLSDParam)
|
||||
*/
|
||||
struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
|
||||
{
|
||||
ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
|
||||
LLSD::Integer arity):
|
||||
ParamsDispatchEntry(desc, func),
|
||||
mArity(arity)
|
||||
{}
|
||||
|
||||
LLSD::Integer mArity;
|
||||
|
||||
virtual LLSD addMetadata(LLSD meta) const
|
||||
{
|
||||
LLSD array(LLSD::emptyArray());
|
||||
// Resize to number of arguments required
|
||||
if (mArity)
|
||||
array[mArity - 1] = LLSD();
|
||||
llassert_always(array.size() == mArity);
|
||||
meta["required"] = array;
|
||||
return meta;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* DispatchEntry subclass for dispatching LLSD::Map to functions accepting
|
||||
* arbitrary argument types (convertible via LLSDParam)
|
||||
*/
|
||||
struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
|
||||
{
|
||||
MapParamsDispatchEntry(const std::string& name, const std::string& desc,
|
||||
const invoker_function& func,
|
||||
const LLSD& params, const LLSD& defaults):
|
||||
ParamsDispatchEntry(desc, func),
|
||||
mMapper(name, params, defaults),
|
||||
mRequired(LLSD::emptyMap())
|
||||
{
|
||||
// Build the set of all param keys, then delete the ones that are
|
||||
// optional. What's left are the ones that are required.
|
||||
for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray());
|
||||
pi != pend; ++pi)
|
||||
{
|
||||
mRequired[pi->asString()] = LLSD();
|
||||
}
|
||||
|
||||
if (defaults.isArray() || defaults.isUndefined())
|
||||
{
|
||||
// Right-align the params and defaults arrays.
|
||||
LLSD::Integer offset = params.size() - defaults.size();
|
||||
// Now the name of every defaults[i] is at params[i + offset].
|
||||
for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i)
|
||||
{
|
||||
// Erase this optional param from mRequired.
|
||||
mRequired.erase(params[i + offset].asString());
|
||||
// Instead, make an entry in mOptional with the default
|
||||
// param's name and value.
|
||||
mOptional[params[i + offset].asString()] = defaults[i];
|
||||
}
|
||||
}
|
||||
else if (defaults.isMap())
|
||||
{
|
||||
// if defaults is already a map, then it's already in the form we
|
||||
// intend to deliver in metadata
|
||||
mOptional = defaults;
|
||||
// Just delete from mRequired every key appearing in mOptional.
|
||||
for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap());
|
||||
mi != mend; ++mi)
|
||||
{
|
||||
mRequired.erase(mi->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LLSDArgsMapper mMapper;
|
||||
LLSD mRequired;
|
||||
LLSD mOptional;
|
||||
|
||||
virtual void call(const std::string& desc, const LLSD& event) const
|
||||
{
|
||||
// Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
|
||||
// to base-class call() method.
|
||||
ParamsDispatchEntry::call(desc, mMapper.map(event));
|
||||
}
|
||||
|
||||
virtual LLSD addMetadata(LLSD meta) const
|
||||
{
|
||||
meta["required"] = mRequired;
|
||||
meta["optional"] = mOptional;
|
||||
return meta;
|
||||
}
|
||||
};
|
||||
|
||||
void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
|
||||
const std::string& desc,
|
||||
const invoker_function& invoker,
|
||||
LLSD::Integer arity)
|
||||
{
|
||||
mDispatch.insert(
|
||||
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
||||
new ArrayParamsDispatchEntry(desc, invoker, arity))));
|
||||
}
|
||||
|
||||
void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
|
||||
const std::string& desc,
|
||||
const invoker_function& invoker,
|
||||
const LLSD& params,
|
||||
const LLSD& defaults)
|
||||
{
|
||||
mDispatch.insert(
|
||||
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
||||
new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
|
||||
}
|
||||
|
||||
/// Register a callable by name
|
||||
void LLEventDispatcher::add(const std::string& name, const std::string& desc,
|
||||
const Callable& callable, const LLSD& required)
|
||||
{
|
||||
mDispatch.insert(DispatchMap::value_type(name,
|
||||
DispatchMap::mapped_type(callable, desc, required)));
|
||||
mDispatch.insert(
|
||||
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
||||
new LLSDDispatchEntry(desc, callable, required))));
|
||||
}
|
||||
|
||||
void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
|
||||
|
|
@ -83,7 +600,7 @@ bool LLEventDispatcher::remove(const std::string& name)
|
|||
/// such callable exists, die with LL_ERRS.
|
||||
void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
|
||||
{
|
||||
if (! attemptCall(name, event))
|
||||
if (! try_call(name, event))
|
||||
{
|
||||
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
|
||||
<< "' not found" << LL_ENDL;
|
||||
|
|
@ -98,44 +615,29 @@ void LLEventDispatcher::operator()(const LLSD& event) const
|
|||
// This could/should be implemented in terms of the two-arg overload.
|
||||
// However -- we can produce a more informative error message.
|
||||
std::string name(event[mKey]);
|
||||
if (! attemptCall(name, event))
|
||||
if (! try_call(name, event))
|
||||
{
|
||||
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
|
||||
<< " value '" << name << "'" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const
|
||||
bool LLEventDispatcher::try_call(const LLSD& event) const
|
||||
{
|
||||
DispatchMap::const_iterator found = mDispatch.find(name);
|
||||
if (found == mDispatch.end())
|
||||
{
|
||||
// The reason we only return false, leaving it up to our caller to die
|
||||
// with LL_ERRS, is that different callers have different amounts of
|
||||
// available information.
|
||||
return false;
|
||||
}
|
||||
// Found the name, so it's plausible to even attempt the call. But first,
|
||||
// validate the syntax of the event itself.
|
||||
std::string mismatch(llsd_matches(found->second.mRequired, event));
|
||||
if (! mismatch.empty())
|
||||
{
|
||||
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name
|
||||
<< "': bad request: " << mismatch << LL_ENDL;
|
||||
}
|
||||
// Event syntax looks good, go for it!
|
||||
(found->second.mFunc)(event);
|
||||
return true; // tell caller we were able to call
|
||||
return try_call(event[mKey], event);
|
||||
}
|
||||
|
||||
LLEventDispatcher::Callable LLEventDispatcher::get(const std::string& name) const
|
||||
bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
|
||||
{
|
||||
DispatchMap::const_iterator found = mDispatch.find(name);
|
||||
if (found == mDispatch.end())
|
||||
{
|
||||
return Callable();
|
||||
return false;
|
||||
}
|
||||
return found->second.mFunc;
|
||||
// Found the name, so it's plausible to even attempt the call.
|
||||
found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
|
||||
event);
|
||||
return true; // tell caller we were able to call
|
||||
}
|
||||
|
||||
LLSD LLEventDispatcher::getMetadata(const std::string& name) const
|
||||
|
|
@ -147,9 +649,8 @@ LLSD LLEventDispatcher::getMetadata(const std::string& name) const
|
|||
}
|
||||
LLSD meta;
|
||||
meta["name"] = name;
|
||||
meta["desc"] = found->second.mDesc;
|
||||
meta["required"] = found->second.mRequired;
|
||||
return meta;
|
||||
meta["desc"] = found->second->mDesc;
|
||||
return found->second->addMetadata(meta);
|
||||
}
|
||||
|
||||
LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
|
||||
|
|
@ -164,3 +665,8 @@ bool LLDispatchListener::process(const LLSD& event)
|
|||
(*this)(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
|
||||
mDesc(desc)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,18 +27,56 @@
|
|||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* The invoker machinery that constructs a boost::fusion argument list for use
|
||||
* with boost::fusion::invoke() is derived from
|
||||
* http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
|
||||
* whose license information is copied below:
|
||||
*
|
||||
* "(C) Copyright Tobias Schwinger
|
||||
*
|
||||
* Use modification and distribution are subject to the boost Software License,
|
||||
* Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLEVENTDISPATCHER_H)
|
||||
#define LL_LLEVENTDISPATCHER_H
|
||||
|
||||
// nil is too generic a term to be allowed to be a global macro. In
|
||||
// particular, boost::fusion defines a 'class nil' (properly encapsulated in a
|
||||
// namespace) that a global 'nil' macro breaks badly.
|
||||
#if defined(nil)
|
||||
// Capture the value of the macro 'nil', hoping int is an appropriate type.
|
||||
static const int nil_(nil);
|
||||
// Now forget the macro.
|
||||
#undef nil
|
||||
// Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
|
||||
// defined const 'nil_'. Make it static since otherwise it produces duplicate-
|
||||
// symbol link errors later.
|
||||
static const int& nil(nil_);
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <boost/function_types/is_nonmember_callable_builtin.hpp>
|
||||
#include <boost/function_types/parameter_types.hpp>
|
||||
#include <boost/function_types/function_arity.hpp>
|
||||
#include <boost/type_traits/remove_cv.hpp>
|
||||
#include <boost/type_traits/remove_reference.hpp>
|
||||
#include <boost/fusion/include/push_back.hpp>
|
||||
#include <boost/fusion/include/cons.hpp>
|
||||
#include <boost/fusion/include/invoke.hpp>
|
||||
#include <boost/mpl/begin.hpp>
|
||||
#include <boost/mpl/end.hpp>
|
||||
#include <boost/mpl/next.hpp>
|
||||
#include <boost/mpl/deref.hpp>
|
||||
#include <typeinfo>
|
||||
#include "llevents.h"
|
||||
#include "llsdutil.h"
|
||||
|
||||
class LLSD;
|
||||
|
||||
|
|
@ -54,12 +92,18 @@ public:
|
|||
LLEventDispatcher(const std::string& desc, const std::string& key);
|
||||
virtual ~LLEventDispatcher();
|
||||
|
||||
/// Accept any C++ callable, typically a boost::bind() expression
|
||||
/// @name Register functions accepting(const LLSD&)
|
||||
//@{
|
||||
|
||||
/// Accept any C++ callable with the right signature, typically a
|
||||
/// boost::bind() expression
|
||||
typedef boost::function<void(const LLSD&)> Callable;
|
||||
|
||||
/**
|
||||
* Register a @a callable by @a name. The optional @a required parameter
|
||||
* is used to validate the structure of each incoming event (see
|
||||
* Register a @a callable by @a name. The passed @a callable accepts a
|
||||
* single LLSD value and uses it in any way desired, e.g. extract
|
||||
* parameters and call some other function. The optional @a required
|
||||
* parameter is used to validate the structure of each incoming event (see
|
||||
* llsd_matches()).
|
||||
*/
|
||||
void add(const std::string& name,
|
||||
|
|
@ -67,10 +111,24 @@ public:
|
|||
const Callable& callable,
|
||||
const LLSD& required=LLSD());
|
||||
|
||||
/**
|
||||
* The case of a free function (or static method) accepting(const LLSD&)
|
||||
* could also be intercepted by the arbitrary-args overload below. Ensure
|
||||
* that it's directed to the Callable overload above instead.
|
||||
*/
|
||||
void add(const std::string& name,
|
||||
const std::string& desc,
|
||||
void (*f)(const LLSD&),
|
||||
const LLSD& required=LLSD())
|
||||
{
|
||||
add(name, desc, Callable(f), required);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case: a subclass of this class can pass an unbound member
|
||||
* function pointer without explicitly specifying the
|
||||
* <tt>boost::bind()</tt> expression.
|
||||
* function pointer (of an LLEventDispatcher subclass) without explicitly
|
||||
* specifying the <tt>boost::bind()</tt> expression. The passed @a method
|
||||
* accepts a single LLSD value, presumably containing other parameters.
|
||||
*/
|
||||
template <class CLASS>
|
||||
void add(const std::string& name,
|
||||
|
|
@ -81,7 +139,8 @@ public:
|
|||
addMethod<CLASS>(name, desc, method, required);
|
||||
}
|
||||
|
||||
/// Overload for both const and non-const methods
|
||||
/// Overload for both const and non-const methods. The passed @a method
|
||||
/// accepts a single LLSD value, presumably containing other parameters.
|
||||
template <class CLASS>
|
||||
void add(const std::string& name,
|
||||
const std::string& desc,
|
||||
|
|
@ -91,15 +150,106 @@ public:
|
|||
addMethod<CLASS>(name, desc, method, required);
|
||||
}
|
||||
|
||||
/// Convenience: for LLEventDispatcher, not every callable needs a
|
||||
/// documentation string.
|
||||
template <typename CALLABLE>
|
||||
void add(const std::string& name,
|
||||
CALLABLE callable,
|
||||
const LLSD& required=LLSD())
|
||||
{
|
||||
add(name, "", callable, required);
|
||||
}
|
||||
//@}
|
||||
|
||||
/// @name Register functions with arbitrary param lists
|
||||
//@{
|
||||
|
||||
/**
|
||||
* Register a free function with arbitrary parameters. (This also works
|
||||
* for static class methods.)
|
||||
*
|
||||
* @note This supports functions with up to about 6 parameters -- after
|
||||
* that you start getting dismaying compile errors in which
|
||||
* boost::fusion::joint_view is mentioned a surprising number of times.
|
||||
*
|
||||
* When calling this name, pass an LLSD::Array. Each entry in turn will be
|
||||
* converted to the corresponding parameter type using LLSDParam.
|
||||
*/
|
||||
template<typename Function>
|
||||
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
|
||||
>::type add(const std::string& name,
|
||||
const std::string& desc,
|
||||
Function f);
|
||||
|
||||
/**
|
||||
* Register a nonstatic class method with arbitrary parameters.
|
||||
*
|
||||
* @note This supports functions with up to about 6 parameters -- after
|
||||
* that you start getting dismaying compile errors in which
|
||||
* boost::fusion::joint_view is mentioned a surprising number of times.
|
||||
*
|
||||
* To cover cases such as a method on an LLSingleton we don't yet want to
|
||||
* instantiate, instead of directly storing an instance pointer, accept a
|
||||
* nullary callable returning a pointer/reference to the desired class
|
||||
* instance. If you already have an instance in hand,
|
||||
* boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
|
||||
* produce suitable callables.
|
||||
*
|
||||
* When calling this name, pass an LLSD::Array. Each entry in turn will be
|
||||
* converted to the corresponding parameter type using LLSDParam.
|
||||
*/
|
||||
template<typename Method, typename InstanceGetter>
|
||||
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
|
||||
>::type add(const std::string& name,
|
||||
const std::string& desc,
|
||||
Method f,
|
||||
const InstanceGetter& getter);
|
||||
|
||||
/**
|
||||
* Register a free function with arbitrary parameters. (This also works
|
||||
* for static class methods.)
|
||||
*
|
||||
* @note This supports functions with up to about 6 parameters -- after
|
||||
* that you start getting dismaying compile errors in which
|
||||
* boost::fusion::joint_view is mentioned a surprising number of times.
|
||||
*
|
||||
* Pass an LLSD::Array of parameter names, and optionally another
|
||||
* LLSD::Array of default parameter values, a la LLSDArgsMapper.
|
||||
*
|
||||
* When calling this name, pass an LLSD::Map. We will internally generate
|
||||
* an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
|
||||
* to the corresponding parameter type using LLSDParam.
|
||||
*/
|
||||
template<typename Function>
|
||||
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
|
||||
>::type add(const std::string& name,
|
||||
const std::string& desc,
|
||||
Function f,
|
||||
const LLSD& params,
|
||||
const LLSD& defaults=LLSD());
|
||||
|
||||
/**
|
||||
* Register a nonstatic class method with arbitrary parameters.
|
||||
*
|
||||
* @note This supports functions with up to about 6 parameters -- after
|
||||
* that you start getting dismaying compile errors in which
|
||||
* boost::fusion::joint_view is mentioned a surprising number of times.
|
||||
*
|
||||
* To cover cases such as a method on an LLSingleton we don't yet want to
|
||||
* instantiate, instead of directly storing an instance pointer, accept a
|
||||
* nullary callable returning a pointer/reference to the desired class
|
||||
* instance. If you already have an instance in hand,
|
||||
* boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
|
||||
* produce suitable callables.
|
||||
*
|
||||
* Pass an LLSD::Array of parameter names, and optionally another
|
||||
* LLSD::Array of default parameter values, a la LLSDArgsMapper.
|
||||
*
|
||||
* When calling this name, pass an LLSD::Map. We will internally generate
|
||||
* an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
|
||||
* to the corresponding parameter type using LLSDParam.
|
||||
*/
|
||||
template<typename Method, typename InstanceGetter>
|
||||
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
|
||||
>::type add(const std::string& name,
|
||||
const std::string& desc,
|
||||
Method f,
|
||||
const InstanceGetter& getter,
|
||||
const LLSD& params,
|
||||
const LLSD& defaults=LLSD());
|
||||
|
||||
//@}
|
||||
|
||||
/// Unregister a callable
|
||||
bool remove(const std::string& name);
|
||||
|
|
@ -109,12 +259,25 @@ public:
|
|||
/// the @a required prototype specified at add() time, die with LL_ERRS.
|
||||
void operator()(const std::string& name, const LLSD& event) const;
|
||||
|
||||
/// Call a registered callable with an explicitly-specified name and
|
||||
/// return <tt>true</tt>. If no such callable exists, return
|
||||
/// <tt>false</tt>. If the @a event fails to match the @a required
|
||||
/// prototype specified at add() time, die with LL_ERRS.
|
||||
bool try_call(const std::string& name, const LLSD& event) const;
|
||||
|
||||
/// Extract the @a key value from the incoming @a event, and call the
|
||||
/// callable whose name is specified by that map @a key. If no such
|
||||
/// callable exists, die with LL_ERRS. If the @a event fails to match the
|
||||
/// @a required prototype specified at add() time, die with LL_ERRS.
|
||||
void operator()(const LLSD& event) const;
|
||||
|
||||
/// Extract the @a key value from the incoming @a event, call the callable
|
||||
/// whose name is specified by that map @a key and return <tt>true</tt>.
|
||||
/// If no such callable exists, return <tt>false</tt>. If the @a event
|
||||
/// fails to match the @a required prototype specified at add() time, die
|
||||
/// with LL_ERRS.
|
||||
bool try_call(const LLSD& event) const;
|
||||
|
||||
/// @name Iterate over defined names
|
||||
//@{
|
||||
typedef std::pair<std::string, std::string> NameDesc;
|
||||
|
|
@ -122,16 +285,22 @@ public:
|
|||
private:
|
||||
struct DispatchEntry
|
||||
{
|
||||
DispatchEntry(const Callable& func, const std::string& desc, const LLSD& required):
|
||||
mFunc(func),
|
||||
mDesc(desc),
|
||||
mRequired(required)
|
||||
{}
|
||||
Callable mFunc;
|
||||
DispatchEntry(const std::string& desc);
|
||||
virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
|
||||
|
||||
std::string mDesc;
|
||||
LLSD mRequired;
|
||||
|
||||
virtual void call(const std::string& desc, const LLSD& event) const = 0;
|
||||
virtual LLSD addMetadata(LLSD) const = 0;
|
||||
};
|
||||
typedef std::map<std::string, DispatchEntry> DispatchMap;
|
||||
// Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
|
||||
// wants its value type to be "clonable," even just to dereference an
|
||||
// iterator. I don't want to clone entries -- if I have to copy an entry
|
||||
// around, I want it to continue pointing to the same DispatchEntry
|
||||
// subclass object. However, I definitely want DispatchMap to destroy
|
||||
// DispatchEntry if no references are outstanding at the time an entry is
|
||||
// removed. This looks like a job for boost::shared_ptr.
|
||||
typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
|
||||
|
||||
public:
|
||||
/// We want the flexibility to redefine what data we store per name,
|
||||
|
|
@ -149,10 +318,6 @@ public:
|
|||
}
|
||||
//@}
|
||||
|
||||
/// Fetch the Callable for the specified name. If no such name was
|
||||
/// registered, return an empty() Callable.
|
||||
Callable get(const std::string& name) const;
|
||||
|
||||
/// Get information about a specific Callable
|
||||
LLSD getMetadata(const std::string& name) const;
|
||||
|
||||
|
|
@ -175,18 +340,184 @@ private:
|
|||
}
|
||||
}
|
||||
void addFail(const std::string& name, const std::string& classname) const;
|
||||
/// try to dispatch, return @c true if success
|
||||
bool attemptCall(const std::string& name, const LLSD& event) const;
|
||||
|
||||
std::string mDesc, mKey;
|
||||
DispatchMap mDispatch;
|
||||
|
||||
static NameDesc makeNameDesc(const DispatchMap::value_type& item)
|
||||
{
|
||||
return NameDesc(item.first, item.second.mDesc);
|
||||
return NameDesc(item.first, item.second->mDesc);
|
||||
}
|
||||
|
||||
struct LLSDDispatchEntry;
|
||||
struct ParamsDispatchEntry;
|
||||
struct ArrayParamsDispatchEntry;
|
||||
struct MapParamsDispatchEntry;
|
||||
|
||||
// Step 2 of parameter analysis. Instantiating invoker<some_function_type>
|
||||
// implicitly sets its From and To parameters to the (compile time) begin
|
||||
// and end iterators over that function's parameter types.
|
||||
template< typename Function
|
||||
, class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
|
||||
, class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
|
||||
>
|
||||
struct invoker;
|
||||
|
||||
// deliver LLSD arguments one at a time
|
||||
typedef boost::function<LLSD()> args_source;
|
||||
// obtain args from an args_source to build param list and call target
|
||||
// function
|
||||
typedef boost::function<void(const args_source&)> invoker_function;
|
||||
|
||||
template <typename Function>
|
||||
invoker_function make_invoker(Function f);
|
||||
template <typename Method, typename InstanceGetter>
|
||||
invoker_function make_invoker(Method f, const InstanceGetter& getter);
|
||||
void addArrayParamsDispatchEntry(const std::string& name,
|
||||
const std::string& desc,
|
||||
const invoker_function& invoker,
|
||||
LLSD::Integer arity);
|
||||
void addMapParamsDispatchEntry(const std::string& name,
|
||||
const std::string& desc,
|
||||
const invoker_function& invoker,
|
||||
const LLSD& params,
|
||||
const LLSD& defaults);
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* LLEventDispatcher template implementation details
|
||||
*****************************************************************************/
|
||||
// Step 3 of parameter analysis, the recursive case.
|
||||
template<typename Function, class From, class To>
|
||||
struct LLEventDispatcher::invoker
|
||||
{
|
||||
template<typename T>
|
||||
struct remove_cv_ref
|
||||
: boost::remove_cv< typename boost::remove_reference<T>::type >
|
||||
{ };
|
||||
|
||||
// apply() accepts an arbitrary boost::fusion sequence as args. It
|
||||
// examines the next parameter type in the parameter-types sequence
|
||||
// bounded by From and To, obtains the next LLSD object from the passed
|
||||
// args_source and constructs an LLSDParam of appropriate type to try
|
||||
// to convert the value. It then recurs with the next parameter-types
|
||||
// iterator, passing the args sequence thus far.
|
||||
template<typename Args>
|
||||
static inline
|
||||
void apply(Function func, const args_source& argsrc, Args const & args)
|
||||
{
|
||||
typedef typename boost::mpl::deref<From>::type arg_type;
|
||||
typedef typename boost::mpl::next<From>::type next_iter_type;
|
||||
typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
|
||||
|
||||
invoker<Function, next_iter_type, To>::apply
|
||||
( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
|
||||
}
|
||||
|
||||
// Special treatment for instance (first) parameter of a non-static member
|
||||
// function. Accept the instance-getter callable, calling that to produce
|
||||
// the first args value. Since we know we're at the top of the recursion
|
||||
// chain, we need not also require a partial args sequence from our caller.
|
||||
template <typename InstanceGetter>
|
||||
static inline
|
||||
void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
|
||||
{
|
||||
typedef typename boost::mpl::next<From>::type next_iter_type;
|
||||
|
||||
// Instead of grabbing the first item from argsrc and making an
|
||||
// LLSDParam of it, call getter() and pass that as the instance param.
|
||||
invoker<Function, next_iter_type, To>::apply
|
||||
( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
|
||||
}
|
||||
};
|
||||
|
||||
// Step 4 of parameter analysis, the leaf case. When the general
|
||||
// invoker<Function, From, To> logic has advanced From until it matches To,
|
||||
// the compiler will pick this template specialization.
|
||||
template<typename Function, class To>
|
||||
struct LLEventDispatcher::invoker<Function,To,To>
|
||||
{
|
||||
// the argument list is complete, now call the function
|
||||
template<typename Args>
|
||||
static inline
|
||||
void apply(Function func, const args_source&, Args const & args)
|
||||
{
|
||||
boost::fusion::invoke(func, args);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Function>
|
||||
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
|
||||
LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
|
||||
{
|
||||
// Construct an invoker_function, a callable accepting const args_source&.
|
||||
// Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
|
||||
// caller's LLSD::Array.
|
||||
addArrayParamsDispatchEntry(name, desc, make_invoker(f),
|
||||
boost::function_types::function_arity<Function>::value);
|
||||
}
|
||||
|
||||
template<typename Method, typename InstanceGetter>
|
||||
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
|
||||
LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
|
||||
const InstanceGetter& getter)
|
||||
{
|
||||
// Subtract 1 from the compile-time arity because the getter takes care of
|
||||
// the first parameter. We only need (arity - 1) additional arguments.
|
||||
addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
|
||||
boost::function_types::function_arity<Method>::value - 1);
|
||||
}
|
||||
|
||||
template<typename Function>
|
||||
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
|
||||
LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
|
||||
const LLSD& params, const LLSD& defaults)
|
||||
{
|
||||
// See comments for previous is_nonmember_callable_builtin add().
|
||||
addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
|
||||
}
|
||||
|
||||
template<typename Method, typename InstanceGetter>
|
||||
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
|
||||
LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
|
||||
const InstanceGetter& getter,
|
||||
const LLSD& params, const LLSD& defaults)
|
||||
{
|
||||
addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
LLEventDispatcher::invoker_function
|
||||
LLEventDispatcher::make_invoker(Function f)
|
||||
{
|
||||
// Step 1 of parameter analysis, the top of the recursion. Passing a
|
||||
// suitable f (see add()'s enable_if condition) to this method causes it
|
||||
// to infer the function type; specifying that function type to invoker<>
|
||||
// causes it to fill in the begin/end MPL iterators over the function's
|
||||
// list of parameter types.
|
||||
// While normally invoker::apply() could infer its template type from the
|
||||
// boost::fusion::nil parameter value, here we must be explicit since
|
||||
// we're boost::bind()ing it rather than calling it directly.
|
||||
return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
|
||||
f,
|
||||
_1,
|
||||
boost::fusion::nil());
|
||||
}
|
||||
|
||||
template <typename Method, typename InstanceGetter>
|
||||
LLEventDispatcher::invoker_function
|
||||
LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
|
||||
{
|
||||
// Use invoker::method_apply() to treat the instance (first) arg specially.
|
||||
return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
|
||||
f,
|
||||
_1,
|
||||
getter);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* LLDispatchListener
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
|
||||
* that contains (or derives from) LLDispatchListener need only specify the
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "llsdserialize.h"
|
||||
#include "stringize.h"
|
||||
#include "is_approx_equal_fraction.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
|
@ -571,7 +572,7 @@ std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::str
|
|||
return match_types(prototype.type(), TypeVector(), data.type(), pfx);
|
||||
}
|
||||
|
||||
bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
|
||||
bool llsd_equals(const LLSD& lhs, const LLSD& rhs, unsigned bits)
|
||||
{
|
||||
// We're comparing strict equality of LLSD representation rather than
|
||||
// performing any conversions. So if the types aren't equal, the LLSD
|
||||
|
|
@ -588,6 +589,20 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
|
|||
// Both are TypeUndefined. There's nothing more to know.
|
||||
return true;
|
||||
|
||||
case LLSD::TypeReal:
|
||||
// This is where the 'bits' argument comes in handy. If passed
|
||||
// explicitly, it means to use is_approx_equal_fraction() to compare.
|
||||
if (bits >= 0)
|
||||
{
|
||||
return is_approx_equal_fraction(lhs.asReal(), rhs.asReal(), bits);
|
||||
}
|
||||
// Otherwise we compare bit representations, and the usual caveats
|
||||
// about comparing floating-point numbers apply. Omitting 'bits' when
|
||||
// comparing Real values is only useful when we expect identical bit
|
||||
// representation for a given Real value, e.g. for integer-valued
|
||||
// Reals.
|
||||
return (lhs.asReal() == rhs.asReal());
|
||||
|
||||
#define COMPARE_SCALAR(type) \
|
||||
case LLSD::Type##type: \
|
||||
/* LLSD::URI has operator!=() but not operator==() */ \
|
||||
|
|
@ -596,10 +611,6 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
|
|||
|
||||
COMPARE_SCALAR(Boolean);
|
||||
COMPARE_SCALAR(Integer);
|
||||
// The usual caveats about comparing floating-point numbers apply. This is
|
||||
// only useful when we expect identical bit representation for a given
|
||||
// Real value, e.g. for integer-valued Reals.
|
||||
COMPARE_SCALAR(Real);
|
||||
COMPARE_SCALAR(String);
|
||||
COMPARE_SCALAR(UUID);
|
||||
COMPARE_SCALAR(Date);
|
||||
|
|
@ -617,7 +628,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
|
|||
for ( ; lai != laend && rai != raend; ++lai, ++rai)
|
||||
{
|
||||
// If any one array element is unequal, the arrays are unequal.
|
||||
if (! llsd_equals(*lai, *rai))
|
||||
if (! llsd_equals(*lai, *rai, bits))
|
||||
return false;
|
||||
}
|
||||
// Here we've reached the end of one or the other array. They're equal
|
||||
|
|
@ -644,7 +655,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
|
|||
if (rhskeys.erase(lmi->first) != 1)
|
||||
return false;
|
||||
// Both maps have the current key. Compare values.
|
||||
if (! llsd_equals(lmi->second, rhs[lmi->first]))
|
||||
if (! llsd_equals(lmi->second, rhs[lmi->first], bits))
|
||||
return false;
|
||||
}
|
||||
// We've now established that all the lhs keys have equal values in
|
||||
|
|
@ -657,7 +668,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
|
|||
// We expect that every possible type() value is specifically handled
|
||||
// above. Failing to extend this switch to support a new LLSD type is
|
||||
// an error that must be brought to the coder's attention.
|
||||
LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << "): "
|
||||
LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << ", " << bits << "): "
|
||||
"unknown type " << lhs.type() << LL_ENDL;
|
||||
return false; // pacify the compiler
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,8 +123,10 @@ LL_COMMON_API BOOL compare_llsd_with_template(
|
|||
*/
|
||||
LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
|
||||
|
||||
/// Deep equality
|
||||
LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs);
|
||||
/// Deep equality. If you want to compare LLSD::Real values for approximate
|
||||
/// equality rather than bitwise equality, pass @a bits as for
|
||||
/// is_approx_equal_fraction().
|
||||
LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, unsigned bits=-1);
|
||||
|
||||
// Simple function to copy data out of input & output iterators if
|
||||
// there is no need for casting.
|
||||
|
|
@ -138,4 +140,283 @@ template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
|
|||
return dest;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDArray
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* Construct an LLSD::Array inline, with implicit conversion to LLSD. Usage:
|
||||
*
|
||||
* @code
|
||||
* void somefunc(const LLSD&);
|
||||
* ...
|
||||
* somefunc(LLSDArray("text")(17)(3.14));
|
||||
* @endcode
|
||||
*
|
||||
* For completeness, LLSDArray() with no args constructs an empty array, so
|
||||
* <tt>LLSDArray()("text")(17)(3.14)</tt> produces an array equivalent to the
|
||||
* above. But for most purposes, LLSD() is already equivalent to an empty
|
||||
* array, and if you explicitly want an empty isArray(), there's
|
||||
* LLSD::emptyArray(). However, supporting a no-args LLSDArray() constructor
|
||||
* follows the principle of least astonishment.
|
||||
*/
|
||||
class LLSDArray
|
||||
{
|
||||
public:
|
||||
LLSDArray():
|
||||
_data(LLSD::emptyArray())
|
||||
{}
|
||||
|
||||
/**
|
||||
* Need an explicit copy constructor. Consider the following:
|
||||
*
|
||||
* @code
|
||||
* LLSD array_of_arrays(LLSDArray(LLSDArray(17)(34))
|
||||
* (LLSDArray("x")("y")));
|
||||
* @endcode
|
||||
*
|
||||
* The coder intends to construct [[17, 34], ["x", "y"]].
|
||||
*
|
||||
* With the compiler's implicit copy constructor, s/he gets instead
|
||||
* [17, 34, ["x", "y"]].
|
||||
*
|
||||
* The expression LLSDArray(17)(34) constructs an LLSDArray with those two
|
||||
* values. The reader assumes it should be converted to LLSD, as we always
|
||||
* want with LLSDArray, before passing it to the @em outer LLSDArray
|
||||
* constructor! This copy constructor makes that happen.
|
||||
*/
|
||||
LLSDArray(const LLSDArray& inner):
|
||||
_data(LLSD::emptyArray())
|
||||
{
|
||||
_data.append(inner);
|
||||
}
|
||||
|
||||
LLSDArray(const LLSD& value):
|
||||
_data(LLSD::emptyArray())
|
||||
{
|
||||
_data.append(value);
|
||||
}
|
||||
|
||||
LLSDArray& operator()(const LLSD& value)
|
||||
{
|
||||
_data.append(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator LLSD() const { return _data; }
|
||||
LLSD get() const { return _data; }
|
||||
|
||||
private:
|
||||
LLSD _data;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDMap
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage:
|
||||
*
|
||||
* @code
|
||||
* void somefunc(const LLSD&);
|
||||
* ...
|
||||
* somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14));
|
||||
* @endcode
|
||||
*
|
||||
* For completeness, LLSDMap() with no args constructs an empty map, so
|
||||
* <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map
|
||||
* equivalent to the above. But for most purposes, LLSD() is already
|
||||
* equivalent to an empty map, and if you explicitly want an empty isMap(),
|
||||
* there's LLSD::emptyMap(). However, supporting a no-args LLSDMap()
|
||||
* constructor follows the principle of least astonishment.
|
||||
*/
|
||||
class LLSDMap
|
||||
{
|
||||
public:
|
||||
LLSDMap():
|
||||
_data(LLSD::emptyMap())
|
||||
{}
|
||||
LLSDMap(const LLSD::String& key, const LLSD& value):
|
||||
_data(LLSD::emptyMap())
|
||||
{
|
||||
_data[key] = value;
|
||||
}
|
||||
|
||||
LLSDMap& operator()(const LLSD::String& key, const LLSD& value)
|
||||
{
|
||||
_data[key] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator LLSD() const { return _data; }
|
||||
LLSD get() const { return _data; }
|
||||
|
||||
private:
|
||||
LLSD _data;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDParam
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* LLSDParam is a customization point for passing LLSD values to function
|
||||
* parameters of more or less arbitrary type. LLSD provides a small set of
|
||||
* native conversions; but if a generic algorithm explicitly constructs an
|
||||
* LLSDParam object in the function's argument list, a consumer can provide
|
||||
* LLSDParam specializations to support more different parameter types than
|
||||
* LLSD's native conversions.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* @code
|
||||
* void somefunc(const paramtype&);
|
||||
* ...
|
||||
* somefunc(..., LLSDParam<paramtype>(someLLSD), ...);
|
||||
* @endcode
|
||||
*/
|
||||
template <typename T>
|
||||
class LLSDParam
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default implementation converts to T on construction, saves converted
|
||||
* value for later retrieval
|
||||
*/
|
||||
LLSDParam(const LLSD& value):
|
||||
_value(value)
|
||||
{}
|
||||
|
||||
operator T() const { return _value; }
|
||||
|
||||
private:
|
||||
T _value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Turns out that several target types could accept an LLSD param using any of
|
||||
* a few different conversions, e.g. LLUUID's constructor can accept LLUUID or
|
||||
* std::string. Therefore, the compiler can't decide which LLSD conversion
|
||||
* operator to choose, even though to us it seems obvious. But that's okay, we
|
||||
* can specialize LLSDParam for such target types, explicitly specifying the
|
||||
* desired conversion -- that's part of what LLSDParam is all about. Turns out
|
||||
* we have to do that enough to make it worthwhile generalizing. Use a macro
|
||||
* because I need to specify one of the asReal, etc., explicit conversion
|
||||
* methods as well as a type. If I'm overlooking a clever way to implement
|
||||
* that using a template instead, feel free to reimplement.
|
||||
*/
|
||||
#define LLSDParam_for(T, AS) \
|
||||
template <> \
|
||||
class LLSDParam<T> \
|
||||
{ \
|
||||
public: \
|
||||
LLSDParam(const LLSD& value): \
|
||||
_value(value.AS()) \
|
||||
{} \
|
||||
\
|
||||
operator T() const { return _value; } \
|
||||
\
|
||||
private: \
|
||||
T _value; \
|
||||
}
|
||||
|
||||
LLSDParam_for(float, asReal);
|
||||
LLSDParam_for(LLUUID, asUUID);
|
||||
LLSDParam_for(LLDate, asDate);
|
||||
LLSDParam_for(LLURI, asURI);
|
||||
LLSDParam_for(LLSD::Binary, asBinary);
|
||||
|
||||
/**
|
||||
* LLSDParam<const char*> is an example of the kind of conversion you can
|
||||
* support with LLSDParam beyond native LLSD conversions. Normally you can't
|
||||
* pass an LLSD object to a function accepting const char* -- but you can
|
||||
* safely pass an LLSDParam<const char*>(yourLLSD).
|
||||
*/
|
||||
template <>
|
||||
class LLSDParam<const char*>
|
||||
{
|
||||
private:
|
||||
// The difference here is that we store a std::string rather than a const
|
||||
// char*. It's important that the LLSDParam object own the std::string.
|
||||
std::string _value;
|
||||
// We don't bother storing the incoming LLSD object, but we do have to
|
||||
// distinguish whether _value is an empty string because the LLSD object
|
||||
// contains an empty string or because it's isUndefined().
|
||||
bool _undefined;
|
||||
|
||||
public:
|
||||
LLSDParam(const LLSD& value):
|
||||
_value(value),
|
||||
_undefined(value.isUndefined())
|
||||
{}
|
||||
|
||||
// The const char* we retrieve is for storage owned by our _value member.
|
||||
// That's how we guarantee that the const char* is valid for the lifetime
|
||||
// of this LLSDParam object. Constructing your LLSDParam in the argument
|
||||
// list should ensure that the LLSDParam object will persist for the
|
||||
// duration of the function call.
|
||||
operator const char*() const
|
||||
{
|
||||
if (_undefined)
|
||||
{
|
||||
// By default, an isUndefined() LLSD object's asString() method
|
||||
// will produce an empty string. But for a function accepting
|
||||
// const char*, it's often important to be able to pass NULL, and
|
||||
// isUndefined() seems like the best way. If you want to pass an
|
||||
// empty string, you can still pass LLSD(""). Without this special
|
||||
// case, though, no LLSD value could pass NULL.
|
||||
return NULL;
|
||||
}
|
||||
return _value.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
namespace llsd
|
||||
{
|
||||
|
||||
/*****************************************************************************
|
||||
* BOOST_FOREACH() helpers for LLSD
|
||||
*****************************************************************************/
|
||||
/// Usage: BOOST_FOREACH(LLSD item, inArray(someLLSDarray)) { ... }
|
||||
class inArray
|
||||
{
|
||||
public:
|
||||
inArray(const LLSD& array):
|
||||
_array(array)
|
||||
{}
|
||||
|
||||
typedef LLSD::array_const_iterator const_iterator;
|
||||
typedef LLSD::array_iterator iterator;
|
||||
|
||||
iterator begin() { return _array.beginArray(); }
|
||||
iterator end() { return _array.endArray(); }
|
||||
const_iterator begin() const { return _array.beginArray(); }
|
||||
const_iterator end() const { return _array.endArray(); }
|
||||
|
||||
private:
|
||||
LLSD _array;
|
||||
};
|
||||
|
||||
/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
|
||||
typedef std::map<LLSD::String, LLSD>::value_type MapEntry;
|
||||
|
||||
/// Usage: BOOST_FOREACH([const] MapEntry& e, inMap(someLLSDmap)) { ... }
|
||||
class inMap
|
||||
{
|
||||
public:
|
||||
inMap(const LLSD& map):
|
||||
_map(map)
|
||||
{}
|
||||
|
||||
typedef LLSD::map_const_iterator const_iterator;
|
||||
typedef LLSD::map_iterator iterator;
|
||||
|
||||
iterator begin() { return _map.beginMap(); }
|
||||
iterator end() { return _map.endMap(); }
|
||||
const_iterator begin() const { return _map.beginMap(); }
|
||||
const_iterator end() const { return _map.endMap(); }
|
||||
|
||||
private:
|
||||
LLSD _map;
|
||||
};
|
||||
|
||||
} // namespace llsd
|
||||
|
||||
#endif // LL_LLSDUTIL_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -60,6 +60,11 @@ LLFloaterRegListener::LLFloaterRegListener():
|
|||
"Ask to toggle the state of the floater specified in [\"name\"]",
|
||||
&LLFloaterRegListener::toggleInstance,
|
||||
requiredName);
|
||||
add("instanceVisible",
|
||||
"Return on [\"reply\"] an event whose [\"visible\"] indicates the visibility "
|
||||
"of the floater specified in [\"name\"]",
|
||||
&LLFloaterRegListener::instanceVisible,
|
||||
requiredName);
|
||||
LLSD requiredNameButton;
|
||||
requiredNameButton["name"] = LLSD();
|
||||
requiredNameButton["button"] = LLSD();
|
||||
|
|
@ -104,6 +109,14 @@ void LLFloaterRegListener::toggleInstance(const LLSD& event) const
|
|||
LLFloaterReg::toggleInstance(event["name"], event["key"]);
|
||||
}
|
||||
|
||||
void LLFloaterRegListener::instanceVisible(const LLSD& event) const
|
||||
{
|
||||
LLReqID reqID(event);
|
||||
LLSD reply(reqID.makeResponse());
|
||||
reply["visible"] = LLFloaterReg::instanceVisible(event["name"], event["key"]);
|
||||
LLEventPumps::instance().obtain(event["reply"]).post(reply);
|
||||
}
|
||||
|
||||
void LLFloaterRegListener::clickButton(const LLSD& event) const
|
||||
{
|
||||
// If the caller requests a reply, build the reply.
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ private:
|
|||
void showInstance(const LLSD& event) const;
|
||||
void hideInstance(const LLSD& event) const;
|
||||
void toggleInstance(const LLSD& event) const;
|
||||
void instanceVisible(const LLSD& event) const;
|
||||
void clickButton(const LLSD& event) const;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -193,8 +193,8 @@ LLFontGL* LLFontGL::getFontDefault()
|
|||
return NULL;
|
||||
}
|
||||
|
||||
char* _PREHASH_AgentData = "AgentData";
|
||||
char* _PREHASH_AgentID = "AgentID";
|
||||
char* _PREHASH_AgentData = const_cast<char*>("AgentData");
|
||||
char* _PREHASH_AgentID = const_cast<char*>("AgentID");
|
||||
|
||||
LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS);
|
||||
|
||||
|
|
|
|||
|
|
@ -412,6 +412,7 @@ set(viewer_SOURCE_FILES
|
|||
llsidepaneliteminfo.cpp
|
||||
llsidepaneltaskinfo.cpp
|
||||
llsidetray.cpp
|
||||
llsidetraylistener.cpp
|
||||
llsidetraypanelcontainer.cpp
|
||||
llsky.cpp
|
||||
llslurl.cpp
|
||||
|
|
@ -947,6 +948,7 @@ set(viewer_HEADER_FILES
|
|||
llsidepaneliteminfo.h
|
||||
llsidepaneltaskinfo.h
|
||||
llsidetray.h
|
||||
llsidetraylistener.h
|
||||
llsidetraypanelcontainer.h
|
||||
llsky.h
|
||||
llslurl.h
|
||||
|
|
|
|||
|
|
@ -480,10 +480,12 @@ LLLoginInstance::LLLoginInstance() :
|
|||
{
|
||||
mLoginModule->getEventPump().listen("lllogininstance",
|
||||
boost::bind(&LLLoginInstance::handleLoginEvent, this, _1));
|
||||
mDispatcher.add("fail.login", boost::bind(&LLLoginInstance::handleLoginFailure, this, _1));
|
||||
mDispatcher.add("connect", boost::bind(&LLLoginInstance::handleLoginSuccess, this, _1));
|
||||
mDispatcher.add("disconnect", boost::bind(&LLLoginInstance::handleDisconnect, this, _1));
|
||||
mDispatcher.add("indeterminate", boost::bind(&LLLoginInstance::handleIndeterminate, this, _1));
|
||||
// This internal use of LLEventDispatcher doesn't really need
|
||||
// per-function descriptions.
|
||||
mDispatcher.add("fail.login", "", boost::bind(&LLLoginInstance::handleLoginFailure, this, _1));
|
||||
mDispatcher.add("connect", "", boost::bind(&LLLoginInstance::handleLoginSuccess, this, _1));
|
||||
mDispatcher.add("disconnect", "", boost::bind(&LLLoginInstance::handleDisconnect, this, _1));
|
||||
mDispatcher.add("indeterminate", "", boost::bind(&LLLoginInstance::handleIndeterminate, this, _1));
|
||||
}
|
||||
|
||||
LLLoginInstance::~LLLoginInstance()
|
||||
|
|
@ -625,11 +627,7 @@ bool LLLoginInstance::handleLoginEvent(const LLSD& event)
|
|||
|
||||
// Call the method registered in constructor, if any, for more specific
|
||||
// handling
|
||||
LLEventDispatcher::Callable method(mDispatcher.get(event["change"]));
|
||||
if (! method.empty())
|
||||
{
|
||||
method(event);
|
||||
}
|
||||
mDispatcher.try_call(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@
|
|||
|
||||
#include "llsidepanelappearance.h"
|
||||
|
||||
#include "llsidetraylistener.h"
|
||||
|
||||
//#include "llscrollcontainer.h"
|
||||
|
||||
using namespace std;
|
||||
|
|
@ -71,6 +73,8 @@ static const std::string TAB_PANEL_CAPTION_TITLE_BOX = "sidetray_tab_title";
|
|||
|
||||
LLSideTray* LLSideTray::sInstance = 0;
|
||||
|
||||
static LLSideTrayListener sSideTrayListener(LLSideTray::getInstance);
|
||||
|
||||
// static
|
||||
LLSideTray* LLSideTray::getInstance()
|
||||
{
|
||||
|
|
@ -417,6 +421,11 @@ LLSideTrayTab* LLSideTrayTab::createInstance ()
|
|||
return tab;
|
||||
}
|
||||
|
||||
// Now that we know the definition of LLSideTrayTab, we can implement
|
||||
// tab_cast.
|
||||
template <>
|
||||
LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab) { return tab; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// LLSideTrayButton
|
||||
// Side Tray tab button with "tear off" handling.
|
||||
|
|
@ -530,6 +539,8 @@ LLSideTray::LLSideTray(const Params& params)
|
|||
// register handler function to process data from the xml.
|
||||
// panel_name should be specified via "parameter" attribute.
|
||||
commit.add("SideTray.ShowPanel", boost::bind(&LLSideTray::showPanel, this, _2, LLUUID::null));
|
||||
commit.add("SideTray.Toggle", boost::bind(&LLSideTray::onToggleCollapse, this));
|
||||
commit.add("SideTray.Collapse", boost::bind(&LLSideTray::collapseSideBar, this));
|
||||
LLTransientFloaterMgr::getInstance()->addControlView(this);
|
||||
LLView* side_bar_tabs = gViewerWindow->getRootView()->getChildView("side_bar_tabs");
|
||||
if (side_bar_tabs != NULL)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,13 @@
|
|||
class LLAccordionCtrl;
|
||||
class LLSideTrayTab;
|
||||
|
||||
// Deal with LLSideTrayTab being opaque. Generic do-nothing cast...
|
||||
template <class T>
|
||||
T tab_cast(LLSideTrayTab* tab) { return tab; }
|
||||
// specialized for implementation in presence of LLSideTrayTab definition
|
||||
template <>
|
||||
LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab);
|
||||
|
||||
// added inheritance from LLDestroyClass<LLSideTray> to enable Side Tray perform necessary actions
|
||||
// while disconnecting viewer in LLAppViewer::disconnectViewer().
|
||||
// LLDestroyClassList::instance().fireCallbacks() calls destroyClass method. See EXT-245.
|
||||
|
|
@ -217,6 +224,9 @@ private:
|
|||
}
|
||||
|
||||
private:
|
||||
// Since we provide no public way to query mTabs and mDetachedTabs, give
|
||||
// LLSideTrayListener friend access.
|
||||
friend class LLSideTrayListener;
|
||||
LLPanel* mButtonsPanel;
|
||||
typedef std::map<std::string,LLButton*> button_map_t;
|
||||
button_map_t mTabButtons;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* @file llsidetraylistener.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2011-02-15
|
||||
* @brief Implementation for llsidetraylistener.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2011&license=lgpl$
|
||||
* Copyright (c) 2011, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
// associated header
|
||||
#include "llsidetraylistener.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
// other Linden headers
|
||||
#include "llsidetray.h"
|
||||
#include "llsdutil.h"
|
||||
|
||||
LLSideTrayListener::LLSideTrayListener(const Getter& getter):
|
||||
LLEventAPI("LLSideTray",
|
||||
"Operations on side tray (e.g. query state, query tabs)"),
|
||||
mGetter(getter)
|
||||
{
|
||||
add("getCollapsed", "Send on [\"reply\"] an [\"open\"] Boolean",
|
||||
&LLSideTrayListener::getCollapsed, LLSDMap("reply", LLSD()));
|
||||
add("getTabs",
|
||||
"Send on [\"reply\"] a map of tab names and info about them",
|
||||
&LLSideTrayListener::getTabs, LLSDMap("reply", LLSD()));
|
||||
add("getPanels",
|
||||
"Send on [\"reply\"] data about panels available with SideTray.ShowPanel",
|
||||
&LLSideTrayListener::getPanels, LLSDMap("reply", LLSD()));
|
||||
}
|
||||
|
||||
void LLSideTrayListener::getCollapsed(const LLSD& event) const
|
||||
{
|
||||
LLReqID reqID(event);
|
||||
LLSD reply(reqID.makeResponse());
|
||||
reply["open"] = ! mGetter()->getCollapsed();
|
||||
LLEventPumps::instance().obtain(event["reply"]).post(reply);
|
||||
}
|
||||
|
||||
void LLSideTrayListener::getTabs(const LLSD& event) const
|
||||
{
|
||||
LLReqID reqID(event);
|
||||
LLSD reply(reqID.makeResponse());
|
||||
|
||||
LLSideTray* tray = mGetter();
|
||||
LLSD::Integer ord(0);
|
||||
for (LLSideTray::child_list_const_iter_t chi(tray->beginChild()), chend(tray->endChild());
|
||||
chi != chend; ++chi, ++ord)
|
||||
{
|
||||
LLView* child = *chi;
|
||||
// How much info is important? Toss in as much as seems reasonable for
|
||||
// each tab. But to me, at least for the moment, the most important
|
||||
// item is the tab name.
|
||||
LLSD info;
|
||||
// I like the idea of returning a map keyed by tab name. But as
|
||||
// compared to an array of maps, that loses sequence information.
|
||||
// Address that by indicating the original order in each map entry.
|
||||
info["ord"] = ord;
|
||||
info["visible"] = bool(child->getVisible());
|
||||
info["enabled"] = bool(child->getEnabled());
|
||||
info["available"] = child->isAvailable();
|
||||
reply[child->getName()] = info;
|
||||
}
|
||||
|
||||
LLEventPumps::instance().obtain(event["reply"]).post(reply);
|
||||
}
|
||||
|
||||
static LLSD getTabInfo(LLPanel* tab)
|
||||
{
|
||||
LLSD panels;
|
||||
for (LLPanel::tree_iterator_t ti(tab->beginTreeDFS()), tend(tab->endTreeDFS());
|
||||
ti != tend; ++ti)
|
||||
{
|
||||
// *ti is actually an LLView*, which had better not be NULL
|
||||
LLView* view(*ti);
|
||||
if (! view)
|
||||
{
|
||||
LL_ERRS("LLSideTrayListener") << "LLSideTrayTab '" << tab->getName()
|
||||
<< "' has a NULL child LLView*" << LL_ENDL;
|
||||
}
|
||||
|
||||
// The logic we use to decide what "panel" names to return is heavily
|
||||
// based on LLSideTray::showPanel(): the function that actually
|
||||
// implements the "SideTray.ShowPanel" operation. showPanel(), in
|
||||
// turn, depends on LLSideTray::openChildPanel(): when
|
||||
// openChildPanel() returns non-NULL, showPanel() stops searching
|
||||
// attached and detached LLSideTrayTab tabs.
|
||||
|
||||
// For each LLSideTrayTab, openChildPanel() first calls
|
||||
// findChildView(panel_name, true). In other words, panel_name need
|
||||
// not be a direct LLSideTrayTab child, it's sought recursively.
|
||||
// That's why we use (begin|end)TreeDFS() in this loop.
|
||||
|
||||
// But this tree_iterator_t loop will actually traverse every widget
|
||||
// in every panel. Returning all those names will not help our caller:
|
||||
// passing most such names to openChildPanel() would not do what we
|
||||
// want. Even though the code suggests that passing ANY valid
|
||||
// side-panel widget name to openChildPanel() will open the tab
|
||||
// containing that widget, results could get confusing since followup
|
||||
// (onOpen()) logic wouldn't be invoked, and showPanel() wouldn't stop
|
||||
// searching because openChildPanel() would return NULL.
|
||||
|
||||
// We must filter these LLView items, using logic that (sigh!) mirrors
|
||||
// openChildPanel()'s own.
|
||||
|
||||
// openChildPanel() returns a non-NULL LLPanel* when either:
|
||||
// - the LLView is a direct child of an LLSideTrayPanelContainer
|
||||
// - the LLView is itself an LLPanel.
|
||||
// But as LLSideTrayPanelContainer can directly contain LLView items
|
||||
// that are NOT themselves LLPanels (e.g. "sidebar_me" contains an
|
||||
// LLButton called "Jump Right Arrow"), we'd better focus only on
|
||||
// LLSideTrayPanelContainer children that are themselves LLPanel
|
||||
// items. Which means that the second test completely subsumes the
|
||||
// first.
|
||||
LLPanel* panel(dynamic_cast<LLPanel*>(view));
|
||||
if (panel)
|
||||
{
|
||||
// Maybe it's overkill to construct an LLSD::Map for each panel, but
|
||||
// the possibility remains that we might want to deliver more info
|
||||
// about each panel than just its name.
|
||||
panels.append(LLSDMap("name", panel->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return LLSDMap("panels", panels);
|
||||
}
|
||||
|
||||
void LLSideTrayListener::getPanels(const LLSD& event) const
|
||||
{
|
||||
LLReqID reqID(event);
|
||||
LLSD reply(reqID.makeResponse());
|
||||
|
||||
LLSideTray* tray = mGetter();
|
||||
// Iterate through the attached tabs.
|
||||
LLSD::Integer ord(0);
|
||||
for (LLSideTray::child_vector_t::const_iterator
|
||||
ati(tray->mTabs.begin()), atend(tray->mTabs.end());
|
||||
ati != atend; ++ati)
|
||||
{
|
||||
// We don't have access to LLSideTrayTab: the class definition is
|
||||
// hidden in llsidetray.cpp. But as LLSideTrayTab isa LLPanel, use the
|
||||
// LLPanel API. Unfortunately, without the LLSideTrayTab definition,
|
||||
// the compiler doesn't even know this LLSideTrayTab* is an LLPanel*.
|
||||
// Persuade it.
|
||||
LLPanel* tab(tab_cast<LLPanel*>(*ati));
|
||||
reply[tab->getName()] = getTabInfo(tab).with("attached", true).with("ord", ord);
|
||||
}
|
||||
|
||||
// Now iterate over the detached tabs. These can also be opened via
|
||||
// SideTray.ShowPanel.
|
||||
ord = 0;
|
||||
for (LLSideTray::child_vector_t::const_iterator
|
||||
dti(tray->mDetachedTabs.begin()), dtend(tray->mDetachedTabs.end());
|
||||
dti != dtend; ++dti)
|
||||
{
|
||||
LLPanel* tab(tab_cast<LLPanel*>(*dti));
|
||||
reply[tab->getName()] = getTabInfo(tab).with("attached", false).with("ord", ord);
|
||||
}
|
||||
|
||||
LLEventPumps::instance().obtain(event["reply"]).post(reply);
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* @file llsidetraylistener.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2011-02-15
|
||||
* @brief
|
||||
*
|
||||
* $LicenseInfo:firstyear=2011&license=lgpl$
|
||||
* Copyright (c) 2011, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLSIDETRAYLISTENER_H)
|
||||
#define LL_LLSIDETRAYLISTENER_H
|
||||
|
||||
#include "lleventapi.h"
|
||||
#include <boost/function.hpp>
|
||||
|
||||
class LLSideTray;
|
||||
class LLSD;
|
||||
|
||||
class LLSideTrayListener: public LLEventAPI
|
||||
{
|
||||
typedef boost::function<LLSideTray*()> Getter;
|
||||
|
||||
public:
|
||||
LLSideTrayListener(const Getter& getter);
|
||||
|
||||
private:
|
||||
void getCollapsed(const LLSD& event) const;
|
||||
void getTabs(const LLSD& event) const;
|
||||
void getPanels(const LLSD& event) const;
|
||||
|
||||
Getter mGetter;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLSIDETRAYLISTENER_H) */
|
||||
Loading…
Reference in New Issue