Merge with viewer-chui

master
Merov Linden 2013-01-04 20:28:01 -08:00
commit fbc414c02b
98 changed files with 2255 additions and 1350 deletions

View File

@ -71,7 +71,6 @@ set(llui_SOURCE_FILES
llmultislider.cpp
llmultisliderctrl.cpp
llnotifications.cpp
llnotificationslistener.cpp
llnotificationsutil.cpp
llpanel.cpp
llprogressbar.cpp
@ -181,7 +180,6 @@ set(llui_HEADER_FILES
llmultislider.h
llnotificationptr.h
llnotifications.h
llnotificationslistener.h
llnotificationsutil.h
llnotificationtemplate.h
llnotificationvisibilityrule.h

View File

@ -613,30 +613,6 @@ void LLButton::draw()
static LLCachedControl<bool> sEnableButtonFlashing(*LLUI::sSettingGroups["config"], "EnableButtonFlashing", true);
F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
bool flash = false;
if (mFlashingTimer)
{
mFlashing = mFlashingTimer->isFlashingInProgress();
flash = mFlashing && (!sEnableButtonFlashing || mFlashingTimer->isCurrentlyHighlighted());
}
else
{
if(mFlashing)
{
if ( sEnableButtonFlashing)
{
F32 elapsed = mFrameTimer.getElapsedTimeF32();
S32 flash_count = S32(elapsed * mButtonFlashRate * 2.f);
// flash on or off?
flash = (flash_count % 2 == 0) || flash_count > S32((F32)mButtonFlashCount * 2.f);
}
else
{ // otherwise just highlight button in flash color
flash = true;
}
}
}
bool pressed_by_keyboard = FALSE;
if (hasFocus())
{
@ -660,9 +636,21 @@ void LLButton::draw()
bool selected = getToggleState();
bool use_glow_effect = FALSE;
LLColor4 glow_color = LLColor4::white;
LLColor4 highlighting_color = LLColor4::white;
LLColor4 glow_color;
LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
LLUIImage* imagep = NULL;
// Cancel sticking of color, if the button is pressed,
// or when a flashing of the previously selected button is ended
if (mFlashingTimer
&& ((selected && !mFlashingTimer->isFlashingInProgress()) || pressed))
{
mFlashing = false;
}
bool flash = mFlashing && sEnableButtonFlashing;
if (pressed && mDisplayPressedState)
{
imagep = selected ? mImagePressedSelected : mImagePressed;
@ -728,15 +716,20 @@ void LLButton::draw()
imagep = mImageFlash;
}
// else use usual flashing via flash_color
else
else if (mFlashingTimer)
{
LLColor4 flash_color = mFlashBgColor.get();
use_glow_effect = TRUE;
glow_type = LLRender::BT_ALPHA; // blend the glow
if (mNeedsHighlight) // highlighted AND flashing
glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity
else
if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress())
{
glow_color = flash_color;
}
else if (mNeedsHighlight)
{
glow_color = highlighting_color;
}
}
}
@ -785,8 +778,7 @@ void LLButton::draw()
if (use_glow_effect)
{
mCurGlowStrength = lerp(mCurGlowStrength,
mFlashing ? (flash? 1.0 : 0.0)
: mHoverGlowStrength,
mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.0 : 0.0) : mHoverGlowStrength,
LLCriticalDamp::getInterpolant(0.05f));
}
else
@ -973,23 +965,18 @@ void LLButton::setToggleState(BOOL b)
{
setControlValue(b); // will fire LLControlVariable callbacks (if any)
setValue(b); // may or may not be redundant
setFlashing(false); // stop flash state whenever the selected/unselected state if reset
// Unselected label assignments
autoResize();
}
}
void LLButton::setFlashing( bool b )
void LLButton::setFlashing(bool b)
{
if (mFlashingTimer)
{
if (b)
{
mFlashingTimer->startFlashing();
}
else
{
mFlashingTimer->stopFlashing();
}
mFlashing = b;
(b ? mFlashingTimer->startFlashing() : mFlashingTimer->stopFlashing());
}
else if (b != mFlashing)
{

View File

@ -1913,14 +1913,15 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu)
// Successively filter out invalid options
U32 flags = FIRST_SELECTED_ITEM;
U32 multi_select_flag = (mSelectedItems.size() > 1 ? ITEM_IN_MULTI_SELECTION : 0x0);
U32 flags = multi_select_flag | FIRST_SELECTED_ITEM;
for (selected_items_t::iterator item_itor = mSelectedItems.begin();
item_itor != mSelectedItems.end();
++item_itor)
{
LLFolderViewItem* selected_item = (*item_itor);
selected_item->buildContextMenu(*menu, flags);
flags = 0x0;
flags = multi_select_flag;
}
addNoOptions(menu);

View File

@ -400,5 +400,6 @@ public:
// Flags for buildContextMenu()
const U32 SUPPRESS_OPEN_ITEM = 0x1;
const U32 FIRST_SELECTED_ITEM = 0x2;
const U32 ITEM_IN_MULTI_SELECTION = 0x4;
#endif // LL_LLFOLDERVIEW_H

View File

@ -50,6 +50,7 @@ std::map<U8, LLFontGL*> LLFolderViewItem::sFonts; // map of styles to fonts
bool LLFolderViewItem::sColorSetInitialized = false;
LLUIColor LLFolderViewItem::sFgColor;
LLUIColor LLFolderViewItem::sHighlightBgColor;
LLUIColor LLFolderViewItem::sFlashBgColor;
LLUIColor LLFolderViewItem::sFocusOutlineColor;
LLUIColor LLFolderViewItem::sMouseOverColor;
LLUIColor LLFolderViewItem::sFilterBGColor;
@ -151,6 +152,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
{
sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE);
sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE);
sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);
@ -686,26 +688,31 @@ void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const L
return mIsCurSelection;
}
void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &bgColor,
void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor,
const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor)
{
//--------------------------------------------------------------------------------//
// Draw highlight for selected items
//
const S32 focus_top = getRect().getHeight();
const S32 focus_bottom = getRect().getHeight() - mItemHeight;
const bool folder_open = (getRect().getHeight() > mItemHeight + 4);
const S32 FOCUS_LEFT = 1;
// Determine which background color to use for highlighting
LLUIColor bgColor = (isFlashing() ? flashColor : selectColor);
if (isHighlightAllowed()) // always render "current" item (only render other selected items if
// mShowSingleSelection is FALSE) or flashing item
//--------------------------------------------------------------------------------//
// Draw highlight for selected items
// Note: Always render "current" item or flashing item, only render other selected
// items if mShowSingleSelection is FALSE.
//
if (isHighlightAllowed())
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
LLColor4 bg_color = bgColor;
if (!isHighlightActive())
// Highlight for selected but not current items
if (!isHighlightActive() && !isFlashing())
{
LLColor4 bg_color = bgColor;
// do time-based fade of extra objects
F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f);
if (getRoot() && getRoot()->getShowSingleSelection())
@ -718,25 +725,30 @@ void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeybo
// fading in
bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
}
gl_rect_2d(FOCUS_LEFT,
focus_top,
getRect().getWidth() - 2,
focus_bottom,
bg_color, hasKeyboardFocus);
}
if (isHighlightAllowed() || isHighlightActive())
// Highlight for currently selected or flashing item
if (isHighlightActive())
{
// Background
gl_rect_2d(FOCUS_LEFT,
focus_top,
getRect().getWidth() - 2,
focus_bottom,
bg_color, hasKeyboardFocus);
}
if (isHighlightActive())
{
bgColor, hasKeyboardFocus);
// Outline
gl_rect_2d(FOCUS_LEFT,
focus_top,
getRect().getWidth() - 2,
focus_bottom,
focusOutlineColor, FALSE);
}
if (folder_open)
{
gl_rect_2d(FOCUS_LEFT,
@ -810,7 +822,7 @@ void LLFolderViewItem::draw()
drawOpenFolderArrow(default_params, sFgColor);
drawHighlight(show_context, filled, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor);
drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
//--------------------------------------------------------------------------------//
// Draw open icon
@ -1481,17 +1493,20 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection)
void LLFolderViewFolder::destroyView()
{
std::for_each(mItems.begin(), mItems.end(), DeletePointer());
mItems.clear();
while (!mItems.empty())
{
LLFolderViewItem *itemp = mItems.back();
itemp->destroyView(); // LLFolderViewItem::destroyView() removes entry from mItems
}
while (!mFolders.empty())
{
LLFolderViewFolder *folderp = mFolders.back();
folderp->destroyView(); // removes entry from mFolders
folderp->destroyView(); // LLFolderVievFolder::destroyView() removes entry from mFolders
}
LLFolderViewItem::destroyView();
}
}
// extractItem() removes the specified item from the folder, but
// doesn't delete it.

View File

@ -128,6 +128,7 @@ protected:
static LLUIColor sFgColor;
static LLUIColor sFgDisabledColor;
static LLUIColor sHighlightBgColor;
static LLUIColor sFlashBgColor;
static LLUIColor sFocusOutlineColor;
static LLUIColor sMouseOverColor;
static LLUIColor sFilterBGColor;
@ -141,6 +142,8 @@ protected:
virtual void addFolder(LLFolderViewFolder*) { }
virtual bool isHighlightAllowed();
virtual bool isHighlightActive();
virtual bool isFlashing() { return false; }
virtual void setFlashState(bool) { }
static LLFontGL* getLabelFontForStyle(U8 style);
@ -269,7 +272,7 @@ public:
// virtual void handleDropped();
virtual void draw();
void drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color);
void drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &bgColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor);
void drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor);
void drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x);
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,

View File

@ -32,7 +32,6 @@
#include "lllocalcliprect.h"
#include "llpanel.h"
#include "llresizebar.h"
#include "llcriticaldamp.h"
#include "boost/foreach.hpp"
@ -796,6 +795,11 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
}
else
{
if (new_auto_resize_headroom < 1.f)
{
new_auto_resize_headroom = 1.f;
}
F32 new_fractional_size = llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom)
/ new_auto_resize_headroom,
MIN_FRACTIONAL_SIZE,

View File

@ -29,6 +29,7 @@
#define LL_LLLAYOUTSTACK_H
#include "llpanel.h"
#include "llresizebar.h"
class LLLayoutPanel;
@ -178,6 +179,7 @@ public:
F32 getAutoResizeFactor() const;
F32 getVisibleAmount() const;
S32 getVisibleDim() const;
LLResizeBar* getResizeBar() { return mResizeBar; }
bool isCollapsed() const { return mCollapsed;}

View File

@ -41,8 +41,8 @@ LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params)
mTabContainer(NULL),
mTabPos(LLTabContainer::TOP),
mAutoResize(TRUE),
mOrigMinWidth(0),
mOrigMinHeight(0)
mOrigMinWidth(params.min_width),
mOrigMinHeight(params.min_height)
{
}

View File

@ -39,7 +39,6 @@
#include "lldir.h"
#include "llsdserialize.h"
#include "lltrans.h"
#include "llnotificationslistener.h"
#include "llstring.h"
#include "llsdparam.h"
#include "llsdutil.h"
@ -993,10 +992,12 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
bool abortProcessing = false;
if (passesFilter)
{
onFilterPass(pNotification);
abortProcessing = mPassedFilter(payload);
}
else
{
onFilterFail(pNotification);
abortProcessing = mFailedFilter(payload);
}
@ -1168,8 +1169,6 @@ LLNotifications::LLNotifications()
mIgnoreAllNotifications(false)
{
LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
mListener.reset(new LLNotificationsListener(*this));
}

View File

@ -92,7 +92,6 @@
#include "llevents.h"
#include "llfunctorregistry.h"
#include "llinitparam.h"
#include "llnotificationslistener.h"
#include "llnotificationptr.h"
#include "llpointer.h"
#include "llrefcount.h"
@ -681,7 +680,6 @@ namespace LLNotificationComparators
};
typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter;
typedef boost::function<bool (LLNotificationPtr, LLNotificationPtr)> LLNotificationComparator;
typedef std::set<LLNotificationPtr, LLNotificationComparators::orderByUUID> LLNotificationSet;
typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap;
@ -776,6 +774,9 @@ protected:
virtual void onDelete(LLNotificationPtr p) {}
virtual void onChange(LLNotificationPtr p) {}
virtual void onFilterPass(LLNotificationPtr p) {}
virtual void onFilterFail(LLNotificationPtr p) {}
bool updateItem(const LLSD& payload, LLNotificationPtr pNotification);
LLNotificationFilter mFilter;
};
@ -831,7 +832,6 @@ public:
private:
std::string mName;
std::string mParent;
LLNotificationComparator mComparator;
};
// An interface class to provide a clean linker seam to the LLNotifications class.
@ -951,7 +951,6 @@ private:
bool mIgnoreAllNotifications;
boost::scoped_ptr<LLNotificationsListener> mListener;
std::vector<LLNotificationChannelPtr> mDefaultChannels;
};
@ -1030,7 +1029,7 @@ protected:
// Stores only persistent notifications.
// Class users can use connectChanged() to process persistent notifications
// (see LLNotificationStorage for example).
// (see LLPersistentNotificationStorage for example).
class LLPersistentNotificationChannel : public LLNotificationChannel
{
LOG_CLASS(LLPersistentNotificationChannel);

View File

@ -1,354 +0,0 @@
/**
* @file llnotificationslistener.cpp
* @author Brad Kittenbrink
* @date 2009-07-08
* @brief Implementation for llnotificationslistener.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llnotificationslistener.h"
#include "llnotifications.h"
#include "llnotificationtemplate.h"
#include "llsd.h"
#include "llui.h"
LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) :
LLEventAPI("LLNotifications",
"LLNotifications listener to (e.g.) pop up a notification"),
mNotifications(notifications)
{
add("requestAdd",
"Add a notification with specified [\"name\"], [\"substitutions\"] and [\"payload\"].\n"
"If optional [\"reply\"] specified, arrange to send user response on that LLEventPump.",
&LLNotificationsListener::requestAdd);
add("listChannels",
"Post to [\"reply\"] a map of info on existing channels",
&LLNotificationsListener::listChannels,
LLSD().with("reply", LLSD()));
add("listChannelNotifications",
"Post to [\"reply\"] an array of info on notifications in channel [\"channel\"]",
&LLNotificationsListener::listChannelNotifications,
LLSD().with("reply", LLSD()).with("channel", LLSD()));
add("respond",
"Respond to notification [\"uuid\"] with data in [\"response\"]",
&LLNotificationsListener::respond,
LLSD().with("uuid", LLSD()));
add("cancel",
"Cancel notification [\"uuid\"]",
&LLNotificationsListener::cancel,
LLSD().with("uuid", LLSD()));
add("ignore",
"Ignore future notification [\"name\"]\n"
"(from <notification name= > in notifications.xml)\n"
"according to boolean [\"ignore\"].\n"
"If [\"name\"] is omitted or undefined, [un]ignore all future notifications.\n"
"Note that ignored notifications are not forwarded unless intercepted before\n"
"the \"Ignore\" channel.",
&LLNotificationsListener::ignore);
add("forward",
"Forward to [\"pump\"] future notifications on channel [\"channel\"]\n"
"according to boolean [\"forward\"]. When enabled, only types matching\n"
"[\"types\"] are forwarded, as follows:\n"
"omitted or undefined: forward all notifications\n"
"string: forward only the specific named [sig]type\n"
"array of string: forward any notification matching any named [sig]type.\n"
"When boolean [\"respond\"] is true, we auto-respond to each forwarded\n"
"notification.",
&LLNotificationsListener::forward,
LLSD().with("channel", LLSD()));
}
// This is here in the .cpp file so we don't need the definition of class
// Forwarder in the header file.
LLNotificationsListener::~LLNotificationsListener()
{
}
void LLNotificationsListener::requestAdd(const LLSD& event_data) const
{
if(event_data.has("reply"))
{
mNotifications.add(event_data["name"],
event_data["substitutions"],
event_data["payload"],
boost::bind(&LLNotificationsListener::NotificationResponder,
this,
event_data["reply"].asString(),
_1, _2
)
);
}
else
{
mNotifications.add(event_data["name"],
event_data["substitutions"],
event_data["payload"]);
}
}
void LLNotificationsListener::NotificationResponder(const std::string& reply_pump,
const LLSD& notification,
const LLSD& response) const
{
LLSD reponse_event;
reponse_event["notification"] = notification;
reponse_event["response"] = response;
LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event);
}
void LLNotificationsListener::listChannels(const LLSD& params) const
{
LLReqID reqID(params);
LLSD response(reqID.makeResponse());
for (LLNotificationChannel::instance_iter cmi(LLNotificationChannel::beginInstances()),
cmend(LLNotificationChannel::endInstances());
cmi != cmend; ++cmi)
{
LLSD channelInfo;
//channelInfo["parent"] = cmi->second->getParentChannelName();
response[cmi->getName()] = channelInfo;
}
LLEventPumps::instance().obtain(params["reply"]).post(response);
}
void LLNotificationsListener::listChannelNotifications(const LLSD& params) const
{
LLReqID reqID(params);
LLSD response(reqID.makeResponse());
LLNotificationChannelPtr channel(mNotifications.getChannel(params["channel"]));
if (channel)
{
LLSD notifications(LLSD::emptyArray());
for (LLNotificationChannel::Iterator ni(channel->begin()), nend(channel->end());
ni != nend; ++ni)
{
notifications.append(asLLSD(*ni));
}
response["notifications"] = notifications;
}
LLEventPumps::instance().obtain(params["reply"]).post(response);
}
void LLNotificationsListener::respond(const LLSD& params) const
{
LLNotificationPtr notification(mNotifications.find(params["uuid"]));
if (notification)
{
notification->respond(params["response"]);
}
}
void LLNotificationsListener::cancel(const LLSD& params) const
{
LLNotificationPtr notification(mNotifications.find(params["uuid"]));
if (notification)
{
mNotifications.cancel(notification);
}
}
void LLNotificationsListener::ignore(const LLSD& params) const
{
// Calling a method named "ignore", but omitting its "ignore" Boolean
// argument, should by default cause something to be ignored. Explicitly
// pass ["ignore"] = false to cancel ignore.
bool ignore = true;
if (params.has("ignore"))
{
ignore = params["ignore"].asBoolean();
}
// This method can be used to affect either a single notification name or
// all future notifications. The two use substantially different mechanisms.
if (params["name"].isDefined())
{
// ["name"] was passed: ignore just that notification
LLNotificationTemplatePtr templatep = mNotifications.getTemplate(params["name"]);
if (templatep)
{
templatep->mForm->setIgnored(ignore);
}
}
else
{
// no ["name"]: ignore all future notifications
mNotifications.setIgnoreAllNotifications(ignore);
}
}
class LLNotificationsListener::Forwarder: public LLEventTrackable
{
LOG_CLASS(LLNotificationsListener::Forwarder);
public:
Forwarder(LLNotifications& llnotifications, const std::string& channel):
mNotifications(llnotifications),
mRespond(false)
{
// Connect to the specified channel on construction. Because
// LLEventTrackable is a base, we should automatically disconnect when
// destroyed.
LLNotificationChannelPtr channelptr(llnotifications.getChannel(channel));
if (channelptr)
{
// Insert our processing as a "passed filter" listener. This way
// we get to run before all the "changed" listeners, and we get to
// swipe it (hide it from the other listeners) if desired.
channelptr->connectPassedFilter(boost::bind(&Forwarder::handle, this, _1));
}
}
void setPumpName(const std::string& name) { mPumpName = name; }
void setTypes(const LLSD& types) { mTypes = types; }
void setRespond(bool respond) { mRespond = respond; }
private:
bool handle(const LLSD& notification) const;
bool matchType(const LLSD& filter, const std::string& type) const;
LLNotifications& mNotifications;
std::string mPumpName;
LLSD mTypes;
bool mRespond;
};
void LLNotificationsListener::forward(const LLSD& params)
{
std::string channel(params["channel"]);
// First decide whether we're supposed to start forwarding or stop it.
// Default to true.
bool forward = true;
if (params.has("forward"))
{
forward = params["forward"].asBoolean();
}
if (! forward)
{
// This is a request to stop forwarding notifications on the specified
// channel. The rest of the params don't matter.
// Because mForwarders contains scoped_ptrs, erasing the map entry
// DOES delete the heap Forwarder object. Because Forwarder derives
// from LLEventTrackable, destroying it disconnects it from the
// channel.
mForwarders.erase(channel);
return;
}
// From here on, we know we're being asked to start (or modify) forwarding
// on the specified channel. Find or create an appropriate Forwarder.
ForwarderMap::iterator
entry(mForwarders.insert(ForwarderMap::value_type(channel, ForwarderMap::mapped_type())).first);
if (! entry->second)
{
entry->second.reset(new Forwarder(mNotifications, channel));
}
// Now, whether this Forwarder is brand-new or not, update it with the new
// request info.
Forwarder& fwd(*entry->second);
fwd.setPumpName(params["pump"]);
fwd.setTypes(params["types"]);
fwd.setRespond(params["respond"]);
}
bool LLNotificationsListener::Forwarder::handle(const LLSD& notification) const
{
LL_INFOS("LLNotificationsListener") << "handle(" << notification << ")" << LL_ENDL;
if (notification["sigtype"].asString() == "delete")
{
LL_INFOS("LLNotificationsListener") << "ignoring delete" << LL_ENDL;
// let other listeners see the "delete" operation
return false;
}
LLNotificationPtr note(mNotifications.find(notification["id"]));
if (! note)
{
LL_INFOS("LLNotificationsListener") << notification["id"] << " not found" << LL_ENDL;
return false;
}
if (! matchType(mTypes, note->getType()))
{
LL_INFOS("LLNotificationsListener") << "didn't match types " << mTypes << LL_ENDL;
// We're not supposed to intercept this particular notification. Let
// other listeners process it.
return false;
}
LL_INFOS("LLNotificationsListener") << "sending via '" << mPumpName << "'" << LL_ENDL;
// This is a notification we care about. Forward it through specified
// LLEventPump.
LLEventPumps::instance().obtain(mPumpName).post(asLLSD(note));
// Are we also being asked to auto-respond?
if (mRespond)
{
LL_INFOS("LLNotificationsListener") << "should respond" << LL_ENDL;
note->respond(LLSD::emptyMap());
// Did that succeed in removing the notification? Only cancel() if
// it's still around -- otherwise we get an LL_ERRS crash!
note = mNotifications.find(notification["id"]);
if (note)
{
LL_INFOS("LLNotificationsListener") << "respond() didn't clear, canceling" << LL_ENDL;
mNotifications.cancel(note);
}
}
// If we've auto-responded to this notification, then it's going to be
// deleted. Other listeners would get the change operation, try to look it
// up and be baffled by lookup failure. So when we auto-respond, suppress
// this notification: don't pass it to other listeners.
return mRespond;
}
bool LLNotificationsListener::Forwarder::matchType(const LLSD& filter, const std::string& type) const
{
// Decide whether this notification matches filter:
// undefined: forward all notifications
if (filter.isUndefined())
{
return true;
}
// array of string: forward any notification matching any named type
if (filter.isArray())
{
for (LLSD::array_const_iterator ti(filter.beginArray()), tend(filter.endArray());
ti != tend; ++ti)
{
if (ti->asString() == type)
{
return true;
}
}
// Didn't match any entry in the array
return false;
}
// string: forward only the specific named type
return (filter.asString() == type);
}
LLSD LLNotificationsListener::asLLSD(LLNotificationPtr note)
{
LLSD notificationInfo(note->asLLSD());
// For some reason the following aren't included in LLNotification::asLLSD().
notificationInfo["summary"] = note->summarize();
notificationInfo["id"] = note->id();
notificationInfo["type"] = note->getType();
notificationInfo["message"] = note->getMessage();
notificationInfo["label"] = note->getLabel();
return notificationInfo;
}

View File

@ -1,69 +0,0 @@
/**
* @file llnotificationslistener.h
* @author Brad Kittenbrink
* @date 2009-07-08
* @brief Wrap subset of LLNotifications API in event API for test scripts.
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLNOTIFICATIONSLISTENER_H
#define LL_LLNOTIFICATIONSLISTENER_H
#include "lleventapi.h"
#include "llnotificationptr.h"
#include <boost/shared_ptr.hpp>
#include <map>
#include <string>
class LLNotifications;
class LLSD;
class LLNotificationsListener : public LLEventAPI
{
public:
LLNotificationsListener(LLNotifications & notifications);
~LLNotificationsListener();
private:
void requestAdd(LLSD const & event_data) const;
void NotificationResponder(const std::string& replypump,
const LLSD& notification,
const LLSD& response) const;
void listChannels(const LLSD& params) const;
void listChannelNotifications(const LLSD& params) const;
void respond(const LLSD& params) const;
void cancel(const LLSD& params) const;
void ignore(const LLSD& params) const;
void forward(const LLSD& params);
static LLSD asLLSD(LLNotificationPtr);
class Forwarder;
typedef std::map<std::string, boost::shared_ptr<Forwarder> > ForwarderMap;
ForwarderMap mForwarders;
LLNotifications & mNotifications;
};
#endif // LL_LLNOTIFICATIONSLISTENER_H

View File

@ -49,7 +49,6 @@
//#include "llfunctorregistry.h"
//#include "llpointer.h"
#include "llinitparam.h"
//#include "llnotificationslistener.h"
//#include "llnotificationptr.h"
//#include "llcachename.h"
#include "llnotifications.h"

View File

@ -45,7 +45,8 @@ LLResizeBar::LLResizeBar(const LLResizeBar::Params& p)
mSide( p.side ),
mSnappingEnabled(p.snapping_enabled),
mAllowDoubleClickSnapping(p.allow_double_click_snapping),
mResizingView(p.resizing_view)
mResizingView(p.resizing_view),
mResizeListener(NULL)
{
setFollowsNone();
// set up some generically good follow code.
@ -261,6 +262,11 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
}
}
if (mResizeListener)
{
mResizeListener(NULL);
}
return handled;
} // end LLResizeBar::handleHover

View File

@ -71,6 +71,7 @@ public:
void setEnableSnapping(BOOL enable) { mSnappingEnabled = enable; }
void setAllowDoubleClickSnapping(BOOL allow) { mAllowDoubleClickSnapping = allow; }
bool canResize() { return getEnabled() && mMaxSize > mMinSize; }
void setResizeListener(boost::function<void(void*)> listener) {mResizeListener = listener;}
private:
S32 mDragLastScreenX;
@ -84,6 +85,7 @@ private:
BOOL mSnappingEnabled;
BOOL mAllowDoubleClickSnapping;
LLView* mResizingView;
boost::function<void(void*)> mResizeListener;
};
#endif // LL_RESIZEBAR_H

View File

@ -55,6 +55,8 @@
#include "lltexteditor.h"
#include "lltextbox.h"
static const S32 LINE_HEIGHT = 15;
S32 LLView::sDepth = 0;
bool LLView::sDebugRects = false;
bool LLView::sDebugRectsShowNames = true;
@ -1203,11 +1205,24 @@ void LLView::drawDebugRect()
&& preview_iter == sPreviewHighlightedElements.end()
&& sDebugRectsShowNames)
{
//char temp[256];
S32 x, y;
gGL.color4fv( border_color.mV );
x = debug_rect.getWidth()/2;
y = debug_rect.getHeight()/2;
x = debug_rect.getWidth() / 2;
S32 rect_height = debug_rect.getHeight();
S32 lines = rect_height / LINE_HEIGHT + 1;
S32 depth = 0;
LLView * viewp = this;
while (NULL != viewp)
{
viewp = viewp->getParent();
depth++;
}
y = rect_height - LINE_HEIGHT * (depth % lines + 1);
std::string debug_text = llformat("%s (%d x %d)", getName().c_str(),
debug_rect.getWidth(), debug_rect.getHeight());
LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,

View File

@ -137,6 +137,7 @@ set(viewer_SOURCE_FILES
llcommanddispatcherlistener.cpp
llcommandhandler.cpp
llcommandlineparser.cpp
llcommunicationchannel.cpp
llcompilequeue.cpp
llconfirmationmanager.cpp
llconversationlog.cpp
@ -152,6 +153,7 @@ set(viewer_SOURCE_FILES
lldebugview.cpp
lldelayedgestureerror.cpp
lldirpicker.cpp
lldonotdisturbnotificationstorage.cpp
lldndbutton.cpp
lldrawable.cpp
lldrawpool.cpp
@ -448,6 +450,7 @@ set(viewer_SOURCE_FILES
llpathfindingobject.cpp
llpathfindingobjectlist.cpp
llpathfindingpathtool.cpp
llpersistentnotificationstorage.cpp
llphysicsmotion.cpp
llphysicsshapebuilderutil.cpp
llplacesinventorybridge.cpp
@ -722,6 +725,7 @@ set(viewer_HEADER_FILES
llcommanddispatcherlistener.h
llcommandhandler.h
llcommandlineparser.h
llcommunicationchannel.h
llcompilequeue.h
llconfirmationmanager.h
llconversationlog.h
@ -737,6 +741,7 @@ set(viewer_HEADER_FILES
lldebugview.h
lldelayedgestureerror.h
lldirpicker.h
lldonotdisturbnotificationstorage.h
lldndbutton.h
lldrawable.h
lldrawpool.h
@ -1021,6 +1026,7 @@ set(viewer_HEADER_FILES
llpathfindingobject.h
llpathfindingobjectlist.h
llpathfindingpathtool.h
llpersistentnotificationstorage.h
llphysicsmotion.h
llphysicsshapebuilderutil.h
llplacesinventorybridge.h

View File

@ -4676,7 +4676,7 @@
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
<integer>1</integer>
</map>
<key>LandBrushSize</key>
<map>
@ -13173,7 +13173,7 @@
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>0.25</real>
<real>0.5</real>
</map>
<key>WindLightUseAtmosShaders</key>
<map>

View File

@ -53,7 +53,7 @@
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>268</integer>
<integer>205</integer>
</map>
<key>ConversationsMessagePaneCollapsed</key>
<map>

View File

@ -41,6 +41,7 @@
#include "llchannelmanager.h"
#include "llchicletbar.h"
#include "llconsole.h"
#include "lldonotdisturbnotificationstorage.h"
#include "llenvmanager.h"
#include "llfirstuse.h"
#include "llfloatercamera.h"
@ -1389,11 +1390,16 @@ BOOL LLAgent::getAFK() const
//-----------------------------------------------------------------------------
// setDoNotDisturb()
//-----------------------------------------------------------------------------
void LLAgent::setDoNotDisturb(bool pIsDotNotDisturb)
void LLAgent::setDoNotDisturb(bool pIsDoNotDisturb)
{
mIsDoNotDisturb = pIsDotNotDisturb;
sendAnimationRequest(ANIM_AGENT_DO_NOT_DISTURB, (pIsDotNotDisturb ? ANIM_REQUEST_START : ANIM_REQUEST_STOP));
LLNotificationsUI::LLChannelManager::getInstance()->muteAllChannels(pIsDotNotDisturb);
bool isDoNotDisturbSwitchedOff = (mIsDoNotDisturb && !pIsDoNotDisturb);
mIsDoNotDisturb = pIsDoNotDisturb;
sendAnimationRequest(ANIM_AGENT_DO_NOT_DISTURB, (pIsDoNotDisturb ? ANIM_REQUEST_START : ANIM_REQUEST_STOP));
LLNotificationsUI::LLChannelManager::getInstance()->muteAllChannels(pIsDoNotDisturb);
if (isDoNotDisturbSwitchedOff)
{
LLDoNotDisturbNotificationStorage::getInstance()->loadNotifications();
}
}
//-----------------------------------------------------------------------------
@ -2541,51 +2547,21 @@ void LLMaturityPreferencesResponder::error(U32 pStatus, const std::string& pReas
U8 LLMaturityPreferencesResponder::parseMaturityFromServerResponse(const LLSD &pContent)
{
// stinson 05/24/2012 Pathfinding regions have re-defined the response behavior. In the old server code,
// if you attempted to change the preferred maturity to the same value, the response content would be an
// undefined LLSD block. In the new server code with pathfinding, the response content should always be
// defined. Thus, the check for isUndefined() can be replaced with an assert after pathfinding is merged
// into server trunk and fully deployed.
U8 maturity = SIM_ACCESS_MIN;
if (pContent.isUndefined())
llassert(!pContent.isUndefined());
llassert(pContent.isMap());
llassert(pContent.has("access_prefs"));
llassert(pContent.get("access_prefs").isMap());
llassert(pContent.get("access_prefs").has("max"));
llassert(pContent.get("access_prefs").get("max").isString());
if (!pContent.isUndefined() && pContent.isMap() && pContent.has("access_prefs")
&& pContent.get("access_prefs").isMap() && pContent.get("access_prefs").has("max")
&& pContent.get("access_prefs").get("max").isString())
{
maturity = mPreferredMaturity;
}
else
{
llassert(!pContent.isUndefined());
llassert(pContent.isMap());
if (!pContent.isUndefined() && pContent.isMap())
{
// stinson 05/24/2012 Pathfinding regions have re-defined the response syntax. The if statement catches
// the new syntax, and the else statement catches the old syntax. After pathfinding is merged into
// server trunk and fully deployed, we can remove the else statement.
if (pContent.has("access_prefs"))
{
llassert(pContent.has("access_prefs"));
llassert(pContent.get("access_prefs").isMap());
llassert(pContent.get("access_prefs").has("max"));
llassert(pContent.get("access_prefs").get("max").isString());
if (pContent.get("access_prefs").isMap() && pContent.get("access_prefs").has("max") &&
pContent.get("access_prefs").get("max").isString())
{
LLSD::String actualPreference = pContent.get("access_prefs").get("max").asString();
LLStringUtil::trim(actualPreference);
maturity = LLViewerRegion::shortStringToAccess(actualPreference);
}
}
else if (pContent.has("max"))
{
llassert(pContent.get("max").isString());
if (pContent.get("max").isString())
{
LLSD::String actualPreference = pContent.get("max").asString();
LLStringUtil::trim(actualPreference);
maturity = LLViewerRegion::shortStringToAccess(actualPreference);
}
}
}
LLSD::String actualPreference = pContent.get("access_prefs").get("max").asString();
LLStringUtil::trim(actualPreference);
maturity = LLViewerRegion::shortStringToAccess(actualPreference);
}
return maturity;

View File

@ -35,6 +35,11 @@
using namespace LLNotificationsUI;
LLBrowserNotification::LLBrowserNotification()
: LLSystemNotificationHandler("Browser", "browser")
{
}
bool LLBrowserNotification::processNotification(const LLNotificationPtr& notification)
{
LLUUID media_id = notification->getPayload()["media_id"].asUUID();

View File

@ -29,7 +29,8 @@
#include "llchannelmanager.h"
#include "llappviewer.h"
#include "llnotificationstorage.h"
#include "lldonotdisturbnotificationstorage.h"
#include "llpersistentnotificationstorage.h"
#include "llviewercontrol.h"
#include "llviewerwindow.h"
#include "llrootview.h"
@ -138,6 +139,9 @@ void LLChannelManager::onLoginCompleted()
}
LLPersistentNotificationStorage::getInstance()->loadNotifications();
LLDoNotDisturbNotificationStorage::getInstance()->initialize();
LLDoNotDisturbNotificationStorage::getInstance()->loadNotifications();
}
//--------------------------------------------------------------------------

View File

@ -669,47 +669,3 @@ void LLChatBar::onCommitGesture(LLUICtrl* ctrl)
mGestureCombo->setFocus(FALSE);
}
}
/* Cruft - global gChatHandler declared below has been commented out,
so this class is never used. See similar code in llfloaterimnearbychatbar.cpp
class LLChatHandler : public LLCommandHandler
{
public:
// not allowed from outside the app
LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
// Your code here
bool handle(const LLSD& tokens, const LLSD& query_map,
LLMediaCtrl* web)
{
bool retval = false;
// Need at least 2 tokens to have a valid message.
if (tokens.size() < 2)
{
retval = false;
}
else
{
S32 channel = tokens[0].asInteger();
// VWR-19499 Restrict function to chat channels greater than 0.
if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG))
{
retval = true;
// Say mesg on channel
std::string mesg = tokens[1].asString();
send_chat_from_viewer(mesg, CHAT_TYPE_NORMAL, channel);
}
else
{
retval = false;
// Tell us this is an unsupported SLurl.
}
}
return retval;
}
};
// Creating the object registers with the dispatcher.
//LLChatHandler gChatHandler;
cruft */

View File

@ -27,15 +27,17 @@
#include "llviewerprecompiledheaders.h" // must be first include
#include "llchiclet.h"
#include "llchicletbar.h"
#include "llfloaterimsession.h"
#include "llfloaterimcontainer.h"
#include "llfloaterreg.h"
#include "lllocalcliprect.h"
#include "llnotifications.h"
#include "llscriptfloater.h"
#include "llsingleton.h"
#include "llsyswellwindow.h"
static LLDefaultChildRegistry::Register<LLChicletPanel> t1("chiclet_panel");
static LLDefaultChildRegistry::Register<LLNotificationChiclet> t2("chiclet_notification");
static LLDefaultChildRegistry::Register<LLScriptChiclet> t6("chiclet_script");
static LLDefaultChildRegistry::Register<LLInvOfferChiclet> t7("chiclet_offer");
@ -46,6 +48,195 @@ boost::signals2::signal<LLChiclet* (const LLUUID&),
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
LLSysWellChiclet::Params::Params()
: button("button")
, unread_notifications("unread_notifications")
, max_displayed_count("max_displayed_count", 99)
{
button.name = "button";
button.tab_stop = FALSE;
button.label = LLStringUtil::null;
}
LLSysWellChiclet::LLSysWellChiclet(const Params& p)
: LLChiclet(p)
, mButton(NULL)
, mCounter(0)
, mMaxDisplayedCount(p.max_displayed_count)
, mIsNewMessagesState(false)
, mFlashToLitTimer(NULL)
, mContextMenu(NULL)
{
LLButton::Params button_params = p.button;
mButton = LLUICtrlFactory::create<LLButton>(button_params);
addChild(mButton);
mFlashToLitTimer = new LLFlashTimer(boost::bind(&LLSysWellChiclet::changeLitState, this, _1));
}
LLSysWellChiclet::~LLSysWellChiclet()
{
mFlashToLitTimer->unset();
}
void LLSysWellChiclet::setCounter(S32 counter)
{
// do nothing if the same counter is coming. EXT-3678.
if (counter == mCounter) return;
// note same code in LLChicletNotificationCounterCtrl::setCounter(S32 counter)
std::string s_count;
if(counter != 0)
{
static std::string more_messages_exist("+");
std::string more_messages(counter > mMaxDisplayedCount ? more_messages_exist : "");
s_count = llformat("%d%s"
, llmin(counter, mMaxDisplayedCount)
, more_messages.c_str()
);
}
mButton->setLabel(s_count);
mCounter = counter;
}
boost::signals2::connection LLSysWellChiclet::setClickCallback(
const commit_callback_t& cb)
{
return mButton->setClickedCallback(cb);
}
void LLSysWellChiclet::setToggleState(BOOL toggled) {
mButton->setToggleState(toggled);
}
void LLSysWellChiclet::changeLitState(bool blink)
{
setNewMessagesState(!mIsNewMessagesState);
}
void LLSysWellChiclet::setNewMessagesState(bool new_messages)
{
/*
Emulate 4 states of button by background images, see detains in EXT-3147
xml attribute Description
image_unselected "Unlit" - there are no new messages
image_selected "Unlit" + "Selected" - there are no new messages and the Well is open
image_pressed "Lit" - there are new messages
image_pressed_selected "Lit" + "Selected" - there are new messages and the Well is open
*/
mButton->setForcePressedState(new_messages);
mIsNewMessagesState = new_messages;
}
void LLSysWellChiclet::updateWidget(bool is_window_empty)
{
mButton->setEnabled(!is_window_empty);
if (LLChicletBar::instanceExists())
{
LLChicletBar::getInstance()->showWellButton(getName(), !is_window_empty);
}
}
// virtual
BOOL LLSysWellChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
if(!mContextMenu)
{
createMenu();
}
if (mContextMenu)
{
mContextMenu->show(x, y);
LLMenuGL::showPopup(this, mContextMenu, x, y);
}
return TRUE;
}
/************************************************************************/
/* LLNotificationChiclet implementation */
/************************************************************************/
LLNotificationChiclet::LLNotificationChiclet(const Params& p)
: LLSysWellChiclet(p),
mUreadSystemNotifications(0)
{
mNotificationChannel.reset(new ChicletNotificationChannel(this));
// ensure that notification well window exists, to synchronously
// handle toast add/delete events.
LLNotificationWellWindow::getInstance()->setSysWellChiclet(this);
}
void LLNotificationChiclet::onMenuItemClicked(const LLSD& user_data)
{
std::string action = user_data.asString();
if("close all" == action)
{
LLNotificationWellWindow::getInstance()->closeAll();
}
}
bool LLNotificationChiclet::enableMenuItem(const LLSD& user_data)
{
std::string item = user_data.asString();
if (item == "can close all")
{
return mUreadSystemNotifications != 0;
}
return true;
}
void LLNotificationChiclet::createMenu()
{
if(mContextMenu)
{
llwarns << "Menu already exists" << llendl;
return;
}
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
registrar.add("NotificationWellChicletMenu.Action",
boost::bind(&LLNotificationChiclet::onMenuItemClicked, this, _2));
LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
enable_registrar.add("NotificationWellChicletMenu.EnableItem",
boost::bind(&LLNotificationChiclet::enableMenuItem, this, _2));
mContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>
("menu_notification_well_button.xml",
LLMenuGL::sMenuContainer,
LLViewerMenuHolderGL::child_registry_t::instance());
}
/*virtual*/
void LLNotificationChiclet::setCounter(S32 counter)
{
LLSysWellChiclet::setCounter(counter);
updateWidget(getCounter() == 0);
}
bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification )
{
if (notification->getName() == "ScriptDialog")
{
return false;
}
if( !(notification->canLogToIM() && notification->hasFormElements())
&& (!notification->getPayload().has("give_inventory_notification")
|| notification->getPayload()["give_inventory_notification"]))
{
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
LLChiclet::Params::Params()
: show_counter("show_counter", true)
, enable_counter("enable_counter", false)
@ -57,12 +248,6 @@ LLChiclet::LLChiclet(const Params& p)
, mSessionId(LLUUID::null)
, mShowCounter(p.show_counter)
{
}
LLChiclet::~LLChiclet()
{
}
boost::signals2::connection LLChiclet::setLeftButtonClickCallback(
@ -125,6 +310,15 @@ BOOL LLIMChiclet::postBuild()
return TRUE;
}
void LLIMChiclet::enableCounterControl(bool enable)
{
mCounterEnabled = enable;
if(!enable)
{
LLChiclet::setShowCounter(false);
}
}
void LLIMChiclet::setRequiredWidth()
{
S32 required_width = mDefaultWidth;
@ -791,13 +985,19 @@ bool LLChicletPanel::isAnyIMFloaterDoked()
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
LLChicletNotificationCounterCtrl::Params::Params()
: max_displayed_count("max_displayed_count", 99)
{
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
LLChicletAvatarIconCtrl::LLChicletAvatarIconCtrl(const Params& p)
: LLAvatarIconCtrl(p)
{
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

View File

@ -29,10 +29,70 @@
#include "llavatariconctrl.h"
#include "llbutton.h"
#include "llnotifications.h"
#include "lltextbox.h"
class LLMenuGL;
class LLFloaterIMSession;
/**
* Class for displaying amount of messages/notifications(unread).
*/
class LLChicletNotificationCounterCtrl : public LLTextBox
{
public:
struct Params : public LLInitParam::Block<Params, LLTextBox::Params>
{
/**
* Contains maximum displayed count of unread messages. Default value is 9.
*
* If count is less than "max_unread_count" will be displayed as is.
* Otherwise 9+ will be shown (for default value).
*/
Optional<S32> max_displayed_count;
Params();
};
/**
* Sets number of notifications
*/
virtual void setCounter(S32 counter);
/**
* Returns number of notifications
*/
virtual S32 getCounter() const { return mCounter; }
/**
* Returns width, required to display amount of notifications in text form.
* Width is the only valid value.
*/
/*virtual*/ LLRect getRequiredRect();
/**
* Sets number of notifications using LLSD
*/
/*virtual*/ void setValue(const LLSD& value);
/**
* Returns number of notifications wrapped in LLSD
*/
/*virtual*/ LLSD getValue() const;
protected:
LLChicletNotificationCounterCtrl(const Params& p);
friend class LLUICtrlFactory;
private:
S32 mCounter;
S32 mInitialWidth;
S32 mMaxDisplayedCount;
};
/**
* Class for displaying avatar's icon in P2P chiclet.
*/
@ -104,7 +164,7 @@ public:
Params();
};
/*virtual*/ ~LLChiclet();
virtual ~LLChiclet() {}
/**
* Associates chat session id with chiclet.
@ -116,6 +176,11 @@ public:
*/
virtual const LLUUID& getSessionId() const { return mSessionId; }
/**
* Sets show counter state.
*/
virtual void setShowCounter(bool show) { mShowCounter = show; }
/**
* Connects chiclet clicked event with callback.
*/
@ -194,6 +259,22 @@ public:
*/
BOOL postBuild();
/**
* Sets IM session name. This name will be displayed in chiclet tooltip.
*/
virtual void setIMSessionName(const std::string& name) { setToolTip(name); }
/**
* Sets id of person/group user is chatting with.
* Session id should be set before calling this
*/
virtual void setOtherParticipantId(const LLUUID& other_participant_id) { mOtherParticipantId = other_participant_id; }
/**
* Enables/disables the counter control for a chiclet.
*/
virtual void enableCounterControl(bool enable);
/**
* Sets required width for a chiclet according to visible controls.
*/
@ -366,6 +447,131 @@ private:
LLChicletInvOfferIconCtrl* mChicletIconCtrl;
};
/**
* Implements notification chiclet. Used to display total amount of unread messages
* across all IM sessions, total amount of system notifications. See EXT-3147 for details
*/
class LLSysWellChiclet : public LLChiclet
{
public:
struct Params : public LLInitParam::Block<Params, LLChiclet::Params>
{
Optional<LLButton::Params> button;
Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications;
/**
* Contains maximum displayed count of unread messages. Default value is 9.
*
* If count is less than "max_unread_count" will be displayed as is.
* Otherwise 9+ will be shown (for default value).
*/
Optional<S32> max_displayed_count;
Params();
};
/*virtual*/ void setCounter(S32 counter);
// *TODO: mantipov: seems getCounter is not necessary for LLNotificationChiclet
// but inherited interface requires it to implement.
// Probably it can be safe removed.
/*virtual*/S32 getCounter() { return mCounter; }
boost::signals2::connection setClickCallback(const commit_callback_t& cb);
/*virtual*/ ~LLSysWellChiclet();
void setToggleState(BOOL toggled);
void setNewMessagesState(bool new_messages);
//this method should change a widget according to state of the SysWellWindow
virtual void updateWidget(bool is_window_empty);
protected:
LLSysWellChiclet(const Params& p);
friend class LLUICtrlFactory;
/**
* Change Well 'Lit' state from 'Lit' to 'Unlit' and vice-versa.
*
* There is an assumption that it will be called 2*N times to do not change its start state.
* @see FlashToLitTimer
*/
void changeLitState(bool blink);
/**
* Displays menu.
*/
virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
virtual void createMenu() = 0;
protected:
class FlashToLitTimer;
LLButton* mButton;
S32 mCounter;
S32 mMaxDisplayedCount;
bool mIsNewMessagesState;
LLFlashTimer* mFlashToLitTimer;
LLContextMenu* mContextMenu;
};
class LLNotificationChiclet : public LLSysWellChiclet
{
LOG_CLASS(LLNotificationChiclet);
friend class LLUICtrlFactory;
public:
struct Params : public LLInitParam::Block<Params, LLSysWellChiclet::Params>{};
protected:
struct ChicletNotificationChannel : public LLNotificationChannel
{
ChicletNotificationChannel(LLNotificationChiclet* chiclet)
: LLNotificationChannel(LLNotificationChannel::Params().filter(filterNotification).name(chiclet->getSessionId().asString()))
, mChiclet(chiclet)
{
// connect counter handlers to the signals
connectToChannel("Group Notifications");
connectToChannel("Offer");
connectToChannel("Notifications");
}
static bool filterNotification(LLNotificationPtr notify);
// connect counter updaters to the corresponding signals
/*virtual*/ void onAdd(LLNotificationPtr p) { mChiclet->setCounter(++mChiclet->mUreadSystemNotifications); }
/*virtual*/ void onDelete(LLNotificationPtr p) { mChiclet->setCounter(--mChiclet->mUreadSystemNotifications); }
LLNotificationChiclet* const mChiclet;
};
boost::scoped_ptr<ChicletNotificationChannel> mNotificationChannel;
LLNotificationChiclet(const Params& p);
/**
* Processes clicks on chiclet menu.
*/
void onMenuItemClicked(const LLSD& user_data);
/**
* Enables chiclet menu items.
*/
bool enableMenuItem(const LLSD& user_data);
/**
* Creates menu.
*/
/*virtual*/ void createMenu();
/*virtual*/ void setCounter(S32 counter);
S32 mUreadSystemNotifications;
};
/**
* Storage class for all IM chiclets. Provides mechanism to display,
* scroll, create, remove chiclets.

View File

@ -30,6 +30,7 @@
#include "llchiclet.h"
#include "lllayoutstack.h"
#include "llpaneltopinfobar.h"
#include "llsyswellwindow.h"
namespace
{
@ -58,6 +59,8 @@ BOOL LLChicletBar::postBuild()
mToolbarStack = getChild<LLLayoutStack>("toolbar_stack");
mChicletPanel = getChild<LLChicletPanel>("chiclet_list");
showWellButton("notification_well", !LLNotificationWellWindow::getInstance()->isWindowEmpty());
LLPanelTopInfoBar::instance().setResizeCallback(boost::bind(&LLChicletBar::fitWithTopInfoBar, this));
LLPanelTopInfoBar::instance().setVisibleCallback(boost::bind(&LLChicletBar::fitWithTopInfoBar, this));

View File

@ -0,0 +1,79 @@
/**
* @file llcommunicationchannel.cpp
* @brief Implementation of llcommunicationchannel
* @author Stinson@lindenlab.com
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h" // must be first include
#include "llcommunicationchannel.h"
#include <string>
#include <map>
#include "llagent.h"
#include "lldate.h"
#include "llnotifications.h"
LLCommunicationChannel::LLCommunicationChannel(const std::string& pName, const std::string& pParentName)
: LLNotificationChannel(pName, pParentName, filterByDoNotDisturbStatus)
, mHistory()
{
}
LLCommunicationChannel::~LLCommunicationChannel()
{
}
bool LLCommunicationChannel::filterByDoNotDisturbStatus(LLNotificationPtr)
{
return !gAgent.isDoNotDisturb();
}
LLCommunicationChannel::history_list_t::const_iterator LLCommunicationChannel::beginHistory() const
{
return mHistory.begin();
}
LLCommunicationChannel::history_list_t::const_iterator LLCommunicationChannel::endHistory() const
{
return mHistory.end();
}
void LLCommunicationChannel::clearHistory()
{
mHistory.clear();
}
void LLCommunicationChannel::onFilterFail(LLNotificationPtr pNotificationPtr)
{
std::string notificationType = pNotificationPtr->getType();
if ((notificationType == "groupnotify")
|| (notificationType == "offer")
|| (notificationType == "notifytoast"))
{
mHistory.insert(std::make_pair<LLDate, LLNotificationPtr>(pNotificationPtr->getDate(), pNotificationPtr));
}
}

View File

@ -0,0 +1,61 @@
/**
* @file llcommunicationchannel.h
* @brief Header file for llcommunicationchannel
* @author Stinson@lindenlab.com
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLCOMMUNICATIONCHANNEL_H
#define LL_LLCOMMUNICATIONCHANNEL_H
#include <string>
#include <map>
#include "lldate.h"
#include "llerror.h"
#include "llnotifications.h"
class LLCommunicationChannel : public LLNotificationChannel
{
LOG_CLASS(LLCommunicationChannel);
public:
LLCommunicationChannel(const std::string& pName, const std::string& pParentName);
virtual ~LLCommunicationChannel();
static bool filterByDoNotDisturbStatus(LLNotificationPtr);
typedef std::multimap<LLDate, LLNotificationPtr> history_list_t;
history_list_t::const_iterator beginHistory() const;
history_list_t::const_iterator endHistory() const;
void clearHistory();
protected:
virtual void onFilterFail(LLNotificationPtr pNotificationPtr);
private:
history_list_t mHistory;
};
#endif // LL_LLCOMMUNICATIONCHANNEL_H

View File

@ -188,15 +188,23 @@ void LLConversationLogFriendObserver::changed(U32 mask)
LLConversationLog::LLConversationLog() :
mAvatarNameCacheConnection()
{
LLControlVariable* ctrl = gSavedPerAccountSettings.getControl("LogInstantMessages").get();
if (ctrl)
LLControlVariable* log_instant_message = gSavedPerAccountSettings.getControl("LogInstantMessages").get();
LLControlVariable* keep_convers_log = gSavedSettings.getControl("KeepConversationLogTranscripts").get();
bool is_log_message = false;
bool is_keep_log = false;
if (log_instant_message)
{
ctrl->getSignal()->connect(boost::bind(&LLConversationLog::enableLogging, this, _2));
if (ctrl->getValue().asBoolean())
{
enableLogging(true);
}
log_instant_message->getSignal()->connect(boost::bind(&LLConversationLog::enableLogging, this, _2));
is_log_message = log_instant_message->getValue().asBoolean();
}
if (keep_convers_log)
{
keep_convers_log->getSignal()->connect(boost::bind(&LLConversationLog::enableLogging, this, _2));
is_keep_log = keep_convers_log->getValue().asBoolean();
}
enableLogging(is_log_message && is_keep_log);
}
void LLConversationLog::enableLogging(bool enable)
@ -229,17 +237,20 @@ void LLConversationLog::logConversation(const LLUUID& session_id, BOOL has_offli
const LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
LLConversation* conversation = findConversation(session);
if (session && conversation)
if (session)
{
if(has_offline_msg)
if (conversation)
{
updateOfflineIMs(session, has_offline_msg);
if(has_offline_msg)
{
updateOfflineIMs(session, has_offline_msg);
}
updateConversationTimestamp(conversation);
}
else
{
createConversation(session);
}
updateConversationTimestamp(conversation);
}
else if (session && !conversation)
{
createConversation(session);
}
}
@ -304,19 +315,17 @@ void LLConversationLog::updateConversationTimestamp(LLConversation* conversation
LLConversation* LLConversationLog::findConversation(const LLIMModel::LLIMSession* session)
{
if (!session)
if (session)
{
return NULL;
}
const LLUUID session_id = session->isOutgoingAdHoc() ? session->generateOutgouigAdHocHash() : session->mSessionID;
const LLUUID session_id = session->isOutgoingAdHoc() ? session->generateOutgouigAdHocHash() : session->mSessionID;
conversations_vec_t::iterator conv_it = mConversations.begin();
for(; conv_it != mConversations.end(); ++conv_it)
{
if (conv_it->getSessionID() == session_id)
conversations_vec_t::iterator conv_it = mConversations.begin();
for(; conv_it != mConversations.end(); ++conv_it)
{
return &*conv_it;
if (conv_it->getSessionID() == session_id)
{
return &*conv_it;
}
}
}
@ -408,8 +417,8 @@ bool LLConversationLog::saveToFile(const std::string& filename)
// [1343221177] 0 1 0 John Doe| 7e4ec5be-783f-49f5-71dz-16c58c64c145 4ec62a74-c246-0d25-2af6-846beac2aa55 john.doe|
// [1343222639] 2 0 0 Ad-hoc Conference| c3g67c89-c479-4c97-b21d-32869bcfe8rc 68f1c33e-4135-3e3e-a897-8c9b23115c09 Ad-hoc Conference hash597394a0-9982-766d-27b8-c75560213b9a|
fprintf(fp, "[%d] %d %d %d %s| %s %s %s|\n",
(S32)conv_it->getTime(),
fprintf(fp, "[%lld] %d %d %d %s| %s %s %s|\n",
(S64)conv_it->getTime(),
(S32)conv_it->getConversationType(),
(S32)0,
(S32)conv_it->hasOfflineMessages(),
@ -443,7 +452,7 @@ bool LLConversationLog::loadFromFile(const std::string& filename)
char history_file_name[MAX_STRING];
int has_offline_ims;
int stype;
S32 time;
time_t time;
// before CHUI-348 it was a flag of conversation voice state
int prereserved_unused;
@ -453,7 +462,7 @@ bool LLConversationLog::loadFromFile(const std::string& filename)
part_id_buffer[0] = '\0';
conv_id_buffer[0] = '\0';
sscanf(buffer, "[%d] %d %d %d %[^|]| %s %s %[^|]|",
sscanf(buffer, "[%ld] %d %d %d %[^|]| %s %s %[^|]|",
&time,
&stype,
&prereserved_unused,

View File

@ -102,35 +102,44 @@ void LLConversationItem::showProperties(void)
{
}
void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items)
void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32 flags)
{
items.push_back(std::string("view_profile"));
items.push_back(std::string("im"));
items.push_back(std::string("offer_teleport"));
items.push_back(std::string("voice_call"));
items.push_back(std::string("chat_history"));
items.push_back(std::string("separator_chat_history"));
items.push_back(std::string("add_friend"));
items.push_back(std::string("remove_friend"));
items.push_back(std::string("invite_to_group"));
items.push_back(std::string("separator_invite_to_group"));
items.push_back(std::string("map"));
items.push_back(std::string("share"));
items.push_back(std::string("pay"));
items.push_back(std::string("block_unblock"));
items.push_back(std::string("MuteText"));
if(this->getType() != CONV_SESSION_1_ON_1 && mDisplayModeratorOptions)
if (flags & ITEM_IN_MULTI_SELECTION)
{
items.push_back(std::string("Moderator Options Separator"));
items.push_back(std::string("Moderator Options"));
items.push_back(std::string("AllowTextChat"));
items.push_back(std::string("moderate_voice_separator"));
items.push_back(std::string("ModerateVoiceToggleMuteSelected"));
items.push_back(std::string("ModerateVoiceMute"));
items.push_back(std::string("ModerateVoiceUnmute"));
items.push_back(std::string("im"));
items.push_back(std::string("offer_teleport"));
items.push_back(std::string("voice_call"));
items.push_back(std::string("remove_friends"));
}
else
{
items.push_back(std::string("view_profile"));
items.push_back(std::string("im"));
items.push_back(std::string("offer_teleport"));
items.push_back(std::string("voice_call"));
items.push_back(std::string("chat_history"));
items.push_back(std::string("separator_chat_history"));
items.push_back(std::string("add_friend"));
items.push_back(std::string("remove_friend"));
items.push_back(std::string("invite_to_group"));
items.push_back(std::string("separator_invite_to_group"));
items.push_back(std::string("map"));
items.push_back(std::string("share"));
items.push_back(std::string("pay"));
items.push_back(std::string("block_unblock"));
items.push_back(std::string("MuteText"));
if ((getType() != CONV_SESSION_1_ON_1) && mDisplayModeratorOptions)
{
items.push_back(std::string("Moderator Options Separator"));
items.push_back(std::string("Moderator Options"));
items.push_back(std::string("AllowTextChat"));
items.push_back(std::string("moderate_voice_separator"));
items.push_back(std::string("ModerateVoiceToggleMuteSelected"));
items.push_back(std::string("ModerateVoiceMute"));
items.push_back(std::string("ModerateVoiceUnmute"));
}
}
}
//
@ -306,7 +315,7 @@ void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("close_conversation"));
items.push_back(std::string("separator_disconnect_from_voice"));
buildParticipantMenuOptions(items);
buildParticipantMenuOptions(items, flags);
}
else if(this->getType() == CONV_SESSION_GROUP)
{
@ -475,7 +484,7 @@ void LLConversationItemParticipant::buildContextMenu(LLMenuGL& menu, U32 flags)
menuentry_vec_t items;
menuentry_vec_t disabled_items;
buildParticipantMenuOptions(items);
buildParticipantMenuOptions(items, flags);
hide_context_entries(menu, items, disabled_items);
}

View File

@ -130,7 +130,7 @@ public:
void postEvent(const std::string& event_type, LLConversationItemSession* session, LLConversationItemParticipant* participant);
void buildParticipantMenuOptions(menuentry_vec_t& items);
void buildParticipantMenuOptions(menuentry_vec_t& items, U32 flags);
protected:
std::string mName; // Name of the session or the participant

View File

@ -81,7 +81,9 @@ LLConversationViewSession::LLConversationViewSession(const LLConversationViewSes
mSpeakingIndicator(NULL),
mVoiceClientObserver(NULL),
mCollapsedMode(false),
mHasArrow(true)
mHasArrow(true),
mFlashStateOn(false),
mFlashStarted(false)
{
mFlashTimer = new LLFlashTimer();
}
@ -101,7 +103,17 @@ LLConversationViewSession::~LLConversationViewSession()
void LLConversationViewSession::setFlashState(bool flash_state)
{
mFlashStateOn = flash_state;
(flash_state ? mFlashTimer->startFlashing() : mFlashTimer->stopFlashing());
mFlashStarted = false;
mFlashTimer->stopFlashing();
}
void LLConversationViewSession::startFlashing()
{
if (mFlashStateOn && !mFlashStarted)
{
mFlashStarted = true;
mFlashTimer->startFlashing();
}
}
bool LLConversationViewSession::isHighlightAllowed()
@ -198,8 +210,11 @@ void LLConversationViewSession::draw()
drawOpenFolderArrow(default_params, sFgColor);
}
// Indicate that flash can start (moot operation if already started, done or not flashing)
startFlashing();
// draw highlight for selected items
drawHighlight(show_context, true, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor);
drawHighlight(show_context, true, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
// Draw children if root folder, or any other folder that is open. Do not draw children when animating to closed state or you get rendering overlap.
bool draw_children = getRoot() == static_cast<LLFolderViewFolder*>(this) || isOpen();
@ -231,6 +246,7 @@ BOOL LLConversationViewSession::handleMouseDown( S32 x, S32 y, MASK mask )
if(result && getRoot()->getCurSelectedItem() == this)
{
LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
im_container->clearAllFlashStates();
im_container->selectConversationPair(session_id, false);
im_container->collapseMessagesPane(false);
}
@ -427,6 +443,7 @@ void LLConversationViewParticipant::draw()
static LLUIColor sFgDisabledColor = LLUIColorTable::instance().getColor("MenuItemDisabledColor", DEFAULT_WHITE);
static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
static LLUIColor sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE);
static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE);
@ -450,7 +467,7 @@ void LLConversationViewParticipant::draw()
color = mIsSelected ? sHighlightFgColor : sFgColor;
}
drawHighlight(show_context, mIsSelected, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor);
drawHighlight(show_context, mIsSelected, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
drawLabel(font, text_left, y, color, right_x);
refresh();
@ -530,27 +547,6 @@ void LLConversationViewParticipant::onMouseLeave(S32 x, S32 y, MASK mask)
LLFolderViewItem::onMouseLeave(x, y, mask);
}
BOOL LLConversationViewParticipant::handleMouseDown( S32 x, S32 y, MASK mask )
{
LLConversationItem* item = NULL;
LLConversationViewSession* session_widget =
dynamic_cast<LLConversationViewSession *>(this->getParentFolder());
if (session_widget)
{
item = dynamic_cast<LLConversationItem*>(session_widget->getViewModelItem());
}
LLUUID session_id = item? item->getUUID() : LLUUID();
BOOL result = LLFolderViewItem::handleMouseDown(x, y, mask);
if(result)
{
(LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"))->
selectConversationPair(session_id, false);
}
return result;
}
S32 LLConversationViewParticipant::getLabelXPos()
{
return getIndentation() + mAvatarIcon->getRect().getWidth() + mIconPad;

View File

@ -58,6 +58,7 @@ protected:
/*virtual*/ bool isHighlightAllowed();
/*virtual*/ bool isHighlightActive();
/*virtual*/ bool isFlashing() { return mFlashStateOn; }
LLFloaterIMContainer* mContainer;
@ -83,11 +84,12 @@ public:
virtual void refresh();
void setFlashState(bool flash_state);
/*virtual*/ void setFlashState(bool flash_state);
private:
void onCurrentVoiceSessionChanged(const LLUUID& session_id);
void startFlashing();
LLPanel* mItemPanel;
LLPanel* mCallIconLayoutPanel;
@ -95,6 +97,7 @@ private:
LLOutputMonitorCtrl* mSpeakingIndicator;
LLFlashTimer* mFlashTimer;
bool mFlashStateOn;
bool mFlashStarted;
bool mCollapsedMode;
bool mHasArrow;
@ -129,8 +132,6 @@ public:
void addToFolder(LLFolderViewFolder* folder);
void addToSession(const LLUUID& session_id);
/*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
void onMouseEnter(S32 x, S32 y, MASK mask);
void onMouseLeave(S32 x, S32 y, MASK mask);

View File

@ -0,0 +1,162 @@
/**
* @file lldonotdisturbnotificationstorage.cpp
* @brief Implementation of lldonotdisturbnotificationstorage
* @author Stinson@lindenlab.com
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "lldonotdisturbnotificationstorage.h"
#include "llcommunicationchannel.h"
#include "lldir.h"
#include "llerror.h"
#include "llfasttimer_class.h"
#include "llnotifications.h"
#include "llnotificationhandler.h"
#include "llnotificationstorage.h"
#include "llscriptfloater.h"
#include "llsd.h"
#include "llsingleton.h"
#include "lluuid.h"
LLDoNotDisturbNotificationStorage::LLDoNotDisturbNotificationStorage()
: LLSingleton<LLDoNotDisturbNotificationStorage>()
, LLNotificationStorage(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "dnd_notifications.xml"))
{
}
LLDoNotDisturbNotificationStorage::~LLDoNotDisturbNotificationStorage()
{
}
void LLDoNotDisturbNotificationStorage::initialize()
{
getCommunicationChannel()->connectFailedFilter(boost::bind(&LLDoNotDisturbNotificationStorage::onChannelChanged, this, _1));
}
static LLFastTimer::DeclareTimer FTM_SAVE_DND_NOTIFICATIONS("Save DND Notifications");
void LLDoNotDisturbNotificationStorage::saveNotifications()
{
LLFastTimer _(FTM_SAVE_DND_NOTIFICATIONS);
LLNotificationChannelPtr channelPtr = getCommunicationChannel();
const LLCommunicationChannel *commChannel = dynamic_cast<LLCommunicationChannel*>(channelPtr.get());
llassert(commChannel != NULL);
LLSD output = LLSD::emptyMap();
LLSD& data = output["data"];
data = LLSD::emptyArray();
for (LLCommunicationChannel::history_list_t::const_iterator historyIter = commChannel->beginHistory();
historyIter != commChannel->endHistory(); ++historyIter)
{
LLNotificationPtr notificationPtr = historyIter->second;
if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() && !notificationPtr->isExpired())
{
data.append(notificationPtr->asLLSD());
}
}
writeNotifications(output);
}
static LLFastTimer::DeclareTimer FTM_LOAD_DND_NOTIFICATIONS("Load DND Notifications");
void LLDoNotDisturbNotificationStorage::loadNotifications()
{
LLFastTimer _(FTM_LOAD_DND_NOTIFICATIONS);
LLSD input;
if (!readNotifications(input) ||input.isUndefined())
{
return;
}
LLSD& data = input["data"];
if (data.isUndefined())
{
return;
}
LLNotifications& instance = LLNotifications::instance();
for (LLSD::array_const_iterator notification_it = data.beginArray();
notification_it != data.endArray();
++notification_it)
{
LLSD notification_params = *notification_it;
LLNotificationPtr notification(new LLNotification(notification_params));
const LLUUID& notificationID = notification->id();
if (instance.find(notificationID))
{
instance.update(notification);
}
else
{
LLNotificationResponderInterface* responder = createResponder(notification_params["name"], notification_params["responder"]);
if (responder == NULL)
{
LL_WARNS("LLDoNotDisturbNotificationStorage") << "cannot create responder for notification of type '"
<< notification->getType() << "'" << LL_ENDL;
}
else
{
LLNotificationResponderPtr responderPtr(responder);
notification->setResponseFunctor(responderPtr);
}
instance.add(notification);
}
}
// Clear the communication channel history and rewrite the save file to empty it as well
LLNotificationChannelPtr channelPtr = getCommunicationChannel();
LLCommunicationChannel *commChannel = dynamic_cast<LLCommunicationChannel*>(channelPtr.get());
llassert(commChannel != NULL);
commChannel->clearHistory();
saveNotifications();
}
LLNotificationChannelPtr LLDoNotDisturbNotificationStorage::getCommunicationChannel() const
{
LLNotificationChannelPtr channelPtr = LLNotifications::getInstance()->getChannel("Communication");
llassert(channelPtr);
return channelPtr;
}
bool LLDoNotDisturbNotificationStorage::onChannelChanged(const LLSD& pPayload)
{
if (pPayload["sigtype"].asString() != "load")
{
saveNotifications();
}
return false;
}

View File

@ -0,0 +1,57 @@
/**
* @file lldonotdisturbnotificationstorage.h
* @brief Header file for lldonotdisturbnotificationstorage
* @author Stinson@lindenlab.com
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLDONOTDISTURBNOTIFICATIONSTORAGE_H
#define LL_LLDONOTDISTURBNOTIFICATIONSTORAGE_H
#include "llerror.h"
#include "llnotifications.h"
#include "llnotificationstorage.h"
#include "llsingleton.h"
class LLSD;
class LLDoNotDisturbNotificationStorage : public LLSingleton<LLDoNotDisturbNotificationStorage>, public LLNotificationStorage
{
LOG_CLASS(LLDoNotDisturbNotificationStorage);
public:
LLDoNotDisturbNotificationStorage();
~LLDoNotDisturbNotificationStorage();
void initialize();
void saveNotifications();
void loadNotifications();
protected:
private:
LLNotificationChannelPtr getCommunicationChannel() const;
bool onChannelChanged(const LLSD& pPayload);
};
#endif // LL_LLDONOTDISTURBNOTIFICATIONSTORAGE_H

View File

@ -444,7 +444,7 @@ void LLVolumeImplFlexible::doFlexibleUpdate()
return ;
}
// stinson 11/12/2012: Need to check with davep on the following.
// Fix for MAINT-1894
// Skipping the flexible update if render res is negative. If we were to continue with a negative value,
// the subsequent S32 num_render_sections = 1<<mRenderRes; code will specify a really large number of
// render sections which will then create a length exception in the std::vector::resize() method.

View File

@ -67,7 +67,8 @@ BOOL LLFloaterConversationLog::postBuild()
if (ctrl)
{
ctrl->getSignal()->connect(boost::bind(&LLFloaterConversationLog::onCallLoggingEnabledDisabled, this, _2));
onCallLoggingEnabledDisabled(ctrl->getValue().asBoolean());
onCallLoggingEnabledDisabled(ctrl->getValue().asBoolean()
&& gSavedSettings.getBOOL("KeepConversationLogTranscripts"));
}
return LLFloater::postBuild();

View File

@ -51,8 +51,8 @@
#include "llconversationview.h"
#include "llcallbacklist.h"
#include "llworld.h"
#include "llsdserialize.h"
//
// LLFloaterIMContainer
//
@ -171,6 +171,9 @@ BOOL LLFloaterIMContainer::postBuild()
// Open IM session with selected participant on double click event
mConversationsListPanel->setDoubleClickCallback(boost::bind(&LLFloaterIMContainer::doToSelected, this, LLSD("im")));
// The resize limits for LLFloaterIMContainer should be updated, based on current values of width of conversation and message panels
mConversationsPane->getResizeBar()->setResizeListener(boost::bind(&LLFloaterIMContainer::assignResizeLimits, this));
// Create the root model and view for all conversation sessions
LLConversationItem* base_item = new LLConversationItem(getRootViewModel());
@ -247,6 +250,7 @@ void LLFloaterIMContainer::onOpen(const LLSD& key)
{
LLMultiFloater::onOpen(key);
openNearbyChat();
assignResizeLimits();
}
// virtual
@ -308,26 +312,6 @@ void LLFloaterIMContainer::onCloseFloater(LLUUID& id)
setFocus(TRUE);
}
// virtual
void LLFloaterIMContainer::computeResizeLimits(S32& new_min_width, S32& new_min_height)
{
// possibly increase floater's minimum height according to children's minimums
for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx)
{
LLFloater* floaterp = dynamic_cast<LLFloater*>(mTabContainer->getPanelByIndex(tab_idx));
if (floaterp)
{
new_min_height = llmax(new_min_height, floaterp->getMinHeight());
}
}
S32 conversations_pane_min_dim = mConversationsPane->getRelevantMinDim();
S32 messages_pane_min_dim = mMessagesPane->getRelevantMinDim();
// set floater's minimum width according to relevant minimal children's dimensionals
new_min_width = conversations_pane_min_dim + messages_pane_min_dim + LLPANEL_BORDER_WIDTH*2;
}
void LLFloaterIMContainer::onNewMessageReceived(const LLSD& data)
{
LLUUID session_id = data["session_id"].asUUID();
@ -621,6 +605,12 @@ void LLFloaterIMContainer::setVisible(BOOL visible)
LLMultiFloater::setVisible(visible);
}
void LLFloaterIMContainer::updateResizeLimits()
{
LLMultiFloater::updateResizeLimits();
assignResizeLimits();
}
void LLFloaterIMContainer::collapseMessagesPane(bool collapse)
{
if (mMessagesPane->isCollapsed() == collapse)
@ -694,6 +684,7 @@ void LLFloaterIMContainer::collapseConversationsPane(bool collapse)
{
widget->setOpen(false);
}
widget->requestArrange();
}
}
}
@ -728,6 +719,15 @@ void LLFloaterIMContainer::updateState(bool collapse, S32 delta_width)
setResizeLimits(expanded_min_size, expanded_min_size);
}
assignResizeLimits();
}
void LLFloaterIMContainer::assignResizeLimits()
{
const LLRect& conv_rect = mConversationsPane->isCollapsed() ? LLRect() : mConversationsPane->getRect();
S32 msg_limits = mMessagesPane->isCollapsed() ? 0 : mMessagesPane->getExpandedMinDim();
S32 x_limits = conv_rect.getWidth() + msg_limits;
setResizeLimits(x_limits + LLPANEL_BORDER_WIDTH * 3, getMinHeight());
}
void LLFloaterIMContainer::onAddButtonClicked()
@ -1108,7 +1108,27 @@ bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata)
uuid_vec_t uuids;
getParticipantUUIDs(uuids);
if("can_activate_group" == item)
if ("conversation_log" == item)
{
return gSavedSettings.getBOOL("KeepConversationLogTranscripts");
}
//Enable Chat history item for ad-hoc and group conversations
if ("can_chat_history" == item)
{
if (getCurSelectedViewModelItem()->getType() != LLConversationItem::CONV_PARTICIPANT)
{
return isConversationLoggingAllowed();
}
}
// If nothing is selected(and selected item is not group chat), everything needs to be disabled
if (uuids.size() <= 0)
{
return getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP;
}
if("can_activate_group" == item)
{
LLUUID selected_group_id = getCurSelectedViewModelItem()->getUUID();
return gAgent.getGroupID() != selected_group_id;
@ -1119,17 +1139,6 @@ bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata)
bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_vec_t& uuids)
{
if ("conversation_log" == item)
{
return gSavedSettings.getBOOL("KeepConversationLogTranscripts");
}
// If nothing is selected, everything needs to be disabled
if (uuids.size() <= 0)
{
return false;
}
// Extract the single select info
bool is_single_select = (uuids.size() == 1);
const LLUUID& single_id = uuids.front();
@ -1236,6 +1245,19 @@ void LLFloaterIMContainer::showConversation(const LLUUID& session_id)
selectConversationPair(session_id, true);
}
void LLFloaterIMContainer::clearAllFlashStates()
{
conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin();
for (;widget_it != mConversationsWidgets.end(); ++widget_it)
{
LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second);
if (widget)
{
widget->setFlashState(false);
}
}
}
void LLFloaterIMContainer::selectConversation(const LLUUID& session_id)
{
selectConversationPair(session_id, true);
@ -1247,17 +1269,6 @@ BOOL LLFloaterIMContainer::selectConversationPair(const LLUUID& session_id, bool
BOOL handled = TRUE;
LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id);
// On selection, stop the flash state on all conversation widgets
conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin();
for (;widget_it != mConversationsWidgets.end(); ++widget_it)
{
LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second);
if (widget)
{
widget->setFlashState(false);
}
}
/* widget processing */
if (select_widget)
{
@ -1419,14 +1430,14 @@ bool LLFloaterIMContainer::removeConversationListItem(const LLUUID& uuid, bool c
// Delete the widget and the associated conversation item
// Note : since the mConversationsItems is also the listener to the widget, deleting
// the widget will also delete its listener
bool isWidgetSelected = false;
bool is_widget_selected = false;
LLFolderViewItem* new_selection = NULL;
LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid);
if (widget)
{
isWidgetSelected = widget->isSelected();
is_widget_selected = widget->isSelected();
new_selection = mConversationsRoot->getNextFromChild(widget);
if(new_selection == NULL)
if (!new_selection)
{
new_selection = mConversationsRoot->getPreviousFromChild(widget);
}
@ -1441,18 +1452,24 @@ bool LLFloaterIMContainer::removeConversationListItem(const LLUUID& uuid, bool c
if (change_focus)
{
setFocus(TRUE);
if(new_selection != NULL)
if (new_selection)
{
if (mConversationsWidgets.size() == 1)
new_selection = new_selection->getParentFolder();
LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem());
if(vmi != NULL)
{
selectConversationPair(vmi->getUUID(), true);
// If only one widget is left, it has to be the Nearby Chat. Select it directly.
selectConversationPair(LLUUID(NULL), true);
}
else
{
LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem());
if (vmi)
{
selectConversationPair(vmi->getUUID(), true);
}
}
}
}
return isWidgetSelected;
return is_widget_selected;
}
LLConversationViewSession* LLFloaterIMContainer::createConversationItemWidget(LLConversationItem* item)

View File

@ -60,6 +60,7 @@ public:
/*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void draw();
/*virtual*/ void setVisible(BOOL visible);
/*virtual*/ void updateResizeLimits();
void onCloseFloater(LLUUID& id);
/*virtual*/ void addFloater(LLFloater* floaterp,
@ -69,6 +70,7 @@ public:
void showConversation(const LLUUID& session_id);
void selectConversation(const LLUUID& session_id);
BOOL selectConversationPair(const LLUUID& session_id, bool select_widget);
void clearAllFlashStates();
/*virtual*/ void tabClose();
void showStub(bool visible);
@ -105,12 +107,14 @@ public:
bool enableContextMenuItem(const std::string& item, uuid_vec_t& selectedIDS);
void doToParticipants(const std::string& item, uuid_vec_t& selectedIDS);
void assignResizeLimits();
private:
typedef std::map<LLUUID,LLFloater*> avatarID_panel_map_t;
avatarID_panel_map_t mSessions;
boost::signals2::connection mNewMessageConnection;
/*virtual*/ void computeResizeLimits(S32& new_min_width, S32& new_min_height);
/*virtual*/ void computeResizeLimits(S32& new_min_width, S32& new_min_height) {}
void onNewMessageReceived(const LLSD& data);

View File

@ -113,8 +113,8 @@ BOOL LLFloaterIMNearbyChat::postBuild()
BOOL result = LLFloaterIMSessionTab::postBuild();
mInputEditor->setCommitCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxCommit, this));
mInputEditor->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this));
mInputEditor->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this));
mInputEditor->setKeystrokeCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxKeystroke, this));
mInputEditor->setFocusLostCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusLost, this));
mInputEditor->setFocusReceivedCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusReceived, this));
mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle"));
@ -354,13 +354,11 @@ BOOL LLFloaterIMNearbyChat::matchChatTypeTrigger(const std::string& in_str, std:
return string_was_found;
}
void LLFloaterIMNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userdata)
void LLFloaterIMNearbyChat::onChatBoxKeystroke()
{
LLFirstUse::otherAvatarChatFirst(false);
LLFloaterIMNearbyChat* self = (LLFloaterIMNearbyChat *)userdata;
LLWString raw_text = self->mInputEditor->getWText();
LLWString raw_text = mInputEditor->getWText();
// Can't trim the end, because that will cause autocompletion
// to eat trailing spaces that might be part of a gesture.
@ -386,8 +384,8 @@ void LLFloaterIMNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userd
// the selection will already be deleted, but we need to trim
// off the character before
std::string new_text = raw_text.substr(0, length-1);
self->mInputEditor->setText( new_text );
self->mInputEditor->setCursorToEnd();
mInputEditor->setText( new_text );
mInputEditor->setCursorToEnd();
length = length - 1;
}
*/
@ -407,17 +405,17 @@ void LLFloaterIMNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userd
if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str))
{
std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size());
self->mInputEditor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part
mInputEditor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part
// Select to end of line, starting from the character
// after the last one the user typed.
self->mInputEditor->selectNext(rest_of_match, false);
mInputEditor->selectNext(rest_of_match, false);
}
else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str))
{
std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size());
self->mInputEditor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part
self->mInputEditor->endOfDoc();
mInputEditor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part
mInputEditor->endOfDoc();
}
//llinfos << "GESTUREDEBUG " << trigger
@ -428,7 +426,7 @@ void LLFloaterIMNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userd
}
// static
void LLFloaterIMNearbyChat::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata)
void LLFloaterIMNearbyChat::onChatBoxFocusLost()
{
// stop typing animation
gAgent.stopTyping();

View File

@ -83,8 +83,8 @@ public:
protected:
static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str);
static void onChatBoxKeystroke(LLTextEditor* caller, void* userdata);
static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata);
void onChatBoxKeystroke();
void onChatBoxFocusLost();
void onChatBoxFocusReceived();
void sendChat( EChatType type );

View File

@ -73,8 +73,7 @@ LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id)
mTypingTimer(),
mTypingTimeoutTimer(),
mPositioned(false),
mSessionInitialized(false),
mStartConferenceInSameFloater(false)
mSessionInitialized(false)
{
mIsNearbyChat = false;
@ -83,8 +82,11 @@ LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id)
setOverlapsScreenChannel(true);
LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this);
mEnableCallbackRegistrar.add("Avatar.EnableGearItem", boost::bind(&LLFloaterIMSession::enableGearMenuItem, this, _2));
mCommitCallbackRegistrar.add("Avatar.GearDoToSelected", boost::bind(&LLFloaterIMSession::GearDoToSelected, this, _2));
mEnableCallbackRegistrar.add("Avatar.CheckGearItem", boost::bind(&LLFloaterIMSession::checkGearMenuItem, this, _2));
setDocked(true);
setDocked(true);
}
@ -190,6 +192,36 @@ void LLFloaterIMSession::onSendMsg( LLUICtrl* ctrl, void* userdata )
self->setTyping(false);
}
bool LLFloaterIMSession::enableGearMenuItem(const LLSD& userdata)
{
std::string command = userdata.asString();
uuid_vec_t selected_uuids;
selected_uuids.push_back(mOtherParticipantUUID);
LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
return floater_container->enableContextMenuItem(command, selected_uuids);
}
void LLFloaterIMSession::GearDoToSelected(const LLSD& userdata)
{
std::string command = userdata.asString();
uuid_vec_t selected_uuids;
selected_uuids.push_back(mOtherParticipantUUID);
LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
floater_container->doToParticipants(command, selected_uuids);
}
bool LLFloaterIMSession::checkGearMenuItem(const LLSD& userdata)
{
std::string command = userdata.asString();
uuid_vec_t selected_uuids;
selected_uuids.push_back(mOtherParticipantUUID);
LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
return floater_container->checkContextMenuItem(command, selected_uuids);
}
void LLFloaterIMSession::sendMsgFromInputEditor()
{
if (gAgent.isGodlike()
@ -429,8 +461,6 @@ void LLFloaterIMSession::addP2PSessionParticipants(const LLSD& notification, con
return;
}
mStartConferenceInSameFloater = true;
LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID);
// first check whether this is a voice session
@ -724,7 +754,7 @@ void LLFloaterIMSession::sessionInitReplyReceived(const LLUUID& im_session_id)
}
initIMFloater();
LLFloaterIMSessionTab::updateGearBtn();
//*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB)
//need to send delayed messages collected while waiting for session initialization

View File

@ -99,6 +99,9 @@ public:
void setPositioned(bool b) { mPositioned = b; };
void onVisibilityChange(const LLSD& new_visibility);
bool enableGearMenuItem(const LLSD& userdata);
void GearDoToSelected(const LLSD& userdata);
bool checkGearMenuItem(const LLSD& userdata);
// Implements LLVoiceClientStatusObserver::onChange() to enable the call
// button when voice is available
@ -124,8 +127,6 @@ public:
//used as a callback on receiving new IM message
static void sRemoveTypingIndicator(const LLSD& data);
static void onIMChicletCreated(const LLUUID& session_id);
bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; }
const LLUUID& getOtherParticipantUUID() {return mOtherParticipantUUID;}
static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb);
@ -185,8 +186,6 @@ private:
bool mSessionInitialized;
LLSD mQueuedMsgsForInit;
bool mStartConferenceInSameFloater;
uuid_vec_t mInvitedParticipants;
// connection to voice channel state change signal

View File

@ -82,6 +82,13 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id)
LLFloaterIMSessionTab::~LLFloaterIMSessionTab()
{
delete mRefreshTimer;
// Select Nearby Chat session
LLFloaterIMContainer* container = LLFloaterReg::findTypedInstance<LLFloaterIMContainer>("im_container");
if (container)
{
container->selectConversationPair(LLUUID(NULL), true);
}
}
//static
@ -177,6 +184,7 @@ void LLFloaterIMSessionTab::addToHost(const LLUUID& session_id)
// LLFloater::mLastHostHandle = floater_container (a "future" host)
conversp->setHost(floater_container);
conversp->setHost(NULL);
conversp->forceReshape();
}
// Added floaters share some state (like sort order) with their host
conversp->setSortOrder(floater_container->getSortOrder());
@ -197,6 +205,8 @@ BOOL LLFloaterIMSessionTab::postBuild()
mTearOffBtn = getChild<LLButton>("tear_off_btn");
mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this));
mGearBtn = getChild<LLButton>("gear_btn");
mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel");
// Add a scroller for the folder (participant) view
@ -224,7 +234,8 @@ BOOL LLFloaterIMSessionTab::postBuild()
setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE);
mSaveRect = isTornOff();
mSaveRect = isNearbyChat()
&& !gSavedSettings.getBOOL("NearbyChatIsNotTornOff");
initRectControl();
if (isChatMultiTab())
@ -236,14 +247,31 @@ BOOL LLFloaterIMSessionTab::postBuild()
result = LLDockableFloater::postBuild();
}
// Now ready to build the conversation and participants list
// Create the root using an ad-hoc base item
LLConversationItem* base_item = new LLConversationItem(mSessionID, mConversationViewModel);
LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>());
p.rect = LLRect(0, 0, getRect().getWidth(), 0);
p.parent_panel = mParticipantListPanel;
p.listener = base_item;
p.view_model = &mConversationViewModel;
p.root = NULL;
p.use_ellipses = true;
p.options_menu = "menu_conversation.xml";
mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
// Attach that root to the scroller
mScroller->addChild(mConversationsRoot);
mConversationsRoot->setScrollContainer(mScroller);
mConversationsRoot->setFollowsAll();
mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox);
buildConversationViewParticipant();
refreshConversation();
// Zero expiry time is set only once to allow initial update.
mRefreshTimer->setTimerExpirySec(0);
mRefreshTimer->start();
initBtns();
return result;
}
@ -301,10 +329,10 @@ void LLFloaterIMSessionTab::onFocusReceived()
LLTransientDockableFloater::onFocusReceived();
LLFloaterIMContainer* container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
LLFloaterIMContainer* container = LLFloaterReg::findTypedInstance<LLFloaterIMContainer>("im_container");
if (container)
{
container->selectConversationPair(mSessionID, ! getHost());
container->selectConversationPair(mSessionID, true);
container->showStub(! getHost());
}
}
@ -384,31 +412,6 @@ void LLFloaterIMSessionTab::buildConversationViewParticipant()
return;
}
// Create or recreate the root folder: this is a dummy folder (not shown) but required by the LLFolderView architecture
// We need to redo this when rebuilding as the session id (mSessionID) *may* have changed
if (mConversationsRoot)
{
// Remove the old root if any
mScroller->removeChild(mConversationsRoot);
}
// Create the root using an ad-hoc base item
LLConversationItem* base_item = new LLConversationItem(mSessionID, mConversationViewModel);
LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>());
p.rect = LLRect(0, 0, getRect().getWidth(), 0);
p.parent_panel = mParticipantListPanel;
p.listener = base_item;
p.view_model = &mConversationViewModel;
p.root = NULL;
p.use_ellipses = true;
p.options_menu = "menu_conversation.xml";
mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
// Attach that root to the scroller
mScroller->addChild(mConversationsRoot);
mConversationsRoot->setScrollContainer(mScroller);
mConversationsRoot->setFollowsAll();
mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox);
// Create the participants widgets now
LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin();
LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd();
@ -610,8 +613,8 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar()
// prevent start conversation before its container
LLFloaterIMContainer::getInstance();
bool is_torn_off = checkIfTornOff();
if (!is_torn_off)
bool is_not_torn_off = !checkIfTornOff();
if (is_not_torn_off)
{
hideAllStandardButtons();
}
@ -620,7 +623,7 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar()
// Participant list should be visible only in torn off floaters.
bool is_participant_list_visible =
is_torn_off
!is_not_torn_off
&& gSavedSettings.getBOOL("IMShowControlPanel")
&& !mIsP2PChat;
@ -628,27 +631,42 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar()
// Display collapse image (<<) if the floater is hosted
// or if it is torn off but has an open control panel.
bool is_expanded = !is_torn_off || is_participant_list_visible;
bool is_expanded = is_not_torn_off || is_participant_list_visible;
mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon"));
mExpandCollapseBtn->setToolTip(
is_not_torn_off?
getString("expcol_button_not_tearoff_tooltip") :
(is_expanded?
getString("expcol_button_tearoff_and_expanded_tooltip") :
getString("expcol_button_tearoff_and_collapsed_tooltip")));
// toggle floater's drag handle and title visibility
if (mDragHandle)
{
mDragHandle->setTitleVisible(is_torn_off);
mDragHandle->setTitleVisible(!is_not_torn_off);
}
// The button (>>) should be disabled for torn off P2P conversations.
mExpandCollapseBtn->setEnabled(!is_torn_off || !mIsP2PChat);
mExpandCollapseBtn->setEnabled(is_not_torn_off || !mIsP2PChat);
mTearOffBtn->setImageOverlay(getString(is_torn_off? "return_icon" : "tear_off_icon"));
mTearOffBtn->setToolTip(getString(!is_torn_off? "tooltip_to_separate_window" : "tooltip_to_main_window"));
mTearOffBtn->setImageOverlay(getString(is_not_torn_off? "tear_off_icon" : "return_icon"));
mTearOffBtn->setToolTip(getString(is_not_torn_off? "tooltip_to_separate_window" : "tooltip_to_main_window"));
mCloseBtn->setVisible(!is_torn_off && !mIsNearbyChat);
mCloseBtn->setVisible(is_not_torn_off && !mIsNearbyChat);
enableDisableCallBtn();
showTranslationCheckbox();
}
void LLFloaterIMSessionTab::forceReshape()
{
LLRect floater_rect = getRect();
reshape(llmax(floater_rect.getWidth(), this->getMinWidth()),
llmax(floater_rect.getHeight(), this->getMinHeight()),
true);
}
void LLFloaterIMSessionTab::reshapeChatHistory()
{
@ -735,19 +753,6 @@ void LLFloaterIMSessionTab::onOpen(const LLSD& key)
}
}
// virtual
void LLFloaterIMSessionTab::onClose(bool app_quitting)
{
// Always suppress the IM from the conversations list on close if present for any reason
if (LLFloaterIMSessionTab::isChatMultiTab())
{
LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance();
if (im_box)
{
im_box->removeConversationListItem(mKey);
}
}
}
void LLFloaterIMSessionTab::onTearOffClicked()
{
@ -755,7 +760,58 @@ void LLFloaterIMSessionTab::onTearOffClicked()
mSaveRect = isTornOff();
initRectControl();
LLFloater::onClickTearOff(this);
if (isTornOff())
{
forceReshape();
}
refreshConversation();
updateGearBtn();
}
void LLFloaterIMSessionTab::updateGearBtn()
{
BOOL prevVisibility = mGearBtn->getVisible();
mGearBtn->setVisible(checkIfTornOff() && mIsP2PChat);
// Move buttons if Gear button changed visibility
if(prevVisibility != mGearBtn->getVisible())
{
LLRect gear_btn_rect = mGearBtn->getRect();
LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect();
LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect();
S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight;
S32 right_shift = gear_btn_rect.getWidth() + gap_width;
if(mGearBtn->getVisible())
{
// Move buttons to the right to give space for Gear button
add_btn_rect.translate(right_shift,0);
call_btn_rect.translate(right_shift,0);
}
else
{
add_btn_rect.translate(-right_shift,0);
call_btn_rect.translate(-right_shift,0);
}
getChild<LLButton>("add_btn")->setRect(add_btn_rect);
getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect);
}
}
void LLFloaterIMSessionTab::initBtns()
{
LLRect gear_btn_rect = mGearBtn->getRect();
LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect();
LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect();
S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight;
S32 right_shift = gear_btn_rect.getWidth() + gap_width;
add_btn_rect.translate(-right_shift,0);
call_btn_rect.translate(-right_shift,0);
getChild<LLButton>("add_btn")->setRect(add_btn_rect);
getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect);
}
// static

View File

@ -77,7 +77,6 @@ public:
// LLFloater overrides
/*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void onClose(bool app_quitting);
/*virtual*/ BOOL postBuild();
/*virtual*/ void draw();
/*virtual*/ void setVisible(BOOL visible);
@ -92,10 +91,13 @@ public:
void setSortOrder(const LLConversationSort& order);
virtual void onTearOffClicked();
void updateGearBtn();
void initBtns();
virtual void updateMessages() {}
LLConversationItem* getCurSelectedViewModelItem();
void forceReshape();
protected:
// callback for click on any items of the visual states menu
@ -157,6 +159,8 @@ protected:
LLButton* mExpandCollapseBtn;
LLButton* mTearOffBtn;
LLButton* mCloseBtn;
LLButton* mGearBtn;
private:
// Handling selection and contextual menu

View File

@ -49,6 +49,11 @@
/// LLOutboxNotification class
///----------------------------------------------------------------------------
LLNotificationsUI::LLOutboxNotification::LLOutboxNotification()
: LLSystemNotificationHandler("Outbox", "outbox")
{
}
bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLNotificationPtr& notify)
{
LLFloaterOutbox* outbox_floater = LLFloaterReg::getTypedInstance<LLFloaterOutbox>("outbox");
@ -60,10 +65,10 @@ bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLNotifi
void LLNotificationsUI::LLOutboxNotification::onDelete(LLNotificationPtr p)
{
LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler*>(LLNotifications::instance().getChannel("AlertModal").get());
if (sys_handler)
LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast<LLNotificationsUI::LLNotificationHandler*>(LLNotifications::instance().getChannel("AlertModal").get());
if (notification_handler)
{
sys_handler->onDelete(p);
notification_handler->onDelete(p);
}
}
@ -524,9 +529,9 @@ void LLFloaterOutbox::initializationReportError(U32 status, const LLSD& content)
void LLFloaterOutbox::showNotification(const LLNotificationPtr& notification)
{
LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler*>(LLNotifications::instance().getChannel("AlertModal").get());
llassert(sys_handler);
LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast<LLNotificationsUI::LLNotificationHandler*>(LLNotifications::instance().getChannel("AlertModal").get());
llassert(notification_handler);
sys_handler->processNotification(notification);
notification_handler->processNotification(notification);
}

View File

@ -232,11 +232,9 @@ LLHUDEffect *LLHUDObject::addHUDEffect(const U8 type)
case LL_HUD_EFFECT_LOOKAT:
hud_objectp = new LLHUDEffectLookAt(type);
break;
#ifdef XXX_STINSON_CHUI_REWORK
case LL_HUD_EFFECT_VOICE_VISUALIZER:
hud_objectp = new LLVoiceVisualizer(type);
break;
#endif // XXX_STINSON_CHUI_REWORK
case LL_HUD_EFFECT_POINTAT:
hud_objectp = new LLHUDEffectPointAt(type);
break;

View File

@ -39,8 +39,6 @@
#include "lldrawpool.h" // TODO: eliminate, unused below
#include <list>
#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot
class LLViewerCamera;
class LLFontGL;
class LLFace;
@ -96,9 +94,7 @@ public:
LL_HUD_EFFECT_EDIT,
LL_HUD_EFFECT_LOOKAT,
LL_HUD_EFFECT_POINTAT,
#ifdef XXX_STINSON_CHUI_REWORK
LL_HUD_EFFECT_VOICE_VISUALIZER, // Ventrella
#endif // XXX_STINSON_CHUI_REWORK
LL_HUD_NAME_TAG,
LL_HUD_EFFECT_BLOB
};

View File

@ -38,7 +38,7 @@ using namespace LLNotificationsUI;
//--------------------------------------------------------------------------
LLIMHandler::LLIMHandler()
: LLSysHandler("IM Notifications", "notifytoast")
: LLCommunicationNotificationHandler("IM Notifications", "notifytoast")
{
// Getting a Channel for our notifications
mChannel = LLChannelManager::getInstance()->createNotificationChannel()->getHandle();

View File

@ -122,7 +122,7 @@ void on_new_message(const LLSD& msg)
LLUUID session_id = msg["session_id"].asUUID();
LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
// determine action for this session
// determine action for this session
if (session_id.isNull())
{
@ -148,8 +148,8 @@ void on_new_message(const LLSD& msg)
action = gSavedSettings.getString("NotificationGroupChatOptions");
}
// do not show notification in "do not disturb" mode or it goes from agent
if (gAgent.isDoNotDisturb() || gAgent.getID() == participant_id)
// do not show notification which goes from agent
if (gAgent.getID() == participant_id)
{
return;
}
@ -160,67 +160,69 @@ void on_new_message(const LLSD& msg)
LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id);
//session floater not focused (visible or not)
bool sessionFloaterNotFocused = session_floater && !session_floater->hasFocus();
bool session_floater_not_focused = session_floater && !session_floater->hasFocus();
//conv. floater is closed
bool conversation_floater_is_closed =
!( im_box
&& im_box->isInVisibleChain()
&& !im_box->isMinimized());
//conversation floater not focused (visible or not)
bool conversationFloaterNotFocused = im_box && !im_box->hasFocus();
bool conversation_floater_not_focused =
conversation_floater_is_closed || !im_box->hasFocus();
if ("toast" == action)
{
// Skip toasting if we have open window of IM with this session id
if (
session_floater
// Skip toasting and flashing if we have open window of IM with this session id
if (session_floater
&& session_floater->isInVisibleChain()
&& session_floater->hasFocus()
&& !session_floater->isMinimized()
&& !(session_floater->getHost()
&& session_floater->getHost()->isMinimized())
&& !(session_floater->getHost() && session_floater->getHost()->isMinimized())
)
{
return;
}
// Skip toasting for system messages and for nearby chat
if (participant_id.isNull())
{
return;
}
//User is not focused on conversation containing the message
if(sessionFloaterNotFocused)
if(session_floater_not_focused)
{
im_box->flashConversationItemWidget(session_id, true);
//The conversation floater isn't focused/open
if(conversationFloaterNotFocused)
if(conversation_floater_not_focused)
{
gToolBarView->flashCommand(LLCommandId("chat"), true);
//Show IM toasts (upper right toasts)
if(session_id.notNull())
// Skip toasting for system messages and for nearby chat
if(session_id.notNull() && participant_id.notNull())
{
LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
}
}
}
}
else if ("flash" == action)
{
//User is not focused on conversation containing the message
if(sessionFloaterNotFocused && conversationFloaterNotFocused)
{
gToolBarView->flashCommand(LLCommandId("chat"), true);
}
//conversation floater is open but a different conversation is focused
else if(sessionFloaterNotFocused)
{
if (conversation_floater_not_focused)
{
if(session_floater_not_focused)
{
//User is not focused on conversation containing the message
gToolBarView->flashCommand(LLCommandId("chat"), true);
}
im_box->flashConversationItemWidget(session_id, true);
}
}
else if("openconversations" == action)
{
//User is not focused on conversation containing the message
if(sessionFloaterNotFocused)
if(session_floater_not_focused)
{
//Flash line item
im_box->flashConversationItemWidget(session_id, true);
@ -863,7 +865,8 @@ bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from,
bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)
{
if (gSavedPerAccountSettings.getBOOL("LogInstantMessages"))
if (gSavedPerAccountSettings.getBOOL("LogInstantMessages")
&& gSavedSettings.getBOOL("KeepConversationLogTranscripts"))
{
std::string from_name = from;
@ -960,7 +963,7 @@ const std::string LLIMModel::getName(const LLUUID& session_id) const
{
LLIMSession* session = findIMSession(session_id);
if (!session)
if (!session)
{
llwarns << "session " << session_id << "does not exist " << llendl;
return LLTrans::getString("no_session_message");
@ -2487,17 +2490,24 @@ void LLIMMgr::addMessage(
new_session_id = computeSessionID(dialog, other_participant_id);
}
// Open conversation log if offline messages are present
// Open conversation log if offline messages are present and user allows a Call Log
if (is_offline_msg)
{
LLFloaterConversationLog* floater_log =
LLFloaterReg::getTypedInstance<LLFloaterConversationLog>("conversation");
if (floater_log && !(floater_log->isFrontmost()))
{
if (gSavedSettings.getBOOL("KeepConversationLogTranscripts"))
{
floater_log->openFloater();
floater_log->setFrontmost(TRUE);
LLFloaterConversationLog* floater_log =
LLFloaterReg::getTypedInstance<LLFloaterConversationLog>("conversation");
if (floater_log && !(floater_log->isFrontmost()))
{
floater_log->openFloater();
floater_log->setFrontmost(TRUE);
}
}
}
else
{
gToolBarView->flashCommand(LLCommandId("chat"), true);
}
}
//*NOTE session_name is empty in case of incoming P2P sessions
std::string fixed_session_name = from;
@ -2552,7 +2562,7 @@ void LLIMMgr::addMessage(
}
//Play sound for new conversations
if(gSavedSettings.getBOOL("PlaySoundNewConversation") == TRUE)
if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation") == TRUE))
{
make_ui_sound("UISndNewIncomingIMSession");
}
@ -2708,12 +2718,13 @@ LLUUID LLIMMgr::addSession(
{
LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(floater_id);
if (im_floater && im_floater->getStartConferenceInSameFloater())
if (im_floater)
{
// The IM floater should be initialized with a new session_id
// so that it is found by that id when creating a chiclet in LLFloaterIMSession::onIMChicletCreated,
// and a new floater is not created.
im_floater->initIMSession(session_id);
im_floater->reloadMessages();
}
}

View File

@ -29,6 +29,7 @@
#include "llnotificationhandler.h"
#include "llagentcamera.h"
#include "llnotifications.h"
#include "llprogressview.h"
#include "lltoastnotifypanel.h"
@ -41,7 +42,7 @@ using namespace LLNotificationsUI;
//--------------------------------------------------------------------------
LLAlertHandler::LLAlertHandler(const std::string& name, const std::string& notification_type, bool is_modal)
: LLSysHandler(name, notification_type),
: LLSystemNotificationHandler(name, notification_type),
mIsModal(is_modal)
{
LLScreenChannelBase::Params p;
@ -123,3 +124,28 @@ void LLAlertHandler::onChange( LLNotificationPtr notification )
if(channel)
channel->modifyToastByNotificationID(notification->getID(), (LLToastPanel*)alert_dialog);
}
//--------------------------------------------------------------------------
LLViewerAlertHandler::LLViewerAlertHandler(const std::string& name, const std::string& notification_type)
: LLSystemNotificationHandler(name, notification_type)
{
}
bool LLViewerAlertHandler::processNotification(const LLNotificationPtr& p)
{
if (gHeadlessClient)
{
LL_INFOS("LLViewerAlertHandler") << "Alert: " << p->getName() << LL_ENDL;
}
// If we're in mouselook, the mouse is hidden and so the user can't click
// the dialog buttons. In that case, change to First Person instead.
if( gAgentCamera.cameraMouselook() )
{
gAgentCamera.changeCameraToDefault();
}
return false;
}

View File

@ -38,7 +38,7 @@ using namespace LLNotificationsUI;
//--------------------------------------------------------------------------
LLGroupHandler::LLGroupHandler()
: LLSysHandler("Group Notifications", "groupnotify")
: LLCommunicationNotificationHandler("Group Notifications", "groupnotify")
{
// Getting a Channel for our notifications
LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel();

View File

@ -27,6 +27,7 @@
#ifndef LL_LLNOTIFICATIONHANDLER_H
#define LL_LLNOTIFICATIONHANDLER_H
#include <boost/intrusive_ptr.hpp>
#include "llwindow.h"
@ -86,22 +87,37 @@ protected:
/**
* Handler for system notifications.
*/
class LLSysHandler : public LLEventHandler, public LLNotificationChannel
class LLNotificationHandler : public LLEventHandler, public LLNotificationChannel
{
public:
LLSysHandler(const std::string& name, const std::string& notification_type);
virtual ~LLSysHandler() {};
LLNotificationHandler(const std::string& name, const std::string& notification_type, const std::string& parentName);
virtual ~LLNotificationHandler() {};
// base interface functions
/*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); }
/*virtual*/ void onLoad(LLNotificationPtr p) { processNotification(p); }
/*virtual*/ void onDelete(LLNotificationPtr p) { if (mChannel.get()) mChannel.get()->removeToastByNotificationID(p->getID());}
virtual void onAdd(LLNotificationPtr p) { processNotification(p); }
virtual void onChange(LLNotificationPtr p) { processNotification(p); }
virtual void onLoad(LLNotificationPtr p) { processNotification(p); }
virtual void onDelete(LLNotificationPtr p) { if (mChannel.get()) mChannel.get()->removeToastByNotificationID(p->getID());}
virtual bool processNotification(const LLNotificationPtr& notify)=0;
virtual bool processNotification(const LLNotificationPtr& notify) = 0;
};
class LLSystemNotificationHandler : public LLNotificationHandler
{
public:
LLSystemNotificationHandler(const std::string& name, const std::string& notification_type);
virtual ~LLSystemNotificationHandler() {};
};
class LLCommunicationNotificationHandler : public LLNotificationHandler
{
public:
LLCommunicationNotificationHandler(const std::string& name, const std::string& notification_type);
virtual ~LLCommunicationNotificationHandler() {};
};
/**
* Handler for chat message notifications.
* Handler for chat message notifications.
*/
class LLChatHandler : public LLEventHandler
{
@ -115,67 +131,62 @@ public:
* Handler for IM notifications.
* It manages life time of IMs, group messages.
*/
class LLIMHandler : public LLSysHandler
class LLIMHandler : public LLCommunicationNotificationHandler
{
public:
LLIMHandler();
virtual ~LLIMHandler();
bool processNotification(const LLNotificationPtr& p);
protected:
bool processNotification(const LLNotificationPtr& p);
/*virtual*/ void initChannel();
virtual void initChannel();
};
/**
* Handler for system informational notices.
* It manages life time of tip notices.
*/
class LLTipHandler : public LLSysHandler
class LLTipHandler : public LLSystemNotificationHandler
{
public:
LLTipHandler();
virtual ~LLTipHandler();
// base interface functions
/*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); }
/*virtual*/ bool processNotification(const LLNotificationPtr& p);
virtual bool processNotification(const LLNotificationPtr& p);
protected:
/*virtual*/ void initChannel();
virtual void initChannel();
};
/**
* Handler for system informational notices.
* It manages life time of script notices.
*/
class LLScriptHandler : public LLSysHandler
class LLScriptHandler : public LLSystemNotificationHandler
{
public:
LLScriptHandler();
virtual ~LLScriptHandler();
/*virtual*/ void onDelete(LLNotificationPtr p);
// base interface functions
/*virtual*/ bool processNotification(const LLNotificationPtr& p);
virtual void onDelete(LLNotificationPtr p);
virtual bool processNotification(const LLNotificationPtr& p);
protected:
/*virtual*/ void onDeleteToast(LLToast* toast);
/*virtual*/ void initChannel();
virtual void onDeleteToast(LLToast* toast);
virtual void initChannel();
};
/**
* Handler for group system notices.
*/
class LLGroupHandler : public LLSysHandler
class LLGroupHandler : public LLCommunicationNotificationHandler
{
public:
LLGroupHandler();
virtual ~LLGroupHandler();
// base interface functions
/*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); }
/*virtual*/ bool processNotification(const LLNotificationPtr& p);
virtual bool processNotification(const LLNotificationPtr& p);
protected:
virtual void initChannel();
@ -184,15 +195,14 @@ protected:
/**
* Handler for alert system notices.
*/
class LLAlertHandler : public LLSysHandler
class LLAlertHandler : public LLSystemNotificationHandler
{
public:
LLAlertHandler(const std::string& name, const std::string& notification_type, bool is_modal);
virtual ~LLAlertHandler();
/*virtual*/ void onChange(LLNotificationPtr p);
/*virtual*/ void onLoad(LLNotificationPtr p) { processNotification(p); }
/*virtual*/ bool processNotification(const LLNotificationPtr& p);
virtual void onChange(LLNotificationPtr p);
virtual bool processNotification(const LLNotificationPtr& p);
protected:
virtual void initChannel();
@ -200,67 +210,85 @@ protected:
bool mIsModal;
};
class LLViewerAlertHandler : public LLSystemNotificationHandler
{
LOG_CLASS(LLViewerAlertHandler);
public:
LLViewerAlertHandler(const std::string& name, const std::string& notification_type);
virtual ~LLViewerAlertHandler() {};
virtual void onDelete(LLNotificationPtr p) {};
virtual bool processNotification(const LLNotificationPtr& p);
protected:
virtual void initChannel() {};
};
/**
* Handler for offers notices.
* It manages life time of offer notices.
*/
class LLOfferHandler : public LLSysHandler
class LLOfferHandler : public LLCommunicationNotificationHandler
{
public:
LLOfferHandler();
virtual ~LLOfferHandler();
// base interface functions
/*virtual*/ void onChange(LLNotificationPtr p);
/*virtual*/ void onDelete(LLNotificationPtr notification);
/*virtual*/ bool processNotification(const LLNotificationPtr& p);
virtual void onChange(LLNotificationPtr p);
virtual void onDelete(LLNotificationPtr notification);
virtual bool processNotification(const LLNotificationPtr& p);
protected:
/*virtual*/ void initChannel();
virtual void initChannel();
};
/**
* Handler for UI hints.
*/
class LLHintHandler : public LLNotificationChannel
class LLHintHandler : public LLSystemNotificationHandler
{
public:
LLHintHandler() : LLNotificationChannel("Hints", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "hint"))
{}
LLHintHandler();
virtual ~LLHintHandler() {}
/*virtual*/ void onAdd(LLNotificationPtr p);
/*virtual*/ void onLoad(LLNotificationPtr p);
/*virtual*/ void onDelete(LLNotificationPtr p);
virtual void onAdd(LLNotificationPtr p);
virtual void onLoad(LLNotificationPtr p);
virtual void onDelete(LLNotificationPtr p);
virtual bool processNotification(const LLNotificationPtr& p);
protected:
virtual void initChannel() {};
};
/**
* Handler for browser notifications
*/
class LLBrowserNotification : public LLNotificationChannel
class LLBrowserNotification : public LLSystemNotificationHandler
{
public:
LLBrowserNotification()
: LLNotificationChannel("Browser", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "browser"))
{}
/*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); }
/*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); }
bool processNotification(const LLNotificationPtr& p);
LLBrowserNotification();
virtual ~LLBrowserNotification() {}
virtual bool processNotification(const LLNotificationPtr& p);
protected:
virtual void initChannel() {};
};
/**
* Handler for outbox notifications
*/
class LLOutboxNotification : public LLNotificationChannel
class LLOutboxNotification : public LLSystemNotificationHandler
{
public:
LLOutboxNotification()
: LLNotificationChannel("Outbox", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "outbox"))
{}
/*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); }
/*virtual*/ void onChange(LLNotificationPtr p) { }
/*virtual*/ void onDelete(LLNotificationPtr p);
bool processNotification(const LLNotificationPtr& p);
LLOutboxNotification();
virtual ~LLOutboxNotification() {};
virtual void onChange(LLNotificationPtr p) { }
virtual void onDelete(LLNotificationPtr p);
virtual bool processNotification(const LLNotificationPtr& p);
protected:
virtual void initChannel() {};
};
class LLHandlerUtil

View File

@ -41,8 +41,16 @@
using namespace LLNotificationsUI;
LLSysHandler::LLSysHandler(const std::string& name, const std::string& notification_type)
: LLNotificationChannel(name, "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, notification_type))
LLNotificationHandler::LLNotificationHandler(const std::string& name, const std::string& notification_type, const std::string& parentName)
: LLNotificationChannel(name, parentName, LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, notification_type))
{}
LLSystemNotificationHandler::LLSystemNotificationHandler(const std::string& name, const std::string& notification_type)
: LLNotificationHandler(name, notification_type, "System")
{}
LLCommunicationNotificationHandler::LLCommunicationNotificationHandler(const std::string& name, const std::string& notification_type)
: LLNotificationHandler(name, notification_type, "Communication")
{}
// static

View File

@ -33,6 +33,27 @@
using namespace LLNotificationsUI;
void LLHintHandler::onAdd(LLNotificationPtr p) { LLHints::show(p); }
void LLHintHandler::onLoad(LLNotificationPtr p) { LLHints::show(p); }
void LLHintHandler::onDelete(LLNotificationPtr p) { LLHints::hide(p); }
LLHintHandler::LLHintHandler()
: LLSystemNotificationHandler("Hints", "hint")
{
}
void LLHintHandler::onAdd(LLNotificationPtr p)
{
LLHints::show(p);
}
void LLHintHandler::onLoad(LLNotificationPtr p)
{
LLHints::show(p);
}
void LLHintHandler::onDelete(LLNotificationPtr p)
{
LLHints::hide(p);
}
bool LLHintHandler::processNotification(const LLNotificationPtr& p)
{
return false;
}

View File

@ -41,7 +41,7 @@ using namespace LLNotificationsUI;
//--------------------------------------------------------------------------
LLOfferHandler::LLOfferHandler()
: LLSysHandler("Offer", "offer")
: LLCommunicationNotificationHandler("Offer", "offer")
{
// Getting a Channel for our notifications
LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel();

View File

@ -39,7 +39,7 @@ using namespace LLNotificationsUI;
//--------------------------------------------------------------------------
LLScriptHandler::LLScriptHandler()
: LLSysHandler("Notifications", "notify")
: LLSystemNotificationHandler("Notifications", "notify")
{
// Getting a Channel for our notifications
LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel();

View File

@ -25,207 +25,143 @@
*/
#include "llviewerprecompiledheaders.h" // must be first include
#include "llnotificationstorage.h"
#include "llxmlnode.h" // for linux compilers
#include <string>
#include <map>
#include "llchannelmanager.h"
#include "llscreenchannel.h"
#include "llscriptfloater.h"
#include "llerror.h"
#include "llfile.h"
#include "llnotifications.h"
#include "llpointer.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llsingleton.h"
#include "llviewermessage.h"
//////////////////////////////////////////////////////////////////////////
class LLResponderRegistry
class LLResponderRegistry : public LLSingleton<LLResponderRegistry>
{
public:
static void registerResponders();
static LLNotificationResponderInterface* createResponder(const std::string& notification_name, const LLSD& params);
LLResponderRegistry();
~LLResponderRegistry();
LLNotificationResponderInterface* createResponder(const std::string& pNotificationName, const LLSD& pParams);
protected:
private:
template<typename RESPONDER_TYPE>
static LLNotificationResponderInterface* create(const LLSD& params)
{
RESPONDER_TYPE* responder = new RESPONDER_TYPE();
responder->fromLLSD(params);
return responder;
}
template<typename RESPONDER_TYPE> static LLNotificationResponderInterface* create(const LLSD& pParams);
typedef boost::function<LLNotificationResponderInterface* (const LLSD& params)> responder_constructor_t;
static void add(const std::string& notification_name, const responder_constructor_t& ctr);
private:
void add(const std::string& pNotificationName, const responder_constructor_t& pConstructor);
typedef std::map<std::string, responder_constructor_t> build_map_t;
static build_map_t sBuildMap;
build_map_t mBuildMap;
};
//////////////////////////////////////////////////////////////////////////
LLPersistentNotificationStorage::LLPersistentNotificationStorage()
LLNotificationStorage::LLNotificationStorage(std::string pFileName)
: mFileName(pFileName)
{
mFileName = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
}
bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload)
LLNotificationStorage::~LLNotificationStorage()
{
// we ignore "load" messages, but rewrite the persistence file on any other
const std::string sigtype = payload["sigtype"].asString();
if ("load" != sigtype)
{
saveNotifications();
}
return false;
}
static LLFastTimer::DeclareTimer FTM_SAVE_NOTIFICATIONS("Save Notifications");
void LLPersistentNotificationStorage::saveNotifications()
bool LLNotificationStorage::writeNotifications(const LLSD& pNotificationData) const
{
LLFastTimer _(FTM_SAVE_NOTIFICATIONS);
llofstream notify_file(mFileName.c_str());
if (!notify_file.is_open())
llofstream notifyFile(mFileName.c_str());
bool didFileOpen = notifyFile.is_open();
if (!didFileOpen)
{
llwarns << "Failed to open " << mFileName << llendl;
return;
LL_WARNS("LLNotificationStorage") << "Failed to open file '" << mFileName << "'" << LL_ENDL;
}
else
{
LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
formatter->format(pNotificationData, notifyFile, LLSDFormatter::OPTIONS_PRETTY);
}
LLSD output;
LLSD& data = output["data"];
return didFileOpen;
}
boost::intrusive_ptr<LLPersistentNotificationChannel> history_channel = boost::dynamic_pointer_cast<LLPersistentNotificationChannel>(LLNotifications::instance().getChannel("Persistent"));
if (!history_channel)
bool LLNotificationStorage::readNotifications(LLSD& pNotificationData) const
{
bool didFileRead;
pNotificationData.clear();
llifstream notifyFile(mFileName.c_str());
didFileRead = notifyFile.is_open();
if (!didFileRead)
{
return;
LL_WARNS("LLNotificationStorage") << "Failed to open file '" << mFileName << "'" << LL_ENDL;
}
for ( std::vector<LLNotificationPtr>::iterator it = history_channel->beginHistory(), end_it = history_channel->endHistory();
it != end_it;
++it)
else
{
LLNotificationPtr notification = *it;
// After a notification was placed in Persist channel, it can become
// responded, expired or canceled - in this case we are should not save it
if(notification->isRespondedTo() || notification->isCancelled()
|| notification->isExpired())
LLPointer<LLSDParser> parser = new LLSDXMLParser();
didFileRead = (parser->parse(notifyFile, pNotificationData, LLSDSerialize::SIZE_UNLIMITED) >= 0);
if (!didFileRead)
{
continue;
}
data.append(notification->asLLSD());
}
LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
}
static LLFastTimer::DeclareTimer FTM_LOAD_NOTIFICATIONS("Load Notifications");
void LLPersistentNotificationStorage::loadNotifications()
{
LLFastTimer _(FTM_LOAD_NOTIFICATIONS);
LLResponderRegistry::registerResponders();
LLNotifications::instance().getChannel("Persistent")->
connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1));
llifstream notify_file(mFileName.c_str());
if (!notify_file.is_open())
{
llwarns << "Failed to open " << mFileName << llendl;
return;
}
LLSD input;
LLPointer<LLSDParser> parser = new LLSDXMLParser();
if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
{
llwarns << "Failed to parse open notifications" << llendl;
return;
}
if (input.isUndefined())
{
return;
}
LLSD& data = input["data"];
if (data.isUndefined())
{
return;
}
using namespace LLNotificationsUI;
LLScreenChannel* notification_channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()->
findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
LLNotifications& instance = LLNotifications::instance();
for (LLSD::array_const_iterator notification_it = data.beginArray();
notification_it != data.endArray();
++notification_it)
{
LLSD notification_params = *notification_it;
LLNotificationPtr notification(new LLNotification(notification_params));
LLNotificationResponderPtr responder(LLResponderRegistry::
createResponder(notification_params["name"], notification_params["responder"]));
notification->setResponseFunctor(responder);
instance.add(notification);
// hide script floaters so they don't confuse the user and don't overlap startup toast
LLScriptFloaterManager::getInstance()->setFloaterVisible(notification->getID(), false);
if(notification_channel)
{
// hide saved toasts so they don't confuse the user
notification_channel->hideToast(notification->getID());
LL_WARNS("LLNotificationStorage") << "Failed to parse open notifications from file '" << mFileName
<< "'" << LL_ENDL;
}
}
return didFileRead;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
LLResponderRegistry::build_map_t LLResponderRegistry::sBuildMap;
void LLResponderRegistry::registerResponders()
LLNotificationResponderInterface* LLNotificationStorage::createResponder(const std::string& pNotificationName, const LLSD& pParams) const
{
sBuildMap.clear();
return LLResponderRegistry::getInstance()->createResponder(pNotificationName, pParams);
}
LLResponderRegistry::LLResponderRegistry()
: LLSingleton<LLResponderRegistry>()
, mBuildMap()
{
add("ObjectGiveItem", &create<LLOfferInfo>);
add("OwnObjectGiveItem", &create<LLOfferInfo>);
add("UserGiveItem", &create<LLOfferInfo>);
add("TeleportOffered", &create<LLOfferInfo>);
add("TeleportOffered_MaturityExceeded", &create<LLOfferInfo>);
add("OfferFriendship", &create<LLOfferInfo>);
}
LLNotificationResponderInterface* LLResponderRegistry::createResponder(const std::string& notification_name, const LLSD& params)
LLResponderRegistry::~LLResponderRegistry()
{
build_map_t::const_iterator it = sBuildMap.find(notification_name);
if(sBuildMap.end() == it)
}
LLNotificationResponderInterface* LLResponderRegistry::createResponder(const std::string& pNotificationName, const LLSD& pParams)
{
build_map_t::const_iterator it = mBuildMap.find(pNotificationName);
if(mBuildMap.end() == it)
{
return NULL;
}
responder_constructor_t ctr = it->second;
return ctr(params);
return ctr(pParams);
}
void LLResponderRegistry::add(const std::string& notification_name, const responder_constructor_t& ctr)
template<typename RESPONDER_TYPE> LLNotificationResponderInterface* LLResponderRegistry::create(const LLSD& pParams)
{
if(sBuildMap.find(notification_name) != sBuildMap.end())
{
llwarns << "Responder is already registered : " << notification_name << llendl;
llassert(!"Responder already registered");
}
sBuildMap[notification_name] = ctr;
RESPONDER_TYPE* responder = new RESPONDER_TYPE();
responder->fromLLSD(pParams);
return responder;
}
void LLResponderRegistry::add(const std::string& pNotificationName, const responder_constructor_t& pConstructor)
{
if (mBuildMap.find(pNotificationName) != mBuildMap.end())
{
LL_ERRS("LLResponderRegistry") << "Responder is already registered : " << pNotificationName << LL_ENDL;
}
mBuildMap.insert(std::make_pair<std::string, responder_constructor_t>(pNotificationName, pConstructor));
}
// EOF

View File

@ -27,32 +27,27 @@
#ifndef LL_NOTIFICATIONSTORAGE_H
#define LL_NOTIFICATIONSTORAGE_H
#include "llnotifications.h"
#include <string>
// Class that saves not responded(unread) notifications.
// Unread notifications are saved in open_notifications.xml in SL account folder
//
// Notifications that should be saved(if unread) are marked with persist="true" in notifications.xml
// Notifications using functor responders are saved automatically (see llviewermessage.cpp
// lure_callback_reg for example).
// Notifications using object responders(LLOfferInfo) need additional tuning. Responder object should
// be a) serializable(implement LLNotificationResponderInterface),
// b) registered with LLResponderRegistry (found in llnotificationstorage.cpp).
class LLPersistentNotificationStorage : public LLSingleton<LLPersistentNotificationStorage>
#include "llerror.h"
class LLNotificationResponderInterface;
class LLSD;
class LLNotificationStorage
{
LOG_CLASS(LLPersistentNotificationStorage);
LOG_CLASS(LLNotificationStorage);
public:
LLNotificationStorage(std::string pFileName);
~LLNotificationStorage();
LLPersistentNotificationStorage();
protected:
bool writeNotifications(const LLSD& pNotificationData) const;
bool readNotifications(LLSD& pNotificationData) const;
void saveNotifications();
void loadNotifications();
LLNotificationResponderInterface* createResponder(const std::string& pNotificationName, const LLSD& pParams) const;
private:
bool onPersistentChannelChanged(const LLSD& payload);
std::string mFileName;
};

View File

@ -42,7 +42,7 @@ using namespace LLNotificationsUI;
//--------------------------------------------------------------------------
LLTipHandler::LLTipHandler()
: LLSysHandler("NotificationTips", "notifytip")
: LLSystemNotificationHandler("NotificationTips", "notifytip")
{
// Getting a Channel for our notifications
LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel();

View File

@ -137,6 +137,7 @@ void LLPanelBlockedList::updateButtons()
{
bool hasSelected = NULL != mBlockedList->getSelectedItem();
getChildView("unblock_btn")->setEnabled(hasSelected);
getChildView("blocked_gear_btn")->setEnabled(hasSelected);
}
void LLPanelBlockedList::unblockItem()

View File

@ -0,0 +1,145 @@
/**
* @file llpersistentnotificationstorage.cpp
* @brief Implementation of llpersistentnotificationstorage
* @author Stinson@lindenlab.com
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llpersistentnotificationstorage.h"
#include "llchannelmanager.h"
#include "llnotificationstorage.h"
#include "llscreenchannel.h"
#include "llscriptfloater.h"
#include "llviewermessage.h"
LLPersistentNotificationStorage::LLPersistentNotificationStorage()
: LLSingleton<LLPersistentNotificationStorage>()
, LLNotificationStorage(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml"))
{
}
LLPersistentNotificationStorage::~LLPersistentNotificationStorage()
{
}
static LLFastTimer::DeclareTimer FTM_SAVE_NOTIFICATIONS("Save Notifications");
void LLPersistentNotificationStorage::saveNotifications()
{
LLFastTimer _(FTM_SAVE_NOTIFICATIONS);
boost::intrusive_ptr<LLPersistentNotificationChannel> history_channel = boost::dynamic_pointer_cast<LLPersistentNotificationChannel>(LLNotifications::instance().getChannel("Persistent"));
if (!history_channel)
{
return;
}
LLSD output = LLSD::emptyMap();
LLSD& data = output["data"];
for ( std::vector<LLNotificationPtr>::iterator it = history_channel->beginHistory(), end_it = history_channel->endHistory();
it != end_it;
++it)
{
LLNotificationPtr notification = *it;
// After a notification was placed in Persist channel, it can become
// responded, expired or canceled - in this case we are should not save it
if(notification->isRespondedTo() || notification->isCancelled()
|| notification->isExpired())
{
continue;
}
data.append(notification->asLLSD());
}
writeNotifications(output);
}
static LLFastTimer::DeclareTimer FTM_LOAD_NOTIFICATIONS("Load Notifications");
void LLPersistentNotificationStorage::loadNotifications()
{
LLFastTimer _(FTM_LOAD_NOTIFICATIONS);
LLNotifications::instance().getChannel("Persistent")->
connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1));
LLSD input;
if (!readNotifications(input) ||input.isUndefined())
{
return;
}
LLSD& data = input["data"];
if (data.isUndefined())
{
return;
}
using namespace LLNotificationsUI;
LLScreenChannel* notification_channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()->
findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
LLNotifications& instance = LLNotifications::instance();
for (LLSD::array_const_iterator notification_it = data.beginArray();
notification_it != data.endArray();
++notification_it)
{
LLSD notification_params = *notification_it;
LLNotificationPtr notification(new LLNotification(notification_params));
LLNotificationResponderPtr responder(createResponder(notification_params["name"], notification_params["responder"]));
notification->setResponseFunctor(responder);
instance.add(notification);
// hide script floaters so they don't confuse the user and don't overlap startup toast
LLScriptFloaterManager::getInstance()->setFloaterVisible(notification->getID(), false);
if(notification_channel)
{
// hide saved toasts so they don't confuse the user
notification_channel->hideToast(notification->getID());
}
}
}
bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload)
{
// we ignore "load" messages, but rewrite the persistence file on any other
const std::string sigtype = payload["sigtype"].asString();
if ("load" != sigtype)
{
saveNotifications();
}
return false;
}
// EOF

View File

@ -0,0 +1,63 @@
/**
* @file llpersistentnotificationstorage.h
* @brief Header file for llpersistentnotificationstorage
* @author Stinson@lindenlab.com
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLPERSISTENTNOTIFICATIONSTORAGE_H
#define LL_LLPERSISTENTNOTIFICATIONSTORAGE_H
#include "llerror.h"
#include "llnotificationstorage.h"
#include "llsingleton.h"
class LLSD;
// Class that saves not responded(unread) notifications.
// Unread notifications are saved in open_notifications.xml in SL account folder
//
// Notifications that should be saved(if unread) are marked with persist="true" in notifications.xml
// Notifications using functor responders are saved automatically (see llviewermessage.cpp
// lure_callback_reg for example).
// Notifications using object responders(LLOfferInfo) need additional tuning. Responder object should
// be a) serializable(implement LLNotificationResponderInterface),
// b) registered with LLResponderRegistry (found in llpersistentnotificationstorage.cpp).
class LLPersistentNotificationStorage : public LLSingleton<LLPersistentNotificationStorage>, public LLNotificationStorage
{
LOG_CLASS(LLPersistentNotificationStorage);
public:
LLPersistentNotificationStorage();
~LLPersistentNotificationStorage();
void saveNotifications();
void loadNotifications();
protected:
private:
bool onPersistentChannelChanged(const LLSD& payload);
};
#endif // LL_LLPERSISTENTNOTIFICATIONSTORAGE_H

View File

@ -40,6 +40,7 @@
LLSysWellWindow::LLSysWellWindow(const LLSD& key) : LLTransientDockableFloater(NULL, true, key),
mChannel(NULL),
mMessageList(NULL),
mSysWellChiclet(NULL),
NOTIFICATION_WELL_ANCHOR_NAME("notification_well_panel"),
IM_WELL_ANCHOR_NAME("im_well_panel"),
mIsReshapedByUser(false)
@ -79,6 +80,15 @@ void LLSysWellWindow::onStartUpToastClick(S32 x, S32 y, MASK mask)
setVisible(TRUE);
}
void LLSysWellWindow::setSysWellChiclet(LLSysWellChiclet* chiclet)
{
mSysWellChiclet = chiclet;
if(NULL != mSysWellChiclet)
{
mSysWellChiclet->updateWidget(isWindowEmpty());
}
}
//---------------------------------------------------------------------------------
LLSysWellWindow::~LLSysWellWindow()
{
@ -89,6 +99,10 @@ void LLSysWellWindow::removeItemByID(const LLUUID& id)
{
if(mMessageList->removeItemByValue(id))
{
if (NULL != mSysWellChiclet)
{
mSysWellChiclet->updateWidget(isWindowEmpty());
}
reshapeWindow();
}
else
@ -334,6 +348,7 @@ void LLNotificationWellWindow::addItem(LLSysWellItem::Params p)
LLSysWellItem* new_item = new LLSysWellItem(p);
if (mMessageList->addItem(new_item, value, ADD_TOP))
{
mSysWellChiclet->updateWidget(isWindowEmpty());
reshapeWindow();
new_item->setOnItemCloseCallback(boost::bind(&LLNotificationWellWindow::onItemClose, this, _1));
new_item->setOnItemClickCallback(boost::bind(&LLNotificationWellWindow::onItemClick, this, _1));

View File

@ -66,6 +66,8 @@ public:
void onStartUpToastClick(S32 x, S32 y, MASK mask);
void setSysWellChiclet(LLSysWellChiclet* chiclet);
// size constants for the window and for its elements
static const S32 MAX_WINDOW_HEIGHT = 200;
static const S32 MIN_WINDOW_WIDTH = 318;
@ -84,6 +86,11 @@ protected:
LLNotificationsUI::LLScreenChannel* mChannel;
LLFlatListView* mMessageList;
/**
* Reference to an appropriate Well chiclet to release "new message" state. EXT-3147
*/
LLSysWellChiclet* mSysWellChiclet;
bool mIsReshapedByUser;
};

View File

@ -7586,6 +7586,20 @@ void handle_web_content_test(const LLSD& param)
LLWeb::loadURLInternal(url);
}
void handle_show_url(const LLSD& param)
{
std::string url = param.asString();
if(gSavedSettings.getBOOL("UseExternalBrowser"))
{
LLWeb::loadURLExternal(url);
}
else
{
LLWeb::loadURLInternal(url);
}
}
void handle_buy_currency_test(void*)
{
std::string url =
@ -8415,6 +8429,7 @@ void initialize_menus()
// Advanced > UI
commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2)); // sigh! this one opens the MEDIA browser
commit.add("Advanced.WebContentTest", boost::bind(&handle_web_content_test, _2)); // this one opens the Web Content floater
commit.add("Advanced.ShowURL", boost::bind(&handle_show_url, _2));
view_listener_t::addMenu(new LLAdvancedBuyCurrencyTest(), "Advanced.BuyCurrencyTest");
view_listener_t::addMenu(new LLAdvancedDumpSelectMgr(), "Advanced.DumpSelectMgr");
view_listener_t::addMenu(new LLAdvancedDumpInventory(), "Advanced.DumpInventory");

View File

@ -3169,17 +3169,16 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
payload["online"] = (offline == IM_ONLINE);
payload["sender"] = msg->getSender().getIPandPort();
if (is_do_not_disturb)
{
send_do_not_disturb_message(msg, from_id);
LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1);
}
else if (is_muted)
if (is_muted)
{
LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1);
}
else
{
if (is_do_not_disturb)
{
send_do_not_disturb_message(msg, from_id);
}
args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString();
if(message.empty())
{

View File

@ -37,8 +37,10 @@
#include "llagent.h"
#include "llagentcamera.h"
#include "llcommunicationchannel.h"
#include "llfloaterreg.h"
#include "llmeshrepository.h"
#include "llnotificationhandler.h"
#include "llpanellogin.h"
#include "llviewerkeyboard.h"
#include "llviewermenu.h"
@ -127,6 +129,7 @@
#include "llmorphview.h"
#include "llmoveview.h"
#include "llnavigationbar.h"
#include "llnotificationhandler.h"
#include "llpanelpathfindingrebakenavmesh.h"
#include "llpaneltopinfobar.h"
#include "llpopupview.h"
@ -1554,11 +1557,11 @@ LLViewerWindow::LLViewerWindow(const Params& p)
mWindowListener.reset(new LLWindowListener(this, boost::lambda::var(gKeyboard)));
mViewerWindowListener.reset(new LLViewerWindowListener(this));
mAlertsChannel.reset(new LLNotificationChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")));
mModalAlertsChannel.reset(new LLNotificationChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")));
mSystemChannel.reset(new LLNotificationChannel("System", "Visible", LLNotificationFilters::includeEverything));
mCommunicationChannel.reset(new LLCommunicationChannel("Communication", "Visible"));
mAlertsChannel.reset(new LLNotificationsUI::LLViewerAlertHandler("VW_alerts", "alert"));
mModalAlertsChannel.reset(new LLNotificationsUI::LLViewerAlertHandler("VW_alertmodal", "alertmodal"));
mAlertsChannel->connectChanged(&LLViewerWindow::onAlert);
mModalAlertsChannel->connectChanged(&LLViewerWindow::onAlert);
bool ignore = gSavedSettings.getBOOL("IgnoreAllNotifications");
LLNotifications::instance().setIgnoreAllNotifications(ignore);
if (ignore)
@ -5044,25 +5047,6 @@ LLRect LLViewerWindow::getChatConsoleRect()
//----------------------------------------------------------------------------
//static
bool LLViewerWindow::onAlert(const LLSD& notify)
{
LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID());
if (gHeadlessClient)
{
llinfos << "Alert: " << notification->getName() << llendl;
}
// If we're in mouselook, the mouse is hidden and so the user can't click
// the dialog buttons. In that case, change to First Person instead.
if( gAgentCamera.cameraMouselook() )
{
gAgentCamera.changeCameraToDefault();
}
return false;
}
void LLViewerWindow::setUIVisibility(bool visible)
{
mUIVisible = visible;

View File

@ -43,6 +43,7 @@
#include "lltimer.h"
#include "llstat.h"
#include "llmousehandler.h"
#include "llnotifications.h"
#include "llhandle.h"
#include "llinitparam.h"
@ -401,7 +402,6 @@ public:
private:
bool shouldShowToolTipFor(LLMouseHandler *mh);
static bool onAlert(const LLSD& notify);
void switchToolByMask(MASK mask);
void destroyWindow();
@ -418,8 +418,10 @@ private:
bool mActive;
bool mUIVisible;
boost::shared_ptr<class LLNotificationChannel> mAlertsChannel,
mModalAlertsChannel;
LLNotificationChannelPtr mSystemChannel;
LLNotificationChannelPtr mCommunicationChannel;
LLNotificationChannelPtr mAlertsChannel;
LLNotificationChannelPtr mModalAlertsChannel;
LLRect mWindowRectRaw; // whole window, including UI
LLRect mWindowRectScaled; // whole window, scaled by UI size

View File

@ -34,8 +34,6 @@
#include "llvoavatar.h"
#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot
#include <stdio.h>
#include <ctype.h>
@ -711,13 +709,9 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
LLMemType mt(LLMemType::MTYPE_AVATAR);
//VTResume(); // VTune
#ifdef XXX_STINSON_CHUI_REWORK
// mVoiceVisualizer is created by the hud effects manager and uses the HUD Effects pipeline
const BOOL needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job
mVoiceVisualizer = ( LLVoiceVisualizer *)LLHUDManager::getInstance()->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim );
#else // XXX_STINSON_CHUI_REWORK
mVoiceVisualizer = new LLVoiceVisualizer();
#endif // XXX_STINSON_CHUI_REWORK
lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl;
@ -893,11 +887,7 @@ void LLVOAvatar::markDead()
mNameText = NULL;
sNumVisibleChatBubbles--;
}
#ifdef XXX_STINSON_CHUI_REWORK
mVoiceVisualizer->markDead();
#else // XXX_STINSON_CHUI_REWORK
mVoiceVisualizer->setStopSpeaking();
#endif // XXX_STINSON_CHUI_REWORK
LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ;
LLViewerObject::markDead();
}
@ -1429,9 +1419,7 @@ void LLVOAvatar::initInstance(void)
//VTPause(); // VTune
#ifdef XXX_STINSON_CHUI_REWORK
mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) );
#endif // XXX_STINSON_CHUI_REWORK
}
@ -2529,7 +2517,6 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
{
#ifdef XXX_STINSON_CHUI_REWORK
bool render_visualizer = voice_enabled;
// Don't render the user's own voice visualizer when in mouselook, or when opening the mic is disabled.
@ -2542,7 +2529,6 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
}
mVoiceVisualizer->setVoiceEnabled(render_visualizer);
#endif // XXX_STINSON_CHUI_REWORK
if ( voice_enabled )
{
@ -2618,7 +2604,6 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
}
}
#ifdef XXX_STINSON_CHUI_REWORK
//--------------------------------------------------------------------------------------------
// here we get the approximate head position and set as sound source for the voice symbol
// (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing)
@ -2636,7 +2621,6 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
tagPos[VZ] += ( mBodySize[VZ] + 0.125f );
mVoiceVisualizer->setVoiceSourceWorldPosition( tagPos );
}
#endif // XXX_STINSON_CHUI_REWORK
}//if ( voiceEnabled )
}

View File

@ -541,6 +541,7 @@ void LLVoiceClient::setMuteMic(bool muted)
{
mMuteMic = muted;
updateMicMuteLogic();
mMicroChangedSignal();
}

View File

@ -45,7 +45,6 @@
//29de489d-0491-fb00-7dab-f9e686d31e83
#ifdef XXX_STINSON_CHUI_REWORK
//--------------------------------------------------------------------------------------
// sound symbol constants
//--------------------------------------------------------------------------------------
@ -61,7 +60,6 @@ const F32 BASE_BRIGHTNESS = 0.7f; // gray level of the voice indicator when qu
const F32 DOT_SIZE = 0.05f; // size of the dot billboard texture
const F32 DOT_OPACITY = 0.7f; // how opaque the dot is
const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a function of speaking amplitude
#endif // XXX_STINSON_CHUI_REWORK
//--------------------------------------------------------------------------------------
// gesticulation constants
@ -69,13 +67,11 @@ const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a
const F32 DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE = 0.2f;
const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE = 1.0f;
#ifdef XXX_STINSON_CHUI_REWORK
//--------------------------------------------------------------------------------------
// other constants
//--------------------------------------------------------------------------------------
const F32 ONE_HALF = 1.0f; // to clarify intent and reduce magic numbers in the code.
const LLVector3 WORLD_UPWARD_DIRECTION = LLVector3( 0.0f, 0.0f, 1.0f ); // Z is up in SL
#endif // XXX_STINSON_CHUI_REWORK
//------------------------------------------------------------------
// Initialize the statics
@ -98,28 +94,12 @@ F32 LLVoiceVisualizer::sAahPowerTransfersf = 0.0f;
//-----------------------------------------------
// constructor
//-----------------------------------------------
#ifdef XXX_STINSON_CHUI_REWORK
LLVoiceVisualizer::LLVoiceVisualizer( const U8 type )
: LLHUDEffect(type)
#else // XXX_STINSON_CHUI_REWORK
LLVoiceVisualizer::LLVoiceVisualizer()
: LLRefCount(),
mTimer(),
mStartTime(0.0),
mCurrentlySpeaking(false),
mSpeakingAmplitude(0.0f),
mMaxGesticulationAmplitude(DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE),
mMinGesticulationAmplitude(DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE)
#endif // XXX_STINSON_CHUI_REWORK
{
#ifdef XXX_STINSON_CHUI_REWORK
mCurrentTime = mTimer.getTotalSeconds();
mPreviousTime = mCurrentTime;
mStartTime = mCurrentTime;
#else // XXX_STINSON_CHUI_REWORK
mStartTime = mTimer.getTotalSeconds();
#endif // XXX_STINSON_CHUI_REWORK
#ifdef XXX_STINSON_CHUI_REWORK
mVoiceSourceWorldPosition = LLVector3( 0.0f, 0.0f, 0.0f );
mSpeakingAmplitude = 0.0f;
mCurrentlySpeaking = false;
@ -128,11 +108,9 @@ LLVoiceVisualizer::LLVoiceVisualizer()
mMaxGesticulationAmplitude = DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE;
mSoundSymbol.mActive = true;
mSoundSymbol.mPosition = LLVector3( 0.0f, 0.0f, 0.0f );
#endif // XXX_STINSON_CHUI_REWORK
mTimer.reset();
#ifdef XXX_STINSON_CHUI_REWORK
const char* sound_level_img[] =
{
"voice_meter_dot.j2c",
@ -154,7 +132,6 @@ LLVoiceVisualizer::LLVoiceVisualizer()
}
mSoundSymbol.mTexture[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
#endif // XXX_STINSON_CHUI_REWORK
// The first instance loads the initial state from prefs.
if (!sPrefsInitialized)
@ -174,7 +151,6 @@ LLVoiceVisualizer::LLVoiceVisualizer()
}//---------------------------------------------------
#ifdef XXX_STINSON_CHUI_REWORK
//---------------------------------------------------
void LLVoiceVisualizer::setMinGesticulationAmplitude( F32 m )
{
@ -195,16 +171,13 @@ void LLVoiceVisualizer::setVoiceEnabled( bool v )
mVoiceEnabled = v;
}//---------------------------------------------------
#endif // XXX_STINSON_CHUI_REWORK
//---------------------------------------------------
void LLVoiceVisualizer::setStartSpeaking()
{
mStartTime = mTimer.getTotalSeconds();
mCurrentlySpeaking = true;
#ifdef XXX_STINSON_CHUI_REWORK
mSoundSymbol.mActive = true;
#endif // XXX_STINSON_CHUI_REWORK
}//---------------------------------------------------
@ -359,7 +332,6 @@ void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah )
}//---------------------------------------------------
#ifdef XXX_STINSON_CHUI_REWORK
//---------------------------------------------------
// this method is inherited from HUD Effect
//---------------------------------------------------
@ -558,7 +530,6 @@ void LLVoiceVisualizer::setVoiceSourceWorldPosition( const LLVector3 &p )
mVoiceSourceWorldPosition = p;
}//---------------------------------------------------
#endif // XXX_STINSON_CHUI_REWORK
//---------------------------------------------------
VoiceGesticulationLevel LLVoiceVisualizer::getCurrentGesticulationLevel()
@ -589,7 +560,6 @@ LLVoiceVisualizer::~LLVoiceVisualizer()
}//----------------------------------------------
#ifdef XXX_STINSON_CHUI_REWORK
//---------------------------------------------------
// "packData" is inherited from HUDEffect
//---------------------------------------------------
@ -639,5 +609,3 @@ void LLVoiceVisualizer::markDead()
LLHUDEffect::markDead();
}//------------------------------------------------------------------
#endif // XXX_STINSON_CHUI_REWORK

View File

@ -42,12 +42,7 @@
#ifndef LL_VOICE_VISUALIZER_H
#define LL_VOICE_VISUALIZER_H
#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot
#ifdef XXX_STINSON_CHUI_REWORK
#include "llhudeffect.h"
#else // XXX_STINSON_CHUI_REWORK
#include "llpointer.h"
#endif // XXX_STINSON_CHUI_REWORK
//-----------------------------------------------------------------------------------------------
// The values of voice gesticulation represent energy levels for avatar animation, based on
@ -65,45 +60,30 @@ enum VoiceGesticulationLevel
NUM_VOICE_GESTICULATION_LEVELS
};
#ifdef XXX_STINSON_CHUI_REWORK
const static int NUM_VOICE_SYMBOL_WAVES = 7;
#endif // XXX_STINSON_CHUI_REWORK
//----------------------------------------------------
// LLVoiceVisualizer class
//----------------------------------------------------
#ifdef XXX_STINSON_CHUI_REWORK
class LLVoiceVisualizer : public LLHUDEffect
#else // XXX_STINSON_CHUI_REWORK
class LLVoiceVisualizer : public LLRefCount
#endif // XXX_STINSON_CHUI_REWORK
{
//---------------------------------------------------
// public methods
//---------------------------------------------------
public:
#ifdef XXX_STINSON_CHUI_REWORK
LLVoiceVisualizer( const U8 type ); //constructor
#else // XXX_STINSON_CHUI_REWORK
LLVoiceVisualizer(); //constructor
#endif // XXX_STINSON_CHUI_REWORK
~LLVoiceVisualizer(); //destructor
#ifdef XXX_STINSON_CHUI_REWORK
void setVoiceSourceWorldPosition( const LLVector3 &p ); // this should be the position of the speaking avatar's head
void setMinGesticulationAmplitude( F32 ); // the lower range of meaningful amplitude for setting gesticulation level
void setMaxGesticulationAmplitude( F32 ); // the upper range of meaningful amplitude for setting gesticulation level
#endif // XXX_STINSON_CHUI_REWORK
void setStartSpeaking(); // tell me when the av starts speaking
#ifdef XXX_STINSON_CHUI_REWORK
void setVoiceEnabled( bool ); // tell me whether or not the user is voice enabled
#endif // XXX_STINSON_CHUI_REWORK
void setSpeakingAmplitude( F32 ); // tell me how loud the av is speaking (ranges from 0 to 1)
void setStopSpeaking(); // tell me when the av stops speaking
bool getCurrentlySpeaking(); // the get for the above set
VoiceGesticulationLevel getCurrentGesticulationLevel(); // based on voice amplitude, I'll give you the current "energy level" of avatar speech
void lipSyncOohAah( F32& ooh, F32& aah );
#ifdef XXX_STINSON_CHUI_REWORK
void render(); // inherited from HUD Effect
void packData(LLMessageSystem *mesgsys); // inherited from HUD Effect
void unpackData(LLMessageSystem *mesgsys, S32 blocknum); // inherited from HUD Effect
@ -119,7 +99,6 @@ class LLVoiceVisualizer : public LLRefCount
//----------------------------------------------------------------------------------------------
void setMaxGesticulationAmplitude();
void setMinGesticulationAmplitude();
#endif // XXX_STINSON_CHUI_REWORK
//---------------------------------------------------
// private members
@ -129,7 +108,6 @@ class LLVoiceVisualizer : public LLRefCount
static void setPreferences( );
static void lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ); // convert a string of digits to an array of floats
#ifdef XXX_STINSON_CHUI_REWORK
struct SoundSymbol
{
F32 mWaveExpansion [ NUM_VOICE_SYMBOL_WAVES ];
@ -140,20 +118,15 @@ class LLVoiceVisualizer : public LLRefCount
bool mActive;
LLVector3 mPosition;
};
#endif // XXX_STINSON_CHUI_REWORK
LLFrameTimer mTimer; // so I can ask the current time in seconds
F64 mStartTime; // time in seconds when speaking started
#ifdef XXX_STINSON_CHUI_REWORK
F64 mCurrentTime; // current time in seconds, captured every step
F64 mPreviousTime; // copy of "current time" from last frame
SoundSymbol mSoundSymbol; // the sound symbol that appears over the avatar's head
bool mVoiceEnabled; // if off, no rendering should happen
#endif // XXX_STINSON_CHUI_REWORK
bool mCurrentlySpeaking; // is the user currently speaking?
#ifdef XXX_STINSON_CHUI_REWORK
LLVector3 mVoiceSourceWorldPosition; // give this to me every step - I need it to update the sound symbol
#endif // XXX_STINSON_CHUI_REWORK
F32 mSpeakingAmplitude; // this should be set as often as possible when the user is speaking
F32 mMaxGesticulationAmplitude; // this is the upper-limit of the envelope of detectable gesticulation leves
F32 mMinGesticulationAmplitude; // this is the lower-limit of the envelope of detectable gesticulation leves

View File

@ -11,6 +11,9 @@
<color
name="EmphasisColor_35"
value="0.38 0.694 0.573 0.35" />
<color
name="BeaconColor"
value="0.749 0.298 0 1" />
<color
name="White"
value="1 1 1 1" />
@ -522,6 +525,9 @@
<color
name="MenuItemHighlightBgColor"
reference="EmphasisColor_35" />
<color
name="MenuItemFlashBgColor"
reference="BeaconColor" />
<color
name="MenuItemHighlightFgColor"
reference="White" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -365,6 +365,8 @@ with the same filename but different name
<texture name="Nearby_chat_icon" file_name="icons/nearby_chat_icon.png" preload="false" />
<texture name="Notices_Unread" file_name="bottomtray/Notices_Unread.png" preload="true" />
<texture name="NoEntryLines" file_name="world/NoEntryLines.png" use_mips="true" preload="false" />
<texture name="NoEntryPassLines" file_name="world/NoEntryPassLines.png" use_mips="true" preload="false" />

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Luk alle" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Alle schließen" name="Close All"/>
</context_menu>

View File

@ -1,91 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<floater
can_resize="true"
positioning="cascading"
height="400"
min_height="100"
min_width="390"
layout="topleft"
name="floater_conversation_log"
save_rect="true"
single_instance="true"
reuse_instance="true"
title="CONVERSATION LOG"
width="450">
can_resize="true"
positioning="cascading"
height="200"
min_height="100"
min_width="230"
layout="topleft"
name="floater_conversation_log"
save_rect="true"
single_instance="true"
reuse_instance="true"
title="CONVERSATION LOG"
width="300">
<string name="logging_calls_disabled">
Conversations are not being logged. To log conversations in the future, select "Save IM logs on my computer" under Preferences > Privacy.
</string>
<panel
follows="left|top|right"
height="27"
layout="topleft"
left="0"
name="buttons_panel"
top="0">
<filter_editor
follows="left|top|right"
height="23"
layout="topleft"
left="8"
label="Filter People"
max_length_chars="300"
name="people_filter_input"
text_color="Black"
text_pad_left="10"
top="4"
width="364" />
<menu_button
follows="right"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_overlay="Conv_toolbar_sort"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
left_pad="5"
menu_filename="menu_conversation_log_view.xml"
menu_position="bottomleft"
name="conversation_view_btn"
tool_tip="View/sort options"
top="3"
width="31" />
<menu_button
follows="right"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_overlay="OptionsMenu_Off"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
left_pad="2"
name="conversations_gear_btn"
tool_tip="Actions on selected person or group"
top="3"
width="31" />
</panel>
<panel
follows="all"
height="370"
layout="topleft"
left="5"
name="buttons_panel"
right="-5"
top_pad="5">
<panel
follows="left|top|right"
height="32"
left="0"
name="buttons_panel"
top="0">
<filter_editor
follows="left|top|right"
height="23"
layout="topleft"
left="8"
label="Filter People"
max_length_chars="300"
name="people_filter_input"
text_color="Black"
text_pad_left="10"
top="4"
width="204" />
<menu_button
follows="top|right"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_overlay="Conv_toolbar_sort"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
left_pad="8"
menu_filename="menu_conversation_log_view.xml"
menu_position="bottomleft"
name="conversation_view_btn"
tool_tip="View/sort options"
top="3"
width="31" />
<menu_button
follows="top|right"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_overlay="OptionsMenu_Off"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
left_pad="8"
name="conversations_gear_btn"
tool_tip="Actions on selected person or group"
top="3"
width="31" />
</panel>
<panel
bottom="-1"
follows="all"
left="0"
name="log_panel"
right="-1"
top="32">
<conversation_log_list
opaque="true"
allow_select="true"
follows="all"
height="360"
layout="topleft"
left="3"
keep_selection_visible_on_reshape="true"
item_pad="2"
multi_select="false"
name="conversation_log_list"
right="-3"
top="5" />
</panel>
allow_select="true"
bottom="-8"
opaque="true"
follows="all"
left="8"
keep_selection_visible_on_reshape="true"
item_pad="2"
multi_select="false"
name="conversation_log_list"
right="-8"
top="0" />
</panel>
</floater>

View File

@ -3,9 +3,8 @@
can_close="true"
can_minimize="true"
can_resize="true"
height="230"
height="210"
layout="topleft"
min_height="50"
name="floater_im_box"
help_topic="floater_im_box"
save_rect="true"
@ -24,22 +23,20 @@
value="Conv_toolbar_expand"/>
<layout_stack
animate="true"
bottom="-1"
follows="all"
height="230"
layout="topleft"
left="0"
name="conversations_stack"
orientation="horizontal"
top="0"
width="450">
right="-1"
top="0">
<layout_panel
auto_resize="false"
user_resize="true"
height="430"
name="conversations_layout_panel"
min_dim="38"
width="268"
expanded_min_dim="120">
expanded_min_dim="156">
<layout_stack
animate="false"
follows="left|top|right"
@ -48,8 +45,8 @@
left="0"
name="conversations_pane_buttons_stack"
orientation="horizontal"
top="0"
width="268">
right="-1"
top="0">
<layout_panel
auto_resize="true"
height="35"
@ -109,46 +106,44 @@
image_unselected="Toolbar_Middle_Off"
layout="topleft"
top="5"
left="5"
left="1"
name="expand_collapse_btn"
tool_tip="Collapse/Expand this list"
width="31" />
</layout_panel>
</layout_stack>
<panel
bottom="-5"
follows="all"
layout="topleft"
name="conversations_list_panel"
opaque="true"
top_pad="0"
top="35"
left="5"
height="390"
width="263"/>
right="-1"/>
</layout_panel>
<layout_panel
auto_resize="true"
user_resize="true"
height="430"
name="messages_layout_panel"
width="412"
expanded_min_dim="225">
expanded_min_dim="222">
<panel_container
bottom="-5"
follows="all"
height="430"
layout="topleft"
left="0"
name="im_box_tab_container"
top="0"
width="412">
right="-1"
top="0">
<panel
bottom="-1"
follows="all"
layout="topleft"
name="stub_panel"
opaque="true"
top_pad="0"
left="0"
height="430"
width="412">
right="-1">
<button
follows="right|top"
height="25"

View File

@ -49,6 +49,15 @@
<floater.string
name="end_call_button_tooltip"
value="Close voice connection"/>
<floater.string
name="expcol_button_not_tearoff_tooltip"
value="Collapse this pane"/>
<floater.string
name="expcol_button_tearoff_and_expanded_tooltip"
value="Collapse participant list"/>
<floater.string
name="expcol_button_tearoff_and_collapsed_tooltip"
value="Expand participant list"/>
<view
follows="all"
layout="topleft"
@ -79,6 +88,21 @@
tool_tip="View/sort options"
top="5"
width="31" />
<menu_button
menu_filename="menu_im_conversation.xml"
follows="top|left"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_overlay="OptionsMenu_Off"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
top="5"
left_pad="4"
name="gear_btn"
visible="false"
tool_tip="Actions on selected person"
width="31"/>
<button
enabled="false"
follows="top|left"
@ -191,7 +215,7 @@
auto_resize="true"
visible="true"
name="left_part_holder"
min_width="225">
min_width="221">
<panel
name="trnsAndChat_panel"
follows="all"

View File

@ -75,6 +75,13 @@
<on_click function="Avatar.DoToSelected" parameter="remove_friend" />
<on_enable function="Avatar.EnableItem" parameter="can_delete" />
</menu_item_call>
<menu_item_call
label="Remove friends"
layout="topleft"
name="remove_friends">
<on_click function="Avatar.DoToSelected" parameter="remove_friend" />
<on_enable function="Avatar.EnableItem" parameter="can_delete" />
</menu_item_call>
<menu_item_call
label="Invite to group..."
layout="topleft"

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<toggleable_menu
layout="topleft"
name="Conversation Gear Menu">
<menu_item_call
label="View Profile"
layout="topleft"
name="View Profile">
<on_click function="Avatar.GearDoToSelected" parameter="view_profile" />
<on_enable function="Avatar.EnableGearItem" parameter="can_view_profile" />
</menu_item_call>
<menu_item_call
label="Add Friend"
layout="topleft"
name="Add Friend">
<on_click function="Avatar.GearDoToSelected" parameter="add_friend" />
<on_enable function="Avatar.EnableGearItem" parameter="can_add" />
</menu_item_call>
<menu_item_call
label="Remove friend"
layout="topleft"
name="remove_friend">
<on_click function="Avatar.GearDoToSelected" parameter="remove_friend" />
<on_enable function="Avatar.EnableGearItem" parameter="can_delete" />
</menu_item_call>
<menu_item_call
label="Offer teleport"
layout="topleft"
name="offer_teleport">
<on_click function="Avatar.GearDoToSelected" parameter="offer_teleport"/>
<on_enable function="Avatar.EnableGearItem" parameter="can_offer_teleport"/>
</menu_item_call>
<menu_item_call
label="Invite to group..."
layout="topleft"
name="invite_to_group">
<on_click function="Avatar.GearDoToSelected" parameter="invite_to_group" />
<on_enable function="Avatar.EnableGearItem" parameter="can_invite" />
</menu_item_call>
<menu_item_separator
layout="topleft"
name="View Icons Separator" />
<menu_item_call
label="Chat history..."
layout="topleft"
name="chat_history">
<on_click function="Avatar.GearDoToSelected" parameter="chat_history"/>
<on_enable function="Avatar.EnableGearItem" parameter="can_chat_history"/>
</menu_item_call>
<menu_item_separator
layout="topleft"/>
<menu_item_call
label="Map"
layout="topleft"
name="map">
<on_click function="Avatar.GearDoToSelected" parameter="map" />
<on_enable function="Avatar.EnableGearItem" parameter="can_show_on_map" />
</menu_item_call>
<menu_item_call
label="Share"
layout="topleft"
name="Share">
<on_click function="Avatar.GearDoToSelected" parameter="share" />
<on_enable function="Avatar.EnableGearItem" parameter="can_share" />
</menu_item_call>
<menu_item_call
label="Pay"
layout="topleft"
name="Pay">
<on_click function="Avatar.GearDoToSelected" parameter="pay" />
<on_enable function="Avatar.EnableGearItem" parameter="can_pay" />
</menu_item_call>
<menu_item_separator
layout="topleft"/>
<menu_item_check
label="Block Voice"
layout="topleft"
name="Block/Unblock">
<on_check function="Avatar.CheckGearItem" parameter="is_blocked" />
<on_click function="Avatar.GearDoToSelected" parameter="block_unblock" />
<on_enable function="Avatar.EnableGearItem" parameter="can_block" />
</menu_item_check>
<menu_item_check
label="Block Text"
layout="topleft"
name="MuteText">
<on_check function="Avatar.CheckGearItem" parameter="is_muted" />
<on_click function="Avatar.GearDoToSelected" parameter="mute_unmute" />
<on_enable function="Avatar.EnableGearItem" parameter="can_block" />
</menu_item_check>
<menu_item_separator
layout="topleft"/>
</toggleable_menu>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<context_menu
layout="topleft"
name="Notification Well Button Context Menu">
<menu_item_call
label="Close All"
layout="topleft"
name="Close All">
<menu_item_call.on_click
function="NotificationWellChicletMenu.Action"
parameter="close all" />
<menu_item_call.on_enable
function="NotificationWellChicletMenu.EnableItem"
parameter="can close all" />
</menu_item_call>
</context_menu>

View File

@ -1267,7 +1267,58 @@
function="Floater.Show"
parameter="hud" />
</menu_item_call>-->
<menu_item_separator/>
<menu_item_call
label="Users guide"
name="Users guide">
<menu_item_call.on_click
function="Advanced.ShowURL"
parameter="http://community.secondlife.com/t5/English-Knowledge-Base/Second-Life-User-s-Guide/ta-p/1244857"/>
</menu_item_call>
<menu_item_call
label="Knowledge Base"
name="Knowledge Base">
<menu_item_call.on_click
function="Advanced.ShowURL"
parameter="http://community.secondlife.com/t5/tkb/communitypage"/>
</menu_item_call>
<menu_item_call
label="Wiki"
name="Wiki">
<menu_item_call.on_click
function="Advanced.ShowURL"
parameter="http://wiki.secondlife.com"/>
</menu_item_call>
<menu_item_call
label="Community Forums"
name="Community Forums">
<menu_item_call.on_click
function="Advanced.ShowURL"
parameter="http://community.secondlife.com/t5/Forums/ct-p/Forums"/>
</menu_item_call>
<menu_item_call
label="Support portal"
name="Support portal">
<menu_item_call.on_click
function="Advanced.ShowURL"
parameter="https://support.secondlife.com/"/>
</menu_item_call>
<menu_item_separator/>
<menu_item_call
label="[SECOND_LIFE] News"
name="Second Life News">
<menu_item_call.on_click
function="Advanced.ShowURL"
parameter="http://community.secondlife.com/t5/Featured-News/bg-p/blog_feature_news"/>
</menu_item_call>
<menu_item_call
label="[SECOND_LIFE] Blogs"
name="Second Life Blogs">
<menu_item_call.on_click
function="Advanced.ShowURL"
parameter="http://community.secondlife.com/t5/Blogs/ct-p/Blogs"/>
</menu_item_call>
<menu_item_separator/>
<menu_item_call

View File

@ -3696,7 +3696,6 @@ Do Not Disturb is on. You will not be notified of incoming communications.
- Other residents will receive your Do Not Disturb response (set in Preferences &gt; General).
- Teleportation offers will be declined.
- Voice calls will be rejected.
- Inventory offers will go to your Trash.
<usetemplate
ignoretext="I change my status to Do Not Disturb mode"
name="okignore"
@ -6584,7 +6583,7 @@ However, this region contains content accessible to adults only.
log_to_im="true"
log_to_chat="false"
show_toast="false"
type="offer">
type="notify">
Teleport offer sent to [TO_NAME]
</notification>
@ -6637,7 +6636,7 @@ However, this region contains content accessible to adults only.
name="FriendshipOffered"
log_to_im="true"
show_toast="false"
type="offer">
type="notify">
<tag>friendship</tag>
You have offered friendship to [TO_NAME]
</notification>
@ -6667,7 +6666,7 @@ However, this region contains content accessible to adults only.
icon="notify.tga"
name="FriendshipAccepted"
log_to_im="true"
type="offer">
type="notify">
<tag>friendship</tag>
&lt;nolink&gt;[NAME]&lt;/nolink&gt; accepted your friendship offer.
</notification>
@ -6687,7 +6686,7 @@ However, this region contains content accessible to adults only.
name="FriendshipAcceptedByMe"
log_to_im="true"
show_toast="false"
type="offer">
type="notify">
<tag>friendship</tag>
Friendship offer accepted.
</notification>
@ -6697,7 +6696,7 @@ Friendship offer accepted.
name="FriendshipDeclinedByMe"
log_to_im="true"
show_toast="false"
type="offer">
type="notify">
<tag>friendship</tag>
Friendship offer declined.
</notification>

View File

@ -77,5 +77,49 @@
width="12" />
</chiclet_panel>
</layout_panel>
<layout_panel auto_resize="false"
width="4"
min_width="4"/>
<layout_panel
auto_resize="false"
follows="right"
height="28"
layout="topleft"
min_height="28"
min_width="37"
name="notification_well_panel"
top="0"
width="37">
<chiclet_notification
follows="right"
height="23"
layout="topleft"
left="0"
max_displayed_count="99"
name="notification_well"
top="5"
width="35">
<button
auto_resize="false"
bottom_pad="3"
follows="right"
halign="center"
height="23"
image_overlay="Notices_Unread"
image_overlay_alignment="center"
image_pressed="WellButton_Lit"
image_pressed_selected="WellButton_Lit_Selected"
image_selected="PushButton_Press"
label_color="Black"
left="0"
name="Unread"
tool_tip="Notifications"
width="34">
<init_callback
function="Button.SetDockableFloaterToggle"
parameter="notification_well_window" />
</button>
</chiclet_notification>
</layout_panel>
</layout_stack>
</panel>

View File

@ -30,9 +30,9 @@
image_overlay_alignment="left"
use_ellipses="true"
auto_resize="true"
button_flash_count="3"
button_flash_rate="0.25"
flash_color="EmphasisColor"/>
button_flash_count="4"
button_flash_rate="0.5"
flash_color="BeaconColor"/>
<button_icon pad_left="10"
pad_right="10"
image_bottom_pad="10"
@ -51,7 +51,7 @@
chrome="true"
use_ellipses="true"
auto_resize="true"
button_flash_count="3"
button_flash_rate="0.25"
flash_color="EmphasisColor"/>
button_flash_count="4"
button_flash_rate="0.5"
flash_color="BeaconColor"/>
</toolbar>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Cerrar todo" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Tout fermer" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Chiudi tutto" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="すべて閉じる" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Zamknij" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Fechar tudo" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Закрыть все" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="Tümünü Kapat" name="Close All"/>
</context_menu>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<context_menu name="Notification Well Button Context Menu">
<menu_item_call label="全部關閉" name="Close All"/>
</context_menu>