Merge viewer-bear

master
Ansariel 2017-05-18 08:55:43 +02:00
commit 55baa89640
21 changed files with 686 additions and 65 deletions

View File

@ -359,26 +359,27 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventfilter "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")
## llexception_test.cpp isn't a regression test, and doesn't need to be run
## every build. It's to help a developer make implementation choices about

View File

@ -38,12 +38,18 @@
#include "llerror.h" // LL_ERRS
#include "llsdutil.h" // llsd_matches()
/*****************************************************************************
* LLEventFilter
*****************************************************************************/
LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):
LLEventStream(name, tweak),
mSource(source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1)))
{
}
/*****************************************************************************
* LLEventMatching
*****************************************************************************/
LLEventMatching::LLEventMatching(const LLSD& pattern):
LLEventFilter("matching"),
mPattern(pattern)
@ -64,6 +70,9 @@ bool LLEventMatching::post(const LLSD& event)
return LLEventStream::post(event);
}
/*****************************************************************************
* LLEventTimeoutBase
*****************************************************************************/
LLEventTimeoutBase::LLEventTimeoutBase():
LLEventFilter("timeout")
{
@ -148,6 +157,14 @@ bool LLEventTimeoutBase::tick(const LLSD&)
return false; // show event to other listeners
}
bool LLEventTimeoutBase::running() const
{
return mMainloop.connected();
}
/*****************************************************************************
* LLEventTimeout
*****************************************************************************/
LLEventTimeout::LLEventTimeout() {}
LLEventTimeout::LLEventTimeout(LLEventPump& source):
@ -164,3 +181,231 @@ bool LLEventTimeout::countdownElapsed() const
{
return mTimer.hasExpired();
}
/*****************************************************************************
* LLEventBatch
*****************************************************************************/
LLEventBatch::LLEventBatch(std::size_t size):
LLEventFilter("batch"),
mBatchSize(size)
{}
LLEventBatch::LLEventBatch(LLEventPump& source, std::size_t size):
LLEventFilter(source, "batch"),
mBatchSize(size)
{}
void LLEventBatch::flush()
{
// copy and clear mBatch BEFORE posting to avoid weird circularity effects
LLSD batch(mBatch);
mBatch.clear();
LLEventStream::post(batch);
}
bool LLEventBatch::post(const LLSD& event)
{
mBatch.append(event);
// calling setSize(same) performs the very check we want
setSize(mBatchSize);
return false;
}
void LLEventBatch::setSize(std::size_t size)
{
mBatchSize = size;
// changing the size might mean that we have to flush NOW
if (mBatch.size() >= mBatchSize)
{
flush();
}
}
/*****************************************************************************
* LLEventThrottleBase
*****************************************************************************/
LLEventThrottleBase::LLEventThrottleBase(F32 interval):
LLEventFilter("throttle"),
mInterval(interval),
mPosts(0)
{}
LLEventThrottleBase::LLEventThrottleBase(LLEventPump& source, F32 interval):
LLEventFilter(source, "throttle"),
mInterval(interval),
mPosts(0)
{}
void LLEventThrottleBase::flush()
{
// flush() is a no-op unless there's something pending.
// Don't test mPending because there's no requirement that the consumer
// post() anything but an isUndefined(). This is what mPosts is for.
if (mPosts)
{
mPosts = 0;
alarmCancel();
// This is not to set our alarm; we are not yet requesting
// any notification. This is just to track whether subsequent post()
// calls fall within this mInterval or not.
timerSet(mInterval);
// copy and clear mPending BEFORE posting to avoid weird circularity
// effects
LLSD pending = mPending;
mPending.clear();
LLEventStream::post(pending);
}
}
LLSD LLEventThrottleBase::pending() const
{
return mPending;
}
bool LLEventThrottleBase::post(const LLSD& event)
{
// Always capture most recent post() event data. If caller wants to
// aggregate multiple events, let them retrieve pending() and modify
// before calling post().
mPending = event;
// Always increment mPosts. Unless we count this call, flush() does
// nothing.
++mPosts;
// We reset mTimer on every flush() call to let us know if we're still
// within the same mInterval. So -- are we?
F32 timeRemaining = timerGetRemaining();
if (! timeRemaining)
{
// more than enough time has elapsed, immediately flush()
flush();
}
else
{
// still within mInterval of the last flush() call: have to defer
if (! alarmRunning())
{
// timeRemaining tells us how much longer it will be until
// mInterval seconds since the last flush() call. At that time,
// flush() deferred events.
alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this));
}
}
return false;
}
void LLEventThrottleBase::setInterval(F32 interval)
{
F32 oldInterval = mInterval;
mInterval = interval;
// If we are not now within oldInterval of the last flush(), we're done:
// this will only affect behavior starting with the next flush().
F32 timeRemaining = timerGetRemaining();
if (timeRemaining)
{
// We are currently within oldInterval of the last flush(). Figure out
// how much time remains until (the new) mInterval of the last
// flush(). Bt we don't actually store a timestamp for the last
// flush(); it's implicit. There are timeRemaining seconds until what
// used to be the end of the interval. Move that endpoint by the
// difference between the new interval and the old.
timeRemaining += (mInterval - oldInterval);
// If we're called with a larger interval, the difference is positive
// and timeRemaining increases.
// If we're called with a smaller interval, the difference is negative
// and timeRemaining decreases. The interesting case is when it goes
// nonpositive: when the new interval means we can flush immediately.
if (timeRemaining <= 0.0f)
{
flush();
}
else
{
// immediately reset mTimer
timerSet(timeRemaining);
// and if mAlarm is running, reset that too
if (alarmRunning())
{
alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this));
}
}
}
}
F32 LLEventThrottleBase::getDelay() const
{
return timerGetRemaining();
}
/*****************************************************************************
* LLEventThrottle implementation
*****************************************************************************/
LLEventThrottle::LLEventThrottle(F32 interval):
LLEventThrottleBase(interval)
{}
LLEventThrottle::LLEventThrottle(LLEventPump& source, F32 interval):
LLEventThrottleBase(source, interval)
{}
void LLEventThrottle::alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action)
{
mAlarm.actionAfter(interval, action);
}
bool LLEventThrottle::alarmRunning() const
{
return mAlarm.running();
}
void LLEventThrottle::alarmCancel()
{
return mAlarm.cancel();
}
void LLEventThrottle::timerSet(F32 interval)
{
mTimer.setTimerExpirySec(interval);
}
F32 LLEventThrottle::timerGetRemaining() const
{
return mTimer.getRemainingTimeF32();
}
/*****************************************************************************
* LLEventBatchThrottle
*****************************************************************************/
LLEventBatchThrottle::LLEventBatchThrottle(F32 interval, std::size_t size):
LLEventThrottle(interval),
mBatchSize(size)
{}
LLEventBatchThrottle::LLEventBatchThrottle(LLEventPump& source, F32 interval, std::size_t size):
LLEventThrottle(source, interval),
mBatchSize(size)
{}
bool LLEventBatchThrottle::post(const LLSD& event)
{
// simply retrieve pending value and append the new event to it
LLSD partial = pending();
partial.append(event);
bool ret = LLEventThrottle::post(partial);
// The post() call above MIGHT have called flush() already. If it did,
// then pending() was reset to empty. If it did not, though, but the batch
// size has grown to the limit, flush() anyway. If there's a limit at all,
// of course. Calling setSize(same) performs the very check we want.
setSize(mBatchSize);
return ret;
}
void LLEventBatchThrottle::setSize(std::size_t size)
{
mBatchSize = size;
// Changing the size might mean that we have to flush NOW. Don't forget
// that 0 means unlimited.
if (mBatchSize && pending().size() >= mBatchSize)
{
flush();
}
}

View File

@ -177,6 +177,9 @@ public:
/// Cancel timer without event
void cancel();
/// Is this timer currently running?
bool running() const;
protected:
virtual void setCountdown(F32 seconds) = 0;
virtual bool countdownElapsed() const = 0;
@ -215,4 +218,162 @@ private:
LLTimer mTimer;
};
/**
* LLEventBatch: accumulate post() events (LLSD blobs) into an LLSD Array
* until the array reaches a certain size, then call listeners with the Array
* and clear it back to empty.
*/
class LL_COMMON_API LLEventBatch: public LLEventFilter
{
public:
// pass batch size
LLEventBatch(std::size_t size);
// construct and connect
LLEventBatch(LLEventPump& source, std::size_t size);
// force out the pending batch
void flush();
// accumulate an event and flush() when big enough
virtual bool post(const LLSD& event);
// query or reset batch size
std::size_t getSize() const { return mBatchSize; }
void setSize(std::size_t size);
private:
LLSD mBatch;
std::size_t mBatchSize;
};
/**
* LLEventThrottleBase: construct with a time interval. Regardless of how
* frequently you call post(), LLEventThrottle will pass on an event to
* its listeners no more often than once per specified interval.
*
* A new event after more than the specified interval will immediately be
* passed along to listeners. But subsequent events will be delayed until at
* least one time interval since listeners were last called. Consider the
* sequence below. Suppose we have an LLEventThrottle constructed with an
* interval of 3 seconds. The numbers on the left are timestamps in seconds
* relative to an arbitrary reference point.
*
* 1: post(): event immediately passed to listeners, next no sooner than 4
* 2: post(): deferred: waiting for 3 seconds to elapse
* 3: post(): deferred
* 4: no post() call, but event delivered to listeners; next no sooner than 7
* 6: post(): deferred
* 7: no post() call, but event delivered; next no sooner than 10
* 12: post(): immediately passed to listeners, next no sooner than 15
* 17: post(): immediately passed to listeners, next no sooner than 20
*
* For a deferred event, the LLSD blob delivered to listeners is from the most
* recent deferred post() call. However, a sender may obtain the previous
* event blob by calling pending(), modifying it as desired and post()ing the
* new value. (See LLEventBatchThrottle.) Each time an event is delivered to
* listeners, the pending() value is reset to isUndefined().
*
* You may also call flush() to immediately pass along any deferred events to
* all listeners.
*
* @NOTE This is an abstract base class so that, for testing, we can use an
* alternate "timer" that doesn't actually consume real time. See
* LLEventThrottle.
*/
class LL_COMMON_API LLEventThrottleBase: public LLEventFilter
{
public:
// pass time interval
LLEventThrottleBase(F32 interval);
// construct and connect
LLEventThrottleBase(LLEventPump& source, F32 interval);
// force out any deferred events
void flush();
// retrieve (aggregate) deferred event since last event sent to listeners
LLSD pending() const;
// register an event, may be either passed through or deferred
virtual bool post(const LLSD& event);
// query or reset interval
F32 getInterval() const { return mInterval; }
void setInterval(F32 interval);
// deferred posts
std::size_t getPostCount() const { return mPosts; }
// time until next event would be passed through, 0.0 if now
F32 getDelay() const;
protected:
// Implement these time-related methods for a valid LLEventThrottleBase
// subclass (see LLEventThrottle). For testing, we use a subclass that
// doesn't involve actual elapsed time.
virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) = 0;
virtual bool alarmRunning() const = 0;
virtual void alarmCancel() = 0;
virtual void timerSet(F32 interval) = 0;
virtual F32 timerGetRemaining() const = 0;
private:
// remember throttle interval
F32 mInterval;
// count post() calls since last flush()
std::size_t mPosts;
// pending event data from most recent deferred event
LLSD mPending;
};
/**
* Production implementation of LLEventThrottle.
*/
class LLEventThrottle: public LLEventThrottleBase
{
public:
LLEventThrottle(F32 interval);
LLEventThrottle(LLEventPump& source, F32 interval);
private:
virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) /*override*/;
virtual bool alarmRunning() const /*override*/;
virtual void alarmCancel() /*override*/;
virtual void timerSet(F32 interval) /*override*/;
virtual F32 timerGetRemaining() const /*override*/;
// use this to arrange a deferred flush() call
LLEventTimeout mAlarm;
// use this to track whether we're within mInterval of last flush()
LLTimer mTimer;
};
/**
* LLEventBatchThrottle: like LLEventThrottle, it's reluctant to pass events
* to listeners more often than once per specified time interval -- but only
* reluctant, since exceeding the specified batch size limit can cause it to
* deliver accumulated events sooner. Like LLEventBatch, it accumulates
* pending events into an LLSD Array, optionally flushing when the batch grows
* to a certain size.
*/
class LLEventBatchThrottle: public LLEventThrottle
{
public:
// pass time interval and (optionally) max batch size; 0 means batch can
// grow arbitrarily large
LLEventBatchThrottle(F32 interval, std::size_t size = 0);
// construct and connect
LLEventBatchThrottle(LLEventPump& source, F32 interval, std::size_t size = 0);
// append a new event to current batch
virtual bool post(const LLSD& event);
// query or reset batch size
std::size_t getSize() const { return mBatchSize; }
void setSize(std::size_t size);
private:
std::size_t mBatchSize;
};
#endif /* ! defined(LL_LLEVENTFILTER_H) */

View File

@ -138,4 +138,15 @@ struct Collect
StringVec result;
};
struct Concat
{
bool operator()(const LLSD& event)
{
result += event.asString();
return false;
}
void clear() { result.clear(); }
std::string result;
};
#endif /* ! defined(LL_LISTENER_H) */

View File

@ -70,6 +70,85 @@ private:
bool mElapsed;
};
// Similar remarks about LLEventThrottle: we're actually testing the logic in
// LLEventThrottleBase, dummying out the LLTimer and LLEventTimeout used by
// the production LLEventThrottle class.
class TestEventThrottle: public LLEventThrottleBase
{
public:
TestEventThrottle(F32 interval):
LLEventThrottleBase(interval),
mAlarmRemaining(-1),
mTimerRemaining(-1)
{}
TestEventThrottle(LLEventPump& source, F32 interval):
LLEventThrottleBase(source, interval),
mAlarmRemaining(-1),
mTimerRemaining(-1)
{}
/*----- implementation of LLEventThrottleBase timing functionality -----*/
virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) /*override*/
{
mAlarmRemaining = interval;
mAlarmAction = action;
}
virtual bool alarmRunning() const /*override*/
{
// decrementing to exactly 0 should mean the alarm fires
return mAlarmRemaining > 0;
}
virtual void alarmCancel() /*override*/
{
mAlarmRemaining = -1;
}
virtual void timerSet(F32 interval) /*override*/
{
mTimerRemaining = interval;
}
virtual F32 timerGetRemaining() const /*override*/
{
// LLTimer.getRemainingTimeF32() never returns negative; 0.0 means expired
return (mTimerRemaining > 0.0)? mTimerRemaining : 0.0;
}
/*------------------- methods for manipulating time --------------------*/
void alarmAdvance(F32 delta)
{
bool wasRunning = alarmRunning();
mAlarmRemaining -= delta;
if (wasRunning && ! alarmRunning())
{
mAlarmAction();
}
}
void timerAdvance(F32 delta)
{
// This simple implementation, like alarmAdvance(), completely ignores
// HOW negative mTimerRemaining might go. All that matters is whether
// it's negative. We trust that no test method in this source will
// drive it beyond the capacity of an F32. Seems like a safe assumption.
mTimerRemaining -= delta;
}
void advance(F32 delta)
{
// Advance the timer first because it has no side effects.
// alarmAdvance() might call flush(), which will need to see the
// change in the timer.
timerAdvance(delta);
alarmAdvance(delta);
}
F32 mAlarmRemaining, mTimerRemaining;
LLEventTimeoutBase::Action mAlarmAction;
};
/*****************************************************************************
* TUT
*****************************************************************************/
@ -116,7 +195,9 @@ namespace tut
listener0.listenTo(driver));
// Construct a pattern LLSD: desired Event must have a key "foo"
// containing string "bar"
LLEventMatching filter(driver, LLSD().insert("foo", "bar"));
LLSD pattern;
pattern.insert("foo", "bar");
LLEventMatching filter(driver, pattern);
listener1.reset(0);
LLTempBoundListener temp2(
listener1.listenTo(filter));
@ -285,6 +366,47 @@ namespace tut
mainloop.post(17);
check_listener("no timeout 3", listener0, LLSD(0));
}
template<> template<>
void filter_object::test<5>()
{
set_test_name("LLEventThrottle");
TestEventThrottle throttle(3);
Concat cat;
throttle.listen("concat", boost::ref(cat));
// (sequence taken from LLEventThrottleBase Doxygen comments)
// 1: post(): event immediately passed to listeners, next no sooner than 4
throttle.advance(1);
throttle.post("1");
ensure_equals("1", cat.result, "1"); // delivered immediately
// 2: post(): deferred: waiting for 3 seconds to elapse
throttle.advance(1);
throttle.post("2");
ensure_equals("2", cat.result, "1"); // "2" not yet delivered
// 3: post(): deferred
throttle.advance(1);
throttle.post("3");
ensure_equals("3", cat.result, "1"); // "3" not yet delivered
// 4: no post() call, but event delivered to listeners; next no sooner than 7
throttle.advance(1);
ensure_equals("4", cat.result, "13"); // "3" delivered
// 6: post(): deferred
throttle.advance(2);
throttle.post("6");
ensure_equals("6", cat.result, "13"); // "6" not yet delivered
// 7: no post() call, but event delivered; next no sooner than 10
throttle.advance(1);
ensure_equals("7", cat.result, "136"); // "6" delivered
// 12: post(): immediately passed to listeners, next no sooner than 15
throttle.advance(5);
throttle.post(";12");
ensure_equals("12", cat.result, "136;12"); // "12" delivered
// 17: post(): immediately passed to listeners, next no sooner than 20
throttle.advance(5);
throttle.post(";17");
ensure_equals("17", cat.result, "136;12;17"); // "17" delivered
}
} // namespace tut
/*****************************************************************************

View File

@ -1167,17 +1167,6 @@ void LLAvatarActions::shareWithAvatars(LLView * panel)
LLNotificationsUtil::add("ShareNotification");
}
//static
void LLAvatarActions::purgeSelectedItems()
{
const std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs();
if (inventory_selected_uuids.empty()) return;
LLSD args;
args["COUNT"] = (S32)inventory_selected_uuids.size();
LLNotificationsUtil::add("PurgeSelectedItems", args, LLSD(), &callbackPurgeSelectedItems);
}
// static
bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NULL*/)
{
@ -1524,24 +1513,6 @@ bool LLAvatarActions::callbackAddFriendWithMessage(const LLSD& notification, con
return false;
}
bool LLAvatarActions::callbackPurgeSelectedItems(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option == 0)
{
const std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs();
if (inventory_selected_uuids.empty()) return false;
std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end();
for (; it != it_end; ++it)
{
remove_inventory_object(*it, NULL);
}
}
return false;
}
// static
bool LLAvatarActions::handleKick(const LLSD& notification, const LLSD& response)
{

View File

@ -135,8 +135,6 @@ public:
*/
static void shareWithAvatars(LLView * panel);
static void purgeSelectedItems();
/**
* Block/unblock the avatar.
*/
@ -325,7 +323,6 @@ protected:
private:
static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response);
static bool callbackPurgeSelectedItems(const LLSD& notification, const LLSD& response);
static bool handleRemove(const LLSD& notification, const LLSD& response);
static bool handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id);
static bool handleFreezeAvatar(const LLSD& notification, const LLSD& response);

View File

@ -35,6 +35,7 @@
#include "llfloaterreg.h"
#include "llnamelistctrl.h"
#include "llmenugl.h"
#include "lltrans.h"
#include "llviewerobjectlist.h"
#include "llvoavatar.h"
@ -146,6 +147,8 @@ void LLFloaterAvatarRenderSettings::updateList()
item_params.columns.add().value(av_name.getCompleteName()).column("name");
std::string setting = getString(iter->second == 1 ? "av_never_render" : "av_always_render");
item_params.columns.add().value(setting).column("setting");
std::string timestamp = createTimestamp(LLRenderMuteList::getInstance()->getVisualMuteDate(iter->first));
item_params.columns.add().value(timestamp).column("timestamp");
mAvatarSettingsList->addNameItemRow(item_params);
}
}
@ -207,15 +210,7 @@ void LLFloaterAvatarRenderSettings::onCustomAction (const LLSD& userdata, const
new_setting = S32(LLVOAvatar::AV_ALWAYS_RENDER);
}
LLVOAvatar *avatarp = find_avatar(av_id);
if (avatarp)
{
avatarp->setVisualMuteSettings(LLVOAvatar::VisualMuteSettings(new_setting));
}
else
{
LLRenderMuteList::getInstance()->saveVisualMuteSetting(av_id, new_setting);
}
setAvatarRenderSetting(av_id, new_setting);
}
@ -275,15 +270,45 @@ void LLFloaterAvatarRenderSettings::onClickAdd(const LLSD& userdata)
void LLFloaterAvatarRenderSettings::callbackAvatarPicked(const uuid_vec_t& ids, S32 visual_setting)
{
if (ids.empty()) return;
setAvatarRenderSetting(ids[0], visual_setting);
}
LLVOAvatar *avatarp = find_avatar(ids[0]);
void LLFloaterAvatarRenderSettings::setAvatarRenderSetting(const LLUUID& av_id, S32 new_setting)
{
LLVOAvatar *avatarp = find_avatar(av_id);
if (avatarp)
{
avatarp->setVisualMuteSettings(LLVOAvatar::VisualMuteSettings(visual_setting));
avatarp->setVisualMuteSettings(LLVOAvatar::VisualMuteSettings(new_setting));
}
else
{
LLRenderMuteList::getInstance()->saveVisualMuteSetting(ids[0], visual_setting);
LLRenderMuteList::getInstance()->saveVisualMuteSetting(av_id, new_setting);
}
}
BOOL LLFloaterAvatarRenderSettings::handleKeyHere(KEY key, MASK mask )
{
BOOL handled = FALSE;
if (KEY_DELETE == key)
{
setAvatarRenderSetting(mAvatarSettingsList->getCurrentID(), (S32)LLVOAvatar::AV_RENDER_NORMALLY);
handled = TRUE;
}
return handled;
}
std::string LLFloaterAvatarRenderSettings::createTimestamp(S32 datetime)
{
std::string timeStr;
LLSD substitution;
substitution["datetime"] = datetime;
timeStr = "["+LLTrans::getString ("TimeMonth")+"]/["
+LLTrans::getString ("TimeDay")+"]/["
+LLTrans::getString ("TimeYear")+"]";
LLStringUtil::format (timeStr, substitution);
return timeStr;
}
#endif

View File

@ -45,6 +45,7 @@ public:
/*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void draw();
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask );
void onAvatarListRightClick(LLUICtrl* ctrl, S32 x, S32 y);
@ -53,6 +54,9 @@ public:
void onCustomAction (const LLSD& userdata, const LLUUID& av_id);
bool isActionChecked(const LLSD& userdata, const LLUUID& av_id);
void onClickAdd(const LLSD& userdata);
void setAvatarRenderSetting(const LLUUID& av_id, S32 new_setting);
std::string createTimestamp(S32 datetime);
static void setNeedsUpdate();

View File

@ -60,13 +60,19 @@ LLFloaterPreviewTrash::~LLFloaterPreviewTrash()
// static
void LLFloaterPreviewTrash::show()
{
LLFloaterReg::showTypedInstance<LLFloaterPreviewTrash>("preview_trash");
LLFloaterReg::showTypedInstance<LLFloaterPreviewTrash>("preview_trash", LLSD(), TRUE);
}
// static
bool LLFloaterPreviewTrash::isVisible()
{
return LLFloaterReg::instanceVisible("preview_trash");
}
void LLFloaterPreviewTrash::onClickEmpty()
{
gInventory.emptyFolderType("", LLFolderType::FT_TRASH);
gInventory.emptyFolderType("PurgeSelectedItems", LLFolderType::FT_TRASH);
closeFloater();
}

View File

@ -35,6 +35,7 @@ class LLFloaterPreviewTrash
{
public:
static void show();
static bool isVisible();
LLFloaterPreviewTrash(const LLSD& key);
~LLFloaterPreviewTrash();

View File

@ -417,6 +417,7 @@ void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch)
}
}
removeBatchNoCheck(batch);
model->checkTrashOverflow();
}
void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch)
@ -4195,6 +4196,13 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
}
if(trash_id == mUUID)
{
bool is_recent_panel = false;
LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE);
if (active_panel && (active_panel->getName() == "Recent Items"))
{
is_recent_panel = true;
}
// This is the trash.
items.push_back(std::string("Empty Trash"));
@ -4202,7 +4210,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
LLInventoryModel::item_array_t* item_array;
gInventory.getDirectDescendentsOf(mUUID, cat_array, item_array);
// Enable Empty menu item only when there is something to act upon.
if (0 == cat_array->size() && 0 == item_array->size())
if ((0 == cat_array->size() && 0 == item_array->size()) || is_recent_panel)
{
disabled_items.push_back(std::string("Empty Trash"));
}

View File

@ -3623,8 +3623,16 @@ void LLInventoryModel::checkTrashOverflow()
const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH);
if (getDescendentsCountRecursive(trash_id, trash_max_capacity) >= trash_max_capacity)
{
LLNotificationsUtil::add("TrashIsFull", LLSD(), LLSD(),
boost::bind(callback_preview_trash_folder, _1, _2));
if (LLFloaterPreviewTrash::isVisible())
{
// bring to front
LLFloaterPreviewTrash::show();
}
else
{
LLNotificationsUtil::add("TrashIsFull", LLSD(), LLSD(),
boost::bind(callback_preview_trash_folder, _1, _2));
}
}
}

View File

@ -43,6 +43,7 @@
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llnotificationsutil.h"
#include "llpreview.h"
#include "llsidepanelinventory.h"
#include "lltrans.h"
@ -1342,6 +1343,33 @@ void LLInventoryPanel::fileUploadLocation(const LLSD& userdata)
}
}
void LLInventoryPanel::purgeSelectedItems()
{
const std::set<LLFolderViewItem*> inventory_selected = mFolderRoot.get()->getSelectionList();
if (inventory_selected.empty()) return;
LLSD args;
args["COUNT"] = (S32)inventory_selected.size();
LLNotificationsUtil::add("PurgeSelectedItems", args, LLSD(), boost::bind(&LLInventoryPanel::callbackPurgeSelectedItems, this, _1, _2));
}
void LLInventoryPanel::callbackPurgeSelectedItems(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option == 0)
{
const std::set<LLFolderViewItem*> inventory_selected = mFolderRoot.get()->getSelectionList();
if (inventory_selected.empty()) return;
std::set<LLFolderViewItem*>::const_iterator it = inventory_selected.begin();
const std::set<LLFolderViewItem*>::const_iterator it_end = inventory_selected.end();
for (; it != it_end; ++it)
{
LLUUID item_id = static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID();
remove_inventory_object(item_id, NULL);
}
}
}
bool LLInventoryPanel::attachObject(const LLSD& userdata)
{
// Copy selected item UUIDs to a vector.
@ -1625,6 +1653,11 @@ void LLInventoryPanel::updateSelection()
void LLInventoryPanel::doToSelected(const LLSD& userdata)
{
if (("purge" == userdata.asString()))
{
purgeSelectedItems();
return;
}
LLInventoryAction::doToSelected(mInventory, mFolderRoot.get(), userdata.asString());
return;

View File

@ -211,6 +211,7 @@ public:
void doCreate(const LLSD& userdata);
bool beginIMSession();
void fileUploadLocation(const LLSD& userdata);
void purgeSelectedItems();
bool attachObject(const LLSD& userdata);
static void idle(void* user_data);
@ -246,6 +247,8 @@ public:
// Clean up stuff when the folder root gets deleted
void clearFolderRoot();
void callbackPurgeSelectedItems(const LLSD& notification, const LLSD& response);
protected:
void openStartFolderOrMyInventory(); // open the first level of inventory
void onItemsCompletion(); // called when selected items are complete

View File

@ -887,13 +887,14 @@ bool LLRenderMuteList::saveToFile()
LL_WARNS() << "Couldn't open render mute list file: " << filename << LL_ENDL;
return false;
}
for (std::map<LLUUID, S32>::iterator it = sVisuallyMuteSettingsMap.begin(); it != sVisuallyMuteSettingsMap.end(); ++it)
{
if (it->second != 0)
{
std::string id_string;
it->first.toString(id_string);
fprintf(fp, "%d %s\n", (S32)it->second, id_string.c_str());
fprintf(fp, "%d %s [%d]\n", (S32)it->second, id_string.c_str(), (S32)sVisuallyMuteDateMap[it->first]);
}
}
fclose(fp);
@ -916,8 +917,10 @@ bool LLRenderMuteList::loadFromFile()
{
id_buffer[0] = '\0';
S32 setting = 0;
sscanf(buffer, " %d %254s\n", &setting, id_buffer);
S32 time = 0;
sscanf(buffer, " %d %254s [%d]\n", &setting, id_buffer, &time);
sVisuallyMuteSettingsMap[LLUUID(id_buffer)] = setting;
sVisuallyMuteDateMap[LLUUID(id_buffer)] = (time == 0) ? (S32)time_corrected() : time;
}
fclose(fp);
return true;
@ -928,10 +931,15 @@ void LLRenderMuteList::saveVisualMuteSetting(const LLUUID& agent_id, S32 setting
if(setting == 0)
{
sVisuallyMuteSettingsMap.erase(agent_id);
sVisuallyMuteDateMap.erase(agent_id);
}
else
{
sVisuallyMuteSettingsMap[agent_id] = setting;
if (sVisuallyMuteDateMap.find(agent_id) == sVisuallyMuteDateMap.end())
{
sVisuallyMuteDateMap[agent_id] = (S32)time_corrected();
}
}
saveToFile();
notifyObservers();
@ -948,6 +956,17 @@ S32 LLRenderMuteList::getSavedVisualMuteSetting(const LLUUID& agent_id)
return 0;
}
S32 LLRenderMuteList::getVisualMuteDate(const LLUUID& agent_id)
{
std::map<LLUUID, S32>::iterator iter = sVisuallyMuteDateMap.find(agent_id);
if (iter != sVisuallyMuteDateMap.end())
{
return iter->second;
}
return 0;
}
void LLRenderMuteList::addObserver(LLMuteListObserver* observer)
{
mObservers.insert(observer);

View File

@ -188,10 +188,13 @@ public:
S32 getSavedVisualMuteSetting(const LLUUID& agent_id);
void saveVisualMuteSetting(const LLUUID& agent_id, S32 setting);
S32 getVisualMuteDate(const LLUUID& agent_id);
void addObserver(LLMuteListObserver* observer);
void removeObserver(LLMuteListObserver* observer);
std::map<LLUUID, S32> sVisuallyMuteSettingsMap;
std::map<LLUUID, S32> sVisuallyMuteDateMap;
private:
void notifyObservers();

View File

@ -131,7 +131,6 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p)
mCommitCallbackRegistrar.add("Inventory.ResetFilters", boost::bind(&LLPanelMainInventory::resetFilters, this));
//mCommitCallbackRegistrar.add("Inventory.SetSortBy", boost::bind(&LLPanelMainInventory::setSortBy, this, _2)); // <FS:Zi> Sort By menu handlers
mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this));
mCommitCallbackRegistrar.add("Inventory.Purge", boost::bind(&LLAvatarActions::purgeSelectedItems));
// <FS:Zi> Filter Links Menu
mCommitCallbackRegistrar.add("Inventory.FilterLinks.Set", boost::bind(&LLPanelMainInventory::onFilterLinksChecked, this, _2));

View File

@ -57,10 +57,14 @@
<name_list.columns
label="Name"
name="name"
relative_width="0.65" />
relative_width="0.5" />
<name_list.columns
label="Render setting"
name="setting"
relative_width="0.35" />
relative_width="0.25" />
<name_list.columns
label="Date added"
name="timestamp"
relative_width="0.25" />
</name_list>
</floater>

View File

@ -526,7 +526,8 @@
layout="topleft"
name="Purge Item">
<menu_item_call.on_click
function="Inventory.Purge"/>
function="Inventory.DoToSelected"
parameter="purge"/>
</menu_item_call>
<menu_item_call
label="Restore Item"

View File

@ -9267,7 +9267,7 @@ Select residents to share with.
name="MeshUploadErrorDetails"
icon="alert.tga"
type="alert">
[LABEL] failed to upload: [MESSAGE] [IDENTIFIER]
[LABEL] failed to upload: [MESSAGE]
[DETAILS] See Firestorm.log for details
</notification>
@ -9276,7 +9276,6 @@ Select residents to share with.
icon="alert.tga"
type="alert">
[LABEL] failed to upload: [MESSAGE]
[IDENTIFIER]
See Firestorm.log for details
</notification>