summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdoc/contributions.txt9
-rwxr-xr-xindra/llmessage/llinstantmessage.h2
-rwxr-xr-xindra/llui/CMakeLists.txt4
-rwxr-xr-xindra/llui/llbutton.cpp4
-rwxr-xr-xindra/llui/llfloater.cpp25
-rwxr-xr-xindra/llui/llfloater.h1
-rwxr-xr-xindra/llui/llnotifications.cpp1
-rwxr-xr-xindra/llui/llnotifications.h4
-rw-r--r--indra/llui/llnotificationslistener.cpp359
-rw-r--r--indra/llui/llnotificationslistener.h69
-rwxr-xr-xindra/llui/lltabcontainer.cpp56
-rwxr-xr-xindra/llui/lluictrl.cpp20
-rwxr-xr-xindra/llui/llview.cpp61
-rw-r--r--indra/llui/llviewereventrecorder.cpp296
-rw-r--r--indra/llui/llviewereventrecorder.h103
-rwxr-xr-xindra/llwindow/llwindowmacosx.cpp2
-rwxr-xr-xindra/newview/app_settings/cmd_line.xml8
-rwxr-xr-xindra/newview/app_settings/settings.xml21
-rwxr-xr-xindra/newview/llappviewer.cpp11
-rwxr-xr-xindra/newview/llappviewerwin32.cpp17
-rwxr-xr-xindra/newview/llavataractions.cpp61
-rwxr-xr-xindra/newview/llavataractions.h6
-rwxr-xr-xindra/newview/llchiclet.cpp23
-rwxr-xr-xindra/newview/llconversationloglist.cpp4
-rwxr-xr-xindra/newview/llconversationmodel.cpp1
-rwxr-xr-xindra/newview/llfloaterimcontainer.cpp4
-rwxr-xr-xindra/newview/llfloaterregioninfo.cpp18
-rwxr-xr-xindra/newview/llinventorybridge.cpp12
-rwxr-xr-xindra/newview/llnetmap.cpp5
-rwxr-xr-xindra/newview/llnotificationscripthandler.cpp2
-rwxr-xr-xindra/newview/llpanelgrouproles.cpp66
-rwxr-xr-xindra/newview/llpanelgrouproles.h1
-rwxr-xr-xindra/newview/llpanelpeoplemenus.cpp10
-rwxr-xr-xindra/newview/llpanelpeoplemenus.h1
-rwxr-xr-xindra/newview/llsyswellwindow.cpp5
-rwxr-xr-xindra/newview/llsyswellwindow.h1
-rwxr-xr-xindra/newview/llviewerkeyboard.cpp5
-rwxr-xr-xindra/newview/llviewermenu.cpp41
-rwxr-xr-xindra/newview/llviewermessage.cpp159
-rwxr-xr-xindra/newview/llviewerwindow.cpp102
-rwxr-xr-xindra/newview/llwindowlistener.cpp9
-rwxr-xr-xindra/newview/llworldmap.cpp11
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_conversation.xml7
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_conversation_log_gear.xml10
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_inventory.xml8
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_people_nearby.xml10
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_viewer.xml28
-rwxr-xr-xindra/newview/skins/default/xui/en/notifications.xml86
-rwxr-xr-xindra/newview/skins/default/xui/en/strings.xml8
-rwxr-xr-xindra/viewer_components/updater/llupdatechecker.cpp49
50 files changed, 1599 insertions, 227 deletions
diff --git a/doc/contributions.txt b/doc/contributions.txt
index caeda19496..4572ebf5a5 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -175,6 +175,7 @@ Ansariel Hiller
STORM-1685
STORM-1713
STORM-1899
+ STORM-1932
MAINT-2368
MAINT-2773
Aralara Rajal
@@ -302,7 +303,11 @@ Christopher Organiser
Ciaran Laval
Cinder Roxley
BUG-2326
+ OPEN-185
STORM-1703
+ STORM-1958
+ STORM-1952
+ STORM-1951
Clara Young
Coaldust Numbers
VWR-1095
@@ -644,7 +649,7 @@ Jonathan Yap
STORM-1809
STORM-1793
STORM-1810
- STORM-1877
+ STORM-1838
STORM-1860
STORM-1852
STORM-1870
@@ -652,6 +657,8 @@ Jonathan Yap
STORM-1858
STORM-1862
OPEN-161
+ STORM-1953
+ STORM-1957
Kadah Coba
STORM-1060
STORM-1843
diff --git a/indra/llmessage/llinstantmessage.h b/indra/llmessage/llinstantmessage.h
index db4a38ea9e..f7118f8ccf 100755
--- a/indra/llmessage/llinstantmessage.h
+++ b/indra/llmessage/llinstantmessage.h
@@ -126,7 +126,7 @@ enum EInstantMessage
IM_LURE_ACCEPTED = 23,
IM_LURE_DECLINED = 24,
IM_GODLIKE_LURE_USER = 25,
- IM_YET_TO_BE_USED = 26,
+ IM_TELEPORT_REQUEST = 26,
// IM that notifie of a new group election.
// Name is name of person who called vote.
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 34a08603fa..589ceac501 100755
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -75,6 +75,7 @@ set(llui_SOURCE_FILES
llmultislider.cpp
llmultisliderctrl.cpp
llnotifications.cpp
+ llnotificationslistener.cpp
llnotificationsutil.cpp
llpanel.cpp
llprogressbar.cpp
@@ -128,6 +129,7 @@ set(llui_SOURCE_FILES
llviewmodel.cpp
llview.cpp
llviewquery.cpp
+ llviewereventrecorder.cpp
llwindowshade.cpp
llxuiparser.cpp
)
@@ -183,6 +185,7 @@ set(llui_HEADER_FILES
llmultislider.h
llnotificationptr.h
llnotifications.h
+ llnotificationslistener.h
llnotificationsutil.h
llnotificationtemplate.h
llnotificationvisibilityrule.h
@@ -240,6 +243,7 @@ set(llui_HEADER_FILES
llviewinject.h
llviewmodel.h
llview.h
+ llviewereventrecorder.h
llviewquery.h
llwindowshade.h
llxuiparser.h
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index a8149a9a1d..4ccb019106 100755
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -49,6 +49,7 @@
#include "lluictrlfactory.h"
#include "llhelp.h"
#include "lldockablefloater.h"
+#include "llviewereventrecorder.h"
static LLDefaultChildRegistry::Register<LLButton> r("button");
@@ -443,6 +444,8 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
*/
LLUICtrl::handleMouseDown(x, y, mask);
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+
if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
mMouseDownTimer.start();
@@ -473,6 +476,7 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
* by calling LLUICtrl::mMouseUpSignal(x, y, mask);
*/
LLUICtrl::handleMouseUp(x, y, mask);
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
// Regardless of where mouseup occurs, handle callback
if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 09e27a264a..913de49d63 100755
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -29,7 +29,7 @@
// mini-map floater, etc.
#include "linden_common.h"
-
+#include "llviewereventrecorder.h"
#include "llfloater.h"
#include "llfocusmgr.h"
@@ -653,7 +653,10 @@ void LLFloater::handleVisibilityChange ( BOOL new_visibility )
void LLFloater::openFloater(const LLSD& key)
{
- llinfos << "Opening floater " << getName() << llendl;
+ llinfos << "Opening floater " << getName() << " full path: " << getPathname() << llendl;
+
+ LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), true,"floater"); // Last param is event subtype or empty string
+
mKey = key; // in case we need to open ourselves again
if (getSoundFlags() != SILENT
@@ -707,6 +710,7 @@ void LLFloater::openFloater(const LLSD& key)
void LLFloater::closeFloater(bool app_quitting)
{
llinfos << "Closing floater " << getName() << llendl;
+ LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), false,"floater"); // Last param is event subtype or empty string
if (app_quitting)
{
LLFloater::sQuitting = true;
@@ -1553,6 +1557,17 @@ BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks)
}
// virtual
+BOOL LLFloater::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ lldebugs << "LLFloater::handleMouseUp calling LLPanel (really LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+ BOOL handled = LLPanel::handleMouseUp(x,y,mask); // Not implemented in LLPanel so this actually calls LLView
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+ }
+ return handled;
+}
+
+// virtual
BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
{
if( mMinimized )
@@ -1572,7 +1587,11 @@ BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
else
{
bringToFront( x, y );
- return LLPanel::handleMouseDown( x, y, mask );
+ BOOL handled = LLPanel::handleMouseDown( x, y, mask );
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+ }
+ return handled;
}
}
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 4dba1e645f..09fe2219c0 100755
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -285,6 +285,7 @@ public:
S32 getHeaderHeight() const { return mHeaderHeight; }
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask);
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 1789f003b9..743d34c57b 100755
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -1206,6 +1206,7 @@ LLNotifications::LLNotifications()
: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
mIgnoreAllNotifications(false)
{
+ mListener.reset(new LLNotificationsListener(*this));
LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
}
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 87573c2a56..9037712cc8 100755
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -98,6 +98,8 @@
#include "llrefcount.h"
#include "llsdparam.h"
+#include "llnotificationslistener.h"
+
class LLAvatarName;
typedef enum e_notification_priority
{
@@ -970,6 +972,8 @@ private:
bool mIgnoreAllNotifications;
+ boost::scoped_ptr<LLNotificationsListener> mListener;
+
std::vector<LLNotificationChannelPtr> mDefaultChannels;
};
diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp
new file mode 100644
index 0000000000..9e8e943ee6
--- /dev/null
+++ b/indra/llui/llnotificationslistener.cpp
@@ -0,0 +1,359 @@
+/**
+ * @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 (LLNotifications::
+
+
+
+ for (LLNotifications::ChannelMap::const_iterator cmi(mNotifications.mChannels.begin()),
+ cmend(mNotifications.mChannels.end());
+ cmi != cmend; ++cmi)
+ {
+ LLSD channelInfo;
+ channelInfo["parent"] = cmi->second->getParentChannelName();
+ response[cmi->first] = 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;
+}
diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h
new file mode 100644
index 0000000000..f9f7641de6
--- /dev/null
+++ b/indra/llui/llnotificationslistener.h
@@ -0,0 +1,69 @@
+/**
+ * @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
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index fd98155704..76ba53ec32 100755
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -27,7 +27,7 @@
#include "linden_common.h"
#include "lltabcontainer.h"
-
+#include "llviewereventrecorder.h"
#include "llfocusmgr.h"
#include "lllocalcliprect.h"
#include "llrect.h"
@@ -578,6 +578,11 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
tab_button->setFocus(TRUE);
}
}
+ if (handled) {
+ // Note: May need to also capture local coords right here ?
+ LLViewerEventRecorder::instance().update_xui(getPathname( ));
+ }
+
return handled;
}
@@ -629,30 +634,33 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
BOOL handled = FALSE;
BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
+ S32 local_x = x - getRect().mLeft;
+ S32 local_y = y - getRect().mBottom;
+
if (has_scroll_arrows)
{
if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
- S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
+ local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
+ local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
- S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
+ local_x = x - mJumpNextArrowBtn->getRect().mLeft;
+ local_y = y - mJumpNextArrowBtn->getRect().mBottom;
handled = mJumpNextArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
- S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
+ local_x = x - mPrevArrowBtn->getRect().mLeft;
+ local_y = y - mPrevArrowBtn->getRect().mBottom;
handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mNextArrowBtn->getRect().mLeft;
- S32 local_y = y - mNextArrowBtn->getRect().mBottom;
+ local_x = x - mNextArrowBtn->getRect().mLeft;
+ local_y = y - mNextArrowBtn->getRect().mBottom;
handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
}
}
@@ -676,6 +684,10 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
}
gFocusMgr.setMouseCapture(NULL);
}
+ if (handled) {
+ // Note: may need to capture local coords here
+ LLViewerEventRecorder::instance().update_xui(getPathname( ));
+ }
return handled;
}
@@ -1059,21 +1071,21 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
if (mIsVertical)
{
- p.name(std::string("vert tab button"));
- p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
- p.image_selected(mMiddleTabParams.tab_left_image_selected);
- p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
+ p.name("vtab_"+std::string(child->getName()));
+ p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
+ p.image_selected(mMiddleTabParams.tab_left_image_selected);
+ p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
}
else
- {
- p.name(std::string(child->getName()) + " tab");
- p.visible(false);
- p.image_unselected(tab_img);
- p.image_selected(tab_selected_img);
- p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM);
- // Try to squeeze in a bit more text
- p.pad_left( mLabelPadLeft );
- p.pad_right(2);
+ {
+ p.name("htab_"+std::string(child->getName()));
+ p.visible(false);
+ p.image_unselected(tab_img);
+ p.image_selected(tab_selected_img);
+ p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM);
+ // Try to squeeze in a bit more text
+ p.pad_left( mLabelPadLeft );
+ p.pad_right(2);
}
// *TODO : It seems wrong not to use p in both cases considering the way p is initialized
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index b9c843e931..1722bf27bd 100755
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -29,7 +29,7 @@
#define LLUICTRL_CPP
#include "lluictrl.h"
-
+#include "llviewereventrecorder.h"
#include "llfocusmgr.h"
#include "llpanel.h"
#include "lluictrlfactory.h"
@@ -308,22 +308,40 @@ void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask)
//virtual
BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask)
{
+
+ lldebugs << "LLUICtrl::handleMouseDown calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+
BOOL handled = LLView::handleMouseDown(x,y,mask);
+
if (mMouseDownSignal)
{
(*mMouseDownSignal)(this,x,y,mask);
}
+ lldebugs << "LLUICtrl::handleMousedown - handled is returning as: " << handled << " " << llendl;
+
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname());
+ }
return handled;
}
//virtual
BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask)
{
+
+ lldebugs << "LLUICtrl::handleMouseUp calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+
BOOL handled = LLView::handleMouseUp(x,y,mask);
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname());
+ }
if (mMouseUpSignal)
{
(*mMouseUpSignal)(this,x,y,mask);
}
+
+ lldebugs << "LLUICtrl::handleMouseUp - handled for xui " << getPathname() << " - is returning as: " << handled << " " << llendl;
+
return handled;
}
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 3613a40e2c..9a42fc637b 100755
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -48,7 +48,9 @@
#include "lluictrlfactory.h"
#include "lltooltip.h"
#include "llsdutil.h"
-
+#include "llsdserialize.h"
+#include "llviewereventrecorder.h"
+#include "llkeyboard.h"
// for ui edit hack
#include "llbutton.h"
#include "lllineeditor.h"
@@ -642,13 +644,27 @@ void LLView::setVisible(BOOL visible)
// virtual
void LLView::handleVisibilityChange ( BOOL new_visibility )
{
+ BOOL old_visibility;
BOOST_FOREACH(LLView* viewp, mChildList)
{
// only views that are themselves visible will have their overall visibility affected by their ancestors
- if (viewp->getVisible())
+ old_visibility=viewp->getVisible();
+
+ if (old_visibility!=new_visibility)
+ {
+ LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget");
+ }
+
+ if (old_visibility)
{
viewp->handleVisibilityChange ( new_visibility );
}
+
+ // Consider changing returns to confirm success and know which widget grabbed it
+ // For now assume success and log at highest xui possible
+ // NOTE we log actual state - which may differ if it somehow failed to set visibility
+ lldebugs << "LLView::handleVisibilityChange - now: " << getVisible() << " xui: " << viewp->getPathname() << " name: " << viewp->getName() << llendl;
+
}
}
@@ -697,6 +713,7 @@ bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y)
&& getEnabled();
}
+// This is NOT event recording related
void LLView::logMouseEvent()
{
if (sDebugMouseHandling)
@@ -743,8 +760,15 @@ LLView* LLView::childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDA
if ((viewp->*method)( local_x, local_y, extra )
|| (allow_mouse_block && viewp->blockMouseEvent( local_x, local_y )))
{
- viewp->logMouseEvent();
- return viewp;
+ lldebugs << "LLView::childrenHandleMouseEvent calling updatemouseeventinfo - local_x|global x "<< local_x << " " << x << "local/global y " << local_y << " " << y << llendl;
+ lldebugs << "LLView::childrenHandleMouseEvent getPathname for viewp result: " << viewp->getPathname() << "for this view: " << getPathname() << llendl;
+
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+
+ // This is NOT event recording related
+ viewp->logMouseEvent();
+
+ return viewp;
}
}
return NULL;
@@ -766,6 +790,7 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
if (viewp->handleToolTip(local_x, local_y, mask)
|| viewp->blockMouseEvent(local_x, local_y))
{
+ // This is NOT event recording related
viewp->logMouseEvent();
return viewp;
}
@@ -824,6 +849,7 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
if (viewp->handleHover(local_x, local_y, mask)
|| viewp->blockMouseEvent(local_x, local_y))
{
+ // This is NOT event recording related
viewp->logMouseEvent();
return viewp;
}
@@ -907,10 +933,11 @@ BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
if (!handled)
{
+ // For event logging we don't care which widget handles it
+ // So we capture the key at the end of this function once we know if it was handled
handled = handleKeyHere( key, mask );
- if (handled && LLView::sDebugKeys)
- {
- llinfos << "Key handled by " << getName() << llendl;
+ if (handled) {
+ llwarns << "Key handled by " << getName() << llendl;
}
}
}
@@ -958,12 +985,17 @@ BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
handled = mParentView->handleUnicodeChar(uni_char, FALSE);
}
+ if (handled) {
+ LLViewerEventRecorder::instance().logKeyUnicodeEvent(uni_char);
+ }
+
return handled;
}
BOOL LLView::handleUnicodeCharHere(llwchar uni_char )
{
+ llwarns << "LLView::handleUnicodeCharHere - about to return false - key is not being handled - a class somewhere should implement this method" << llendl;
return FALSE;
}
@@ -987,12 +1019,21 @@ BOOL LLView::hasMouseCapture()
BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
{
- return childrenHandleMouseUp( x, y, mask ) != NULL;
+
+
+ LLView* r = childrenHandleMouseUp( x, y, mask );
+
+ return (r!=NULL);
+
}
BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
{
- return childrenHandleMouseDown( x, y, mask ) != NULL;
+
+ LLView* r= childrenHandleMouseDown(x, y, mask );
+
+ return (r!=NULL);
+
}
BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
@@ -1065,7 +1106,7 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
{
- return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
+ return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
}
LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp
new file mode 100644
index 0000000000..a352f621eb
--- /dev/null
+++ b/indra/llui/llviewereventrecorder.cpp
@@ -0,0 +1,296 @@
+/**
+ * @file llviewereventrecorder.cpp
+ * @brief Viewer event recording and playback support for mouse and keyboard events
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ *
+ * Copyright (c) 2013, 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 "llviewereventrecorder.h"
+#include "llui.h"
+#include "llleap.h"
+
+LLViewerEventRecorder::LLViewerEventRecorder() {
+
+ clear(UNDEFINED);
+
+ // Remove any previous event log file
+ std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old");
+ LLFile::remove(old_log_ui_events_to_llsd_file);
+
+
+ mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
+ LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file);
+
+}
+
+
+bool LLViewerEventRecorder::displayViewerEventRecorderMenuItems() {
+ return LLUI::sSettingGroups["config"]->getBOOL("ShowEventRecorderMenuItems");
+}
+
+
+void LLViewerEventRecorder::setEventLoggingOn() {
+ if (! mLog.is_open()) {
+ mLog.open(mLogFilename, llofstream::out);
+ }
+ logEvents=true;
+ lldebugs << "LLViewerEventRecorder::setEventLoggingOn event logging turned on" << llendl;
+}
+
+void LLViewerEventRecorder::setEventLoggingOff() {
+ logEvents=false;
+ mLog.flush();
+ mLog.close();
+ lldebugs << "LLViewerEventRecorder::setEventLoggingOff event logging turned off" << llendl;
+}
+
+
+ LLViewerEventRecorder::~LLViewerEventRecorder() {
+ if (mLog.is_open()) {
+ mLog.close();
+ }
+}
+
+void LLViewerEventRecorder::clear_xui() {
+ xui.clear();
+}
+
+void LLViewerEventRecorder::clear(S32 r) {
+
+ xui.clear();
+
+ local_x=r;
+ local_y=r;
+
+ global_x=r;
+ global_y=r;
+
+
+}
+
+void LLViewerEventRecorder::setMouseLocalCoords(S32 x, S32 y) {
+ local_x=x;
+ local_y=y;
+}
+
+void LLViewerEventRecorder::setMouseGlobalCoords(S32 x, S32 y) {
+ global_x=x;
+ global_y=y;
+}
+
+void LLViewerEventRecorder::updateMouseEventInfo(S32 local_x, S32 local_y, S32 global_x, S32 global_y, std::string mName) {
+
+ LLView * target_view = LLUI::resolvePath(LLUI::getRootView(), xui);
+ if (! target_view) {
+ lldebugs << "LLViewerEventRecorder::updateMouseEventInfo - xui path on file at moment is NOT valid - so DO NOT record these local coords" << llendl;
+ return;
+ }
+ lldebugs << "LLViewerEventRecorder::updateMouseEventInfo b4 updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << llendl;
+
+
+ if (this->local_x < 1 && this->local_y<1 && local_x && local_y) {
+ this->local_x=local_x;
+ this->local_y=local_y;
+ }
+ this->global_x=global_x;
+ this->global_y=global_y;
+
+ // ONLY record deepest xui path for hierarchy searches - or first/only xui for floaters/panels reached via mouse captor - and llmousehandler
+ if (mName!="" && mName!="/" && xui=="") {
+ // xui=std::string("/")+mName+xui;
+ //xui=mName+xui;
+ xui = mName; // TODO review confirm we never call with partial path - also cAN REMOVE CHECK FOR "" - ON OTHER HAND IT'S PRETTY HARMLESS
+ }
+
+ lldebugs << "LLViewerEventRecorder::updateMouseEventInfo after updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << llendl;
+}
+
+void LLViewerEventRecorder::logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype) {
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("event",LLSD(std::string("visibility")));
+
+ if (visibility) {
+ event.insert("visibility",LLSD(true));
+ } else {
+ event.insert("visibility",LLSD(false));
+ }
+
+ if (event_subtype!="") {
+ event.insert("event_subtype", LLSD(event_subtype));
+ }
+
+ if(name!="") {
+ event.insert("name",LLSD(name));
+ }
+
+ if (xui!="") {
+ event.insert("path",LLSD(xui));
+ }
+
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+ recordEvent(event);
+}
+
+
+std::string LLViewerEventRecorder::get_xui() {
+ return xui;
+}
+void LLViewerEventRecorder::update_xui(std::string xui) {
+ if (xui!="" && this->xui=="" ) {
+ lldebugs << "LLViewerEventRecorder::update_xui to " << xui << llendl;
+ this->xui=xui;
+ } else {
+ lldebugs << "LLViewerEventRecorder::update_xui called with empty string" << llendl;
+ }
+}
+
+void LLViewerEventRecorder::logKeyEvent(KEY key, MASK mask) {
+
+ // NOTE: Event recording only logs keydown events - the viewer itself hides keyup events at a fairly low level in the code and does not appear to care about them anywhere
+
+ LLSD event = LLSD::emptyMap();
+
+ event.insert("event",LLSD("type"));
+
+ // keysym ...or
+ // keycode...or
+ // char
+ event.insert("keysym",LLSD(LLKeyboard::stringFromKey(key)));
+
+ // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
+ // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
+ // break the test script and it would be useful to have more context to make these sorts of edits safer
+
+ // TODO replace this with a call which extracts to an array of names of masks (just like vita expects during playback)
+ // This is looking more and more like an object is a good idea, for this part a handy method call to setMask(mask) would be nice :-)
+ // call the func - llkeyboard::llsdStringarrayFromMask
+
+ LLSD key_mask=LLSD::emptyArray();
+
+ if (mask & MASK_CONTROL) { key_mask.append(LLSD("CTL")); } // Mac command key - has code of 0x1 in llcommon/indra_contstants
+ if (mask & MASK_ALT) { key_mask.append(LLSD("ALT")); }
+ if (mask & MASK_SHIFT) { key_mask.append(LLSD("SHIFT")); }
+ if (mask & MASK_MAC_CONTROL) { key_mask.append(LLSD("MAC_CONTROL")); }
+
+ event.insert("mask",key_mask);
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+
+ // Although vita has keyDown and keyUp requests it does not have type as a high-level concept
+ // (maybe it should) - instead it has a convenience method that generates the keydown and keyup events
+ // Here we will use "type" as our event type
+
+ lldebugs << "LLVIewerEventRecorder::logKeyEvent Serialized LLSD for event " << event.asString() << "\n" << llendl;
+
+
+ //lldebugs << "[VITA] key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << "handled by " << getName() << llendl;
+ lldebugs << "LLVIewerEventRecorder::logKeyEvent key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << llendl;
+
+
+ recordEvent(event);
+
+}
+
+void LLViewerEventRecorder::playbackRecording() {
+
+ LLSD LeapCommand;
+
+ // ivita sets this on startup, it also sends commands to the viewer to make start, stop, and playback menu items visible in viewer
+ LeapCommand =LLUI::sSettingGroups["config"]->getLLSD("LeapPlaybackEventsCommand");
+
+ lldebugs << "[VITA] launching playback - leap command is: " << LLSDXMLStreamer(LeapCommand) << llendl;
+ LLLeap::create("", LeapCommand, false); // exception=false
+
+}
+
+
+void LLViewerEventRecorder::recordEvent(LLSD event) {
+ lldebugs << "LLViewerEventRecorder::recordEvent event written to log: " << LLSDXMLStreamer(event) << llendl;
+ mLog << event << std::endl;
+
+}
+void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) {
+ if (! logEvents) return;
+
+ // Note: keyUp is not captured since the viewer seems to not care about keyUp events
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+
+
+ // keysym ...or
+ // keycode...or
+ // char
+
+ lldebugs << "Wrapped in conversion to wstring " << wstring_to_utf8str(LLWString( 1, uni_char)) << "\n" << llendl;
+
+ event.insert("char",
+ LLSD( wstring_to_utf8str(LLWString( 1,uni_char)) )
+ );
+
+ // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
+ // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
+ // break the test script and it would be useful to have more context to make these sorts of edits safer
+
+ // TODO need to consider mask keys too? Doesn't seem possible - at least not easily at this point
+
+ event.insert("event",LLSD("keyDown"));
+
+ lldebugs << "[VITA] unicode key: " << uni_char << llendl;
+ lldebugs << "[VITA] dumpxml " << LLSDXMLStreamer(event) << "\n" << llendl;
+
+
+ recordEvent(event);
+
+}
+
+void LLViewerEventRecorder::logMouseEvent(std::string button_state,std::string button_name)
+{
+ if (! logEvents) return;
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("event",LLSD(std::string("mouse"+ button_state)));
+ event.insert("button",LLSD(button_name));
+ if (xui!="") {
+ event.insert("path",LLSD(xui));
+ }
+
+ if (local_x>0 && local_y>0) {
+ event.insert("local_x",LLSD(local_x));
+ event.insert("local_y",LLSD(local_y));
+ }
+
+ if (global_x>0 && global_y>0) {
+ event.insert("global_x",LLSD(global_x));
+ event.insert("global_y",LLSD(global_y));
+ }
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+ recordEvent(event);
+
+
+ clear(UNDEFINED);
+
+
+}
diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h
new file mode 100644
index 0000000000..72ca643ced
--- /dev/null
+++ b/indra/llui/llviewereventrecorder.h
@@ -0,0 +1,103 @@
+/**
+ * @file llviewereventrecorder.h
+ * @brief Viewer event recording and playback support for mouse and keyboard events
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ *
+ * Copyright (c) 2013, 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_VIEWER_EVENT_RECORDER
+#define LL_VIEWER_EVENT_RECORDER
+
+
+#include "linden_common.h"
+
+#include "lldir.h"
+#include "llsd.h"
+#include "llfile.h"
+#include "llvfile.h"
+#include "lldate.h"
+#include "llsdserialize.h"
+#include "llkeyboard.h"
+#include "llstring.h"
+
+#include <sstream>
+
+#include "llsingleton.h" // includes llerror which we need here so we can skip the include here
+
+class LLViewerEventRecorder : public LLSingleton<LLViewerEventRecorder>
+{
+
+ public:
+
+ LLViewerEventRecorder(); // TODO Protect constructor better if we can (not happy in private section) - could add a factory... - we are singleton
+ ~LLViewerEventRecorder();
+
+
+ void updateMouseEventInfo(S32 local_x,S32 local_y, S32 global_x, S32 global_y, std::string mName);
+ void setMouseLocalCoords(S32 x,S32 y);
+ void setMouseGlobalCoords(S32 x,S32 y);
+
+ void logMouseEvent(std::string button_state, std::string button_name );
+ void logKeyEvent(KEY key, MASK mask);
+ void logKeyUnicodeEvent(llwchar uni_char);
+
+ void logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype);
+
+ void clear_xui();
+ std::string get_xui();
+ void update_xui(std::string xui);
+
+ bool getLoggingStatus();
+ void setEventLoggingOn();
+ void setEventLoggingOff();
+
+ void playbackRecording();
+
+ bool displayViewerEventRecorderMenuItems();
+
+
+ protected:
+ // On if we wish to log events at the moment - toggle via Develop/Recorder submenu
+ bool logEvents;
+
+ std::string mLogFilename;
+ llofstream mLog;
+
+
+ private:
+
+ // Mouse event info
+ S32 global_x;
+ S32 global_y;
+ S32 local_x;
+ S32 local_y;
+
+ // XUI path of UI element
+ std::string xui;
+
+ // Actually write the event out to llsd log file
+ void recordEvent(LLSD event);
+
+ void clear(S32 r);
+
+ static const S32 UNDEFINED=-1;
+};
+#endif
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 97637c937f..53c7b4ff24 100755
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -2656,6 +2656,8 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e
break;
case kEventWindowClose:
+ // Note on event recording - QUIT is a known special case and we are choosing NOT to record it for the record and playback feature
+ // it is handled at a very low-level
if(mCallbacks->handleCloseRequest(this))
{
// Get the app to initiate cleanup.
diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml
index 7ab7787d77..a6e93edc79 100755
--- a/indra/newview/app_settings/cmd_line.xml
+++ b/indra/newview/app_settings/cmd_line.xml
@@ -164,6 +164,14 @@
<string>UserLoginInfo</string>
</map>
+ <key>logevents</key>
+ <map>
+ <key>desc</key>
+ <string>Log ui events for later playback</string>
+ <key>map-to</key>
+ <string>LogEvents</string>
+ </map>
+
<key>logmetrics</key>
<map>
<key>desc</key>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 72916ccf8c..284bc85798 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -4842,6 +4842,16 @@
<key>Value</key>
<array />
</map>
+ <key>LeapPlaybackEventsCommand</key>
+ <map>
+ <key>Comment</key>
+ <string>Command line to use leap to launch playback of event recordings</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>LLSD</string>
+ <key>Value</key>
+ </map>
<key>LSLFindCaseInsensitivity</key>
<map>
<key>Comment</key>
@@ -10117,6 +10127,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>ShowEventRecorderMenuItems</key>
+ <map>
+ <key>Comment</key>
+ <string>Whether or not Event Recorder menu choices - Start / Stop event recording should appear in the (currently) Develop menu</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>ShowGestureButton</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index ffb168b43b..d22376c3b2 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -222,6 +222,10 @@
#include "llmachineid.h"
#include "llmainlooprepeater.h"
+
+#include "llviewereventrecorder.h"
+
+
// *FIX: These extern globals should be cleaned up.
// The globals either represent state/config/resource-storage of either
// this app, or another 'component' of the viewer. App globals should be
@@ -696,6 +700,7 @@ LLAppViewer::LLAppViewer() :
LLAppViewer::~LLAppViewer()
{
delete mSettingsLocationList;
+ LLViewerEventRecorder::instance().~LLViewerEventRecorder();
LLLoginInstance::instance().setUpdaterService(0);
@@ -2361,8 +2366,6 @@ bool LLAppViewer::initConfiguration()
gSavedSettings.setString("ClientSettingsFile",
gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global")));
- gSavedSettings.setString("VersionChannelName", LLVersionInfo::getChannel());
-
#ifndef LL_RELEASE_FOR_DOWNLOAD
// provide developer build only overrides for these control variables that are not
// persisted to settings.xml
@@ -2545,6 +2548,10 @@ bool LLAppViewer::initConfiguration()
}
}
+ if (clp.hasOption("logevents")) {
+ LLViewerEventRecorder::instance().setEventLoggingOn();
+ }
+
if(clp.hasOption("channel"))
{
LLVersionInfo::resetChannel(clp.getOption("channel")[0]);
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 3cf3c739d9..80a80f4298 100755
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -172,21 +172,20 @@ void ll_nvapi_init(NvDRSSessionHandle hSession)
nvapi_error(status);
return;
}
+
+ // (5) Now we apply (or save) our changes to the system
+ status = NvAPI_DRS_SaveSettings(hSession);
+ if (status != NVAPI_OK)
+ {
+ nvapi_error(status);
+ return;
+ }
}
else if (status != NVAPI_OK)
{
nvapi_error(status);
return;
}
-
-
-
- // (5) Now we apply (or save) our changes to the system
- status = NvAPI_DRS_SaveSettings(hSession);
- if (status != NVAPI_OK)
- {
- nvapi_error(status);
- }
}
//#define DEBUGGING_SEH_FILTER 1
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index b513a52ff7..5f1f57f550 100755
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -73,6 +73,8 @@
#include "llcallingcard.h"
#include "llslurl.h" // IDEVO
#include "llsidepanelinventory.h"
+#include "llavatarname.h"
+#include "llagentui.h"
// static
void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::string& name)
@@ -391,6 +393,65 @@ void LLAvatarActions::pay(const LLUUID& id)
}
}
+void LLAvatarActions::teleport_request_callback(const LLSD& notification, const LLSD& response)
+{
+ S32 option;
+ if (response.isInteger())
+ {
+ option = response.asInteger();
+ }
+ else
+ {
+ option = LLNotificationsUtil::getSelectedOption(notification, response);
+ }
+
+ if (0 == option)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
+ msg->addUUIDFast(_PREHASH_ToAgentID, notification["substitutions"]["uuid"] );
+ msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
+ msg->addU8Fast(_PREHASH_Dialog, IM_TELEPORT_REQUEST);
+ msg->addUUIDFast(_PREHASH_ID, LLUUID::null);
+ msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
+
+ std::string name;
+ LLAgentUI::buildFullname(name);
+
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ msg->addStringFast(_PREHASH_Message, response["message"]);
+ msg->addU32Fast(_PREHASH_ParentEstateID, 0);
+ msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
+ msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
+
+ gMessageSystem->addBinaryDataFast(
+ _PREHASH_BinaryBucket,
+ EMPTY_BINARY_BUCKET,
+ EMPTY_BINARY_BUCKET_SIZE);
+
+ gAgent.sendReliableMessage();
+ }
+}
+
+// static
+void LLAvatarActions::teleportRequest(const LLUUID& id)
+{
+ LLSD notification;
+ notification["uuid"] = id;
+ notification["NAME_SLURL"] = LLSLURL("agent", id, "about").getSLURLString();
+
+ LLSD payload;
+
+ LLNotificationsUtil::add("TeleportRequestPrompt", notification, payload, teleport_request_callback);
+}
+
// static
void LLAvatarActions::kick(const LLUUID& id)
{
diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h
index 6e1198cd09..403414558e 100755
--- a/indra/newview/llavataractions.h
+++ b/indra/newview/llavataractions.h
@@ -111,6 +111,12 @@ public:
static void pay(const LLUUID& id);
/**
+ * Request teleport from other avatar
+ */
+ static void teleportRequest(const LLUUID& id);
+ static void teleport_request_callback(const LLSD& notification, const LLSD& response);
+
+ /**
* Share items with the avatar.
*/
static void share(const LLUUID& id);
diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp
index 88884042d4..131aea9da3 100755
--- a/indra/newview/llchiclet.cpp
+++ b/indra/newview/llchiclet.cpp
@@ -220,18 +220,25 @@ void LLNotificationChiclet::setCounter(S32 counter)
bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification )
{
- if (notification->getName() == "ScriptDialog")
+ bool displayNotification;
+ if ( (notification->getName() == "ScriptDialog") // special case for scripts
+ // if there is no toast window for the notification, filter it
+ || (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID()))
+ )
{
- return false;
+ displayNotification = false;
}
-
- if( !(notification->canLogToIM() && notification->hasFormElements())
- && (!notification->getPayload().has("give_inventory_notification")
- || notification->getPayload()["give_inventory_notification"]))
+ else if( !(notification->canLogToIM() && notification->hasFormElements())
+ && (!notification->getPayload().has("give_inventory_notification")
+ || notification->getPayload()["give_inventory_notification"]))
{
- return true;
+ displayNotification = true;
}
- return false;
+ else
+ {
+ displayNotification = false;
+ }
+ return displayNotification;
}
//////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp
index 5ab108b39f..44212298cf 100755
--- a/indra/newview/llconversationloglist.cpp
+++ b/indra/newview/llconversationloglist.cpp
@@ -313,6 +313,10 @@ void LLConversationLogList::onCustomAction(const LLSD& userdata)
{
LLAvatarActions::offerTeleport(selected_conversation_participant_id);
}
+ else if ("request_teleport" == command_name)
+ {
+ LLAvatarActions::teleportRequest(selected_conversation_participant_id);
+ }
else if("add_friend" == command_name)
{
if (!LLAvatarActions::isFriend(selected_conversation_participant_id))
diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp
index c74ce24872..b0aaa21ec9 100755
--- a/indra/newview/llconversationmodel.cpp
+++ b/indra/newview/llconversationmodel.cpp
@@ -132,6 +132,7 @@ void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32
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("request_teleport"));
items.push_back(std::string("voice_call"));
items.push_back(std::string("chat_history"));
items.push_back(std::string("separator_chat_history"));
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index b40789db9c..1fbe1be11a 100755
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -1046,6 +1046,10 @@ void LLFloaterIMContainer::doToParticipants(const std::string& command, uuid_vec
{
LLAvatarActions::offerTeleport(selectedIDS);
}
+ else if ("request_teleport" == command)
+ {
+ LLAvatarActions::teleportRequest(selectedIDS.front());
+ }
else if ("voice_call" == command)
{
LLAvatarActions::startCall(userID);
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 50c013a49d..66bf49331b 100755
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -1734,7 +1734,7 @@ void LLPanelEstateInfo::accessAddCore3(const uuid_vec_t& ids, void* data)
LLSD args;
args["NUM_ADDED"] = llformat("%d",ids.size());
args["MAX_AGENTS"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
- args["LIST_TYPE"] = "Allowed Residents";
+ args["LIST_TYPE"] = LLTrans::getString("RegionInfoListTypeAllowedAgents");
args["NUM_EXCESS"] = llformat("%d",(ids.size()+currentCount)-ESTATE_MAX_ACCESS_IDS);
LLNotificationsUtil::add("MaxAgentOnRegionBatch", args);
delete change_info;
@@ -1750,7 +1750,7 @@ void LLPanelEstateInfo::accessAddCore3(const uuid_vec_t& ids, void* data)
LLSD args;
args["NUM_ADDED"] = llformat("%d",ids.size());
args["MAX_AGENTS"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
- args["LIST_TYPE"] = "Banned Residents";
+ args["LIST_TYPE"] = LLTrans::getString("RegionInfoListTypeBannedAgents");
args["NUM_EXCESS"] = llformat("%d",(ids.size()+currentCount)-ESTATE_MAX_ACCESS_IDS);
LLNotificationsUtil::add("MaxAgentOnRegionBatch", args);
delete change_info;
@@ -2815,9 +2815,10 @@ bool LLDispatchSetEstateAccess::operator()(
}
- std::string msg = llformat("Banned residents: (%d, max %d)",
- totalBannedAgents,
- ESTATE_MAX_ACCESS_IDS);
+ LLStringUtil::format_map_t args;
+ args["[BANNEDAGENTS]"] = llformat("%d", totalBannedAgents);
+ args["[MAXBANNED]"] = llformat("%d", ESTATE_MAX_ACCESS_IDS);
+ std::string msg = LLTrans::getString("RegionInfoBannedResidents", args);
panel->getChild<LLUICtrl>("ban_resident_label")->setValue(LLSD(msg));
if (banned_agent_name_list)
@@ -2837,9 +2838,10 @@ bool LLDispatchSetEstateAccess::operator()(
if (access_flags & ESTATE_ACCESS_MANAGERS)
{
- std::string msg = llformat("Estate Managers: (%d, max %d)",
- num_estate_managers,
- ESTATE_MAX_MANAGERS);
+ LLStringUtil::format_map_t args;
+ args["[ESTATEMANAGERS]"] = llformat("%d", num_estate_managers);
+ args["[MAXMANAGERS]"] = llformat("%d", ESTATE_MAX_MANAGERS);
+ std::string msg = LLTrans::getString("RegionInfoEstateManagers", args);
panel->getChild<LLUICtrl>("estate_manager_label")->setValue(LLSD(msg));
LLNameListCtrl* estate_manager_name_list =
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index e4fc469bb7..80ef506272 100755
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -4739,6 +4739,16 @@ void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string act
LLAvatarActions::offerTeleport(item->getCreatorUUID());
}
}
+ else if ("request_lure" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ LLAvatarActions::teleportRequest(item->getCreatorUUID());
+ }
+ }
+
else LLItemBridge::performAction(model, action);
}
@@ -4825,6 +4835,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
items.push_back(std::string("Send Instant Message Separator"));
items.push_back(std::string("Send Instant Message"));
items.push_back(std::string("Offer Teleport..."));
+ items.push_back(std::string("Request Teleport..."));
items.push_back(std::string("Conference Chat"));
if (!good_card)
@@ -4834,6 +4845,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
if (!good_card || !user_online)
{
disabled_items.push_back(std::string("Offer Teleport..."));
+ disabled_items.push_back(std::string("Request Teleport..."));
disabled_items.push_back(std::string("Conference Chat"));
}
}
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index dea90b9042..08b5eaedbb 100755
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -343,8 +343,11 @@ void LLNetMap::draw()
// Draw avatars
for (U32 i = 0; i < avatar_ids.size(); i++)
{
- pos_map = globalPosToView(positions[i]);
LLUUID uuid = avatar_ids[i];
+ // Skip self, we'll draw it later
+ if (uuid == gAgent.getID()) continue;
+
+ pos_map = globalPosToView(positions[i]);
bool show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(uuid) != NULL);
diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp
index 08c98e4f28..56ed1044e9 100755
--- a/indra/newview/llnotificationscripthandler.cpp
+++ b/indra/newview/llnotificationscripthandler.cpp
@@ -87,7 +87,7 @@ bool LLScriptHandler::processNotification(const LLNotificationPtr& notification)
{
LLScriptFloaterManager::getInstance()->onAddNotification(notification->getID());
}
- else
+ else if (notification->canShowToast())
{
LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification);
diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp
index cfdac11d26..fdcd1f5ebb 100755
--- a/indra/newview/llpanelgrouproles.cpp
+++ b/indra/newview/llpanelgrouproles.cpp
@@ -1101,27 +1101,61 @@ void LLPanelGroupMembersSubTab::onEjectMembers(void *userdata)
}
void LLPanelGroupMembersSubTab::handleEjectMembers()
-{
- //send down an eject message
- uuid_vec_t selected_members;
-
+{
std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
if (selection.empty()) return;
-
- std::vector<LLScrollListItem*>::iterator itor;
- for (itor = selection.begin() ;
- itor != selection.end(); ++itor)
+
+ S32 selection_count = selection.size();
+ if (selection_count == 1)
{
- LLUUID member_id = (*itor)->getUUID();
- selected_members.push_back( member_id );
+ LLSD args;
+ LLUUID selected_avatar = mMembersList->getValue().asUUID();
+ std::string fullname = LLSLURL("agent", selected_avatar, "inspect").getSLURLString();
+ args["AVATAR_NAME"] = fullname;
+ LLSD payload;
+ LLNotificationsUtil::add("EjectGroupMemberWarning",
+ args,
+ payload,
+ boost::bind(&LLPanelGroupMembersSubTab::handleEjectCallback, this, _1, _2));
}
+ else
+ {
+ LLSD args;
+ args["COUNT"] = llformat("%d", selection_count);
+ LLSD payload;
+ LLNotificationsUtil::add("EjectGroupMembersWarning",
+ args,
+ payload,
+ boost::bind(&LLPanelGroupMembersSubTab::handleEjectCallback, this, _1, _2));
+ }
+}
- mMembersList->deleteSelectedItems();
-
- sendEjectNotifications(mGroupID, selected_members);
-
- LLGroupMgr::getInstance()->sendGroupMemberEjects(mGroupID,
- selected_members);
+bool LLPanelGroupMembersSubTab::handleEjectCallback(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (0 == option) // Eject button
+ {
+ //send down an eject message
+ uuid_vec_t selected_members;
+
+ std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+ if (selection.empty()) return false;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin() ;
+ itor != selection.end(); ++itor)
+ {
+ LLUUID member_id = (*itor)->getUUID();
+ selected_members.push_back( member_id );
+ }
+
+ mMembersList->deleteSelectedItems();
+
+ sendEjectNotifications(mGroupID, selected_members);
+
+ LLGroupMgr::getInstance()->sendGroupMemberEjects(mGroupID, selected_members);
+ }
+ return false;
}
void LLPanelGroupMembersSubTab::sendEjectNotifications(const LLUUID& group_id, const uuid_vec_t& selected_members)
diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h
index 78bb3c57a1..0cf272f3ee 100755
--- a/indra/newview/llpanelgrouproles.h
+++ b/indra/newview/llpanelgrouproles.h
@@ -167,6 +167,7 @@ public:
static void onEjectMembers(void*);
void handleEjectMembers();
void sendEjectNotifications(const LLUUID& group_id, const uuid_vec_t& selected_members);
+ bool handleEjectCallback(const LLSD& notification, const LLSD& response);
static void onRoleCheck(LLUICtrl* check, void* user_data);
void handleRoleCheck(const LLUUID& role_id,
diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp
index 49f7361c4a..0b2bf1d2c8 100755
--- a/indra/newview/llpanelpeoplemenus.cpp
+++ b/indra/newview/llpanelpeoplemenus.cpp
@@ -74,6 +74,7 @@ LLContextMenu* PeopleContextMenu::createMenu()
registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, id));
registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, id));
registrar.add("Avatar.InviteToGroup", boost::bind(&LLAvatarActions::inviteToGroup, id));
+ registrar.add("Avatar.TeleportRequest", boost::bind(&PeopleContextMenu::requestTeleport, this));
registrar.add("Avatar.Calllog", boost::bind(&LLAvatarActions::viewChatHistory, id));
enable_registrar.add("Avatar.EnableItem", boost::bind(&PeopleContextMenu::enableContextMenuItem, this, _2));
@@ -125,6 +126,7 @@ void PeopleContextMenu::buildContextMenu(class LLMenuGL& menu, 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("request_teleport"));
items.push_back(std::string("voice_call"));
items.push_back(std::string("chat_history"));
items.push_back(std::string("separator_chat_history"));
@@ -255,6 +257,13 @@ bool PeopleContextMenu::checkContextMenuItem(const LLSD& userdata)
return false;
}
+void PeopleContextMenu::requestTeleport()
+{
+ // boost::bind cannot recognize overloaded method LLAvatarActions::teleportRequest(),
+ // so we have to use a wrapper.
+ LLAvatarActions::teleportRequest(mUUIDs.front());
+}
+
void PeopleContextMenu::offerTeleport()
{
// boost::bind cannot recognize overloaded method LLAvatarActions::offerTeleport(),
@@ -284,6 +293,7 @@ void NearbyPeopleContextMenu::buildContextMenu(class LLMenuGL& menu, 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("request_teleport"));
items.push_back(std::string("voice_call"));
items.push_back(std::string("chat_history"));
items.push_back(std::string("separator_chat_history"));
diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h
index 0a1dcef303..abf5fa05e4 100755
--- a/indra/newview/llpanelpeoplemenus.h
+++ b/indra/newview/llpanelpeoplemenus.h
@@ -47,6 +47,7 @@ private:
bool enableContextMenuItem(const LLSD& userdata);
bool checkContextMenuItem(const LLSD& userdata);
void offerTeleport();
+ void requestTeleport();
};
/**
diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp
index e92bd766ca..4846c54189 100755
--- a/indra/newview/llsyswellwindow.cpp
+++ b/indra/newview/llsyswellwindow.cpp
@@ -118,6 +118,11 @@ void LLSysWellWindow::removeItemByID(const LLUUID& id)
}
}
+ LLPanel * LLSysWellWindow::findItemByID(const LLUUID& id)
+{
+ return mMessageList->getItemByValue(id);
+}
+
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void LLSysWellWindow::initChannel()
diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h
index cc5c057d8b..71b41476f5 100755
--- a/indra/newview/llsyswellwindow.h
+++ b/indra/newview/llsyswellwindow.h
@@ -55,6 +55,7 @@ public:
// Operating with items
void removeItemByID(const LLUUID& id);
+ LLPanel * findItemByID(const LLUUID& id);
// Operating with outfit
virtual void setVisible(BOOL visible);
diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp
index 4ecdc31e21..a8e82c6153 100755
--- a/indra/newview/llviewerkeyboard.cpp
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -681,7 +681,10 @@ BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL
{
// it is sufficient to set this value once per call to handlekey
// without clearing it, as it is only used in the subsequent call to scanKey
- mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask);
+ mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask);
+ // mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress)
+ // NOT whether some UI shortcut wishes to handle the keypress
+
}
return mKeyHandledByUI[translated_key];
}
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 49eb7dc94a..323eac19a4 100755
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -40,6 +40,7 @@
#include "llinventorypanel.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
+#include "llviewereventrecorder.h"
// newview includes
#include "llagent.h"
@@ -1954,6 +1955,43 @@ class LLAdvancedDropPacket : public view_listener_t
};
+////////////////////
+// EVENT Recorder //
+///////////////////
+
+
+class LLAdvancedViewerEventRecorder : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ std::string command = userdata.asString();
+ if ("start playback" == command)
+ {
+ llinfos << "Event Playback starting" << llendl;
+ LLViewerEventRecorder::instance().playbackRecording();
+ llinfos << "Event Playback completed" << llendl;
+ }
+ else if ("stop playback" == command)
+ {
+ // Future
+ }
+ else if ("start recording" == command)
+ {
+ LLViewerEventRecorder::instance().setEventLoggingOn();
+ llinfos << "Event recording started" << llendl;
+ }
+ else if ("stop recording" == command)
+ {
+ LLViewerEventRecorder::instance().setEventLoggingOff();
+ llinfos << "Event recording stopped" << llendl;
+ }
+
+ return true;
+ }
+};
+
+
+
/////////////////
// AGENT PILOT //
@@ -8361,6 +8399,8 @@ void initialize_menus()
// Don't prepend MenuName.Foo because these can be used in any menu.
enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service));
+ enable.add("displayViewerEventRecorderMenuItems",boost::bind(&LLViewerEventRecorder::displayViewerEventRecorderMenuItems,&LLViewerEventRecorder::instance()));
+
view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts");
enable.add("Conversation.IsConversationLoggingAllowed", boost::bind(&LLFloaterIMContainer::isConversationLoggingAllowed));
@@ -8619,6 +8659,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedAgentPilot(), "Advanced.AgentPilot");
view_listener_t::addMenu(new LLAdvancedToggleAgentPilotLoop(), "Advanced.ToggleAgentPilotLoop");
view_listener_t::addMenu(new LLAdvancedCheckAgentPilotLoop(), "Advanced.CheckAgentPilotLoop");
+ view_listener_t::addMenu(new LLAdvancedViewerEventRecorder(), "Advanced.EventRecorder");
// Advanced > Debugging
view_listener_t::addMenu(new LLAdvancedForceErrorBreakpoint(), "Advanced.ForceErrorBreakpoint");
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 0db118835c..f744f5ace2 100755
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2216,7 +2216,7 @@ static std::string clean_name_from_im(const std::string& name, EInstantMessage t
case IM_LURE_ACCEPTED:
case IM_LURE_DECLINED:
case IM_GODLIKE_LURE_USER:
- case IM_YET_TO_BE_USED:
+ case IM_TELEPORT_REQUEST:
case IM_GROUP_ELECTION_DEPRECATED:
//IM_GOTO_URL
//IM_FROM_TASK_AS_ALERT
@@ -2981,6 +2981,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
break;
case IM_LURE_USER:
+ case IM_TELEPORT_REQUEST:
{
if (is_muted)
{
@@ -3003,7 +3004,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
bool canUserAccessDstRegion = true;
bool doesUserRequireMaturityIncrease = false;
- if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access))
+ // Do not parse the (empty) lure bucket for TELEPORT_REQUEST
+ if (IM_TELEPORT_REQUEST != dialog && parse_lure_bucket(region_info, region_handle, pos, look_at, region_access))
{
region_access_str = LLViewerRegion::accessToString(region_access);
region_access_icn = LLViewerRegion::getAccessIcon(region_access);
@@ -3075,12 +3077,22 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
}
else
{
- LLNotification::Params params("TeleportOffered");
+ LLNotification::Params params;
+ if (IM_LURE_USER == dialog)
+ {
+ params.name = "TeleportOffered";
+ params.functor.name = "TeleportOffered";
+ }
+ else if (IM_TELEPORT_REQUEST == dialog)
+ {
+ params.name = "TeleportRequest";
+ params.functor.name = "TeleportRequest";
+ }
+
params.substitutions = args;
params.payload = payload;
LLPostponedNotification::add<LLPostponedOfferNotification>( params, from_id, false);
}
-
}
}
break;
@@ -6844,6 +6856,51 @@ void send_group_notice(const LLUUID& group_id,
bin_bucket_size);
}
+void send_lures(const LLSD& notification, const LLSD& response)
+{
+ std::string text = response["message"].asString();
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl);
+ text.append("\r\n").append(slurl.getSLURLString());
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_StartLure);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in.
+ msg->addStringFast(_PREHASH_Message, text);
+ for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray();
+ it != notification["payload"]["ids"].endArray();
+ ++it)
+ {
+ LLUUID target_id = it->asUUID();
+
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_TargetID, target_id);
+
+ // Record the offer.
+ {
+ std::string target_name;
+ gCacheName->getFullName(target_id, target_name); // for im log filenames
+ LLSD args;
+ args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();;
+
+ LLSD payload;
+
+ //*TODO please rewrite all keys to the same case, lower or upper
+ payload["from_id"] = target_id;
+ payload["SUPPRESS_TOAST"] = true;
+ LLNotificationsUtil::add("TeleportOfferSent", args, payload);
+
+ // Add the recepient to the recent people list.
+ LLRecentPeople::instance().add(target_id);
+ }
+ }
+ gAgent.sendReliableMessage();
+}
+
bool handle_lure_callback(const LLSD& notification, const LLSD& response)
{
static const unsigned OFFER_RECIPIENT_LIMIT = 250;
@@ -6857,50 +6914,12 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response)
LLNotificationsUtil::add("TooManyTeleportOffers", args);
return false;
}
-
- std::string text = response["message"].asString();
- LLSLURL slurl;
- LLAgentUI::buildSLURL(slurl);
- text.append("\r\n").append(slurl.getSLURLString());
+
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if(0 == option)
{
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_StartLure);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_Info);
- msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in.
- msg->addStringFast(_PREHASH_Message, text);
- for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray();
- it != notification["payload"]["ids"].endArray();
- ++it)
- {
- LLUUID target_id = it->asUUID();
-
- msg->nextBlockFast(_PREHASH_TargetData);
- msg->addUUIDFast(_PREHASH_TargetID, target_id);
-
- // Record the offer.
- {
- std::string target_name;
- gCacheName->getFullName(target_id, target_name); // for im log filenames
- LLSD args;
- args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();;
-
- LLSD payload;
-
- //*TODO please rewrite all keys to the same case, lower or upper
- payload["from_id"] = target_id;
- LLNotificationsUtil::add("TeleportOfferSent", args, payload);
-
- // Add the recepient to the recent people list.
- LLRecentPeople::instance().add(target_id);
- }
- }
- gAgent.sendReliableMessage();
+ send_lures(notification, response);
}
return false;
@@ -6940,6 +6959,58 @@ void handle_lure(const uuid_vec_t& ids)
}
}
+bool teleport_request_callback(const LLSD& notification, const LLSD& response)
+{
+ LLUUID from_id = notification["payload"]["from_id"].asUUID();
+ if(from_id.isNull())
+ {
+ llwarns << "from_id is NULL" << llendl;
+ return false;
+ }
+
+ std::string from_name;
+ gCacheName->getFullName(from_id, from_name);
+
+ if(LLMuteList::getInstance()->isMuted(from_id) && !LLMuteList::getInstance()->isLinden(from_name))
+ {
+ return false;
+ }
+
+ S32 option = 0;
+ if (response.isInteger())
+ {
+ option = response.asInteger();
+ }
+ else
+ {
+ option = LLNotificationsUtil::getSelectedOption(notification, response);
+ }
+
+ switch(option)
+ {
+ // Yes
+ case 0:
+ {
+ LLSD dummy_notification;
+ dummy_notification["payload"]["ids"][0] = from_id;
+
+ LLSD dummy_response;
+ dummy_response["message"] = response["message"];
+
+ send_lures(dummy_notification, dummy_response);
+ }
+ break;
+
+ // No
+ case 1:
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static LLNotificationFunctorRegistration teleport_request_callback_reg("TeleportRequest", teleport_request_callback);
void send_improved_im(const LLUUID& to_id,
const std::string& name,
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 87ae224655..e98a2cb32c 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -197,6 +197,8 @@
#include "llagentui.h"
#include "llwearablelist.h"
+#include "llviewereventrecorder.h"
+
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llnotificationmanager.h"
@@ -916,27 +918,18 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK
{
llinfos << buttonname << " Mouse " << buttonstatestr << " handled by captor " << mouse_captor->getName() << llendl;
}
- return mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down);
- }
- // Topmost view gets a chance before the hierarchy
- //LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
- //if (top_ctrl)
- //{
- // S32 local_x, local_y;
- // top_ctrl->screenPointToLocal( x, y, &local_x, &local_y );
- // if (top_ctrl->pointInView(local_x, local_y))
- // {
- // return top_ctrl->handleAnyMouseClick(local_x, local_y, mask, clicktype, down) ;
- // }
- // else
- // {
- // if (down)
- // {
- // gFocusMgr.setTopCtrl(NULL);
- // }
- // }
- //}
+ BOOL r = mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down);
+ if (r) {
+
+ lldebugs << "LLViewerWindow::handleAnyMouseClick viewer with mousecaptor calling updatemouseeventinfo - local_x|global x "<< local_x << " " << x << "local/global y " << local_y << " " << y << llendl;
+
+ LLViewerEventRecorder::instance().setMouseGlobalCoords(x,y);
+ LLViewerEventRecorder::instance().logMouseEvent(std::string(buttonstatestr),std::string(buttonname));
+
+ }
+ return r;
+ }
// Mark the click as handled and return if we aren't within the root view to avoid spurious bugs
if( !mRootView->pointInView(x, y) )
@@ -944,27 +937,44 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK
return TRUE;
}
// Give the UI views a chance to process the click
- if( mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) )
+
+ BOOL r= mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) ;
+ if (r)
{
+
+ lldebugs << "LLViewerWindow::handleAnyMouseClick calling updatemouseeventinfo - global x "<< " " << x << "global y " << y << "buttonstate: " << buttonstatestr << " buttonname " << buttonname << llendl;
+
+ LLViewerEventRecorder::instance().setMouseGlobalCoords(x,y);
+
+ // Clear local coords - this was a click on root window so these are not needed
+ // By not including them, this allows the test skeleton generation tool to be smarter when generating code
+ // the code generator can be smarter because when local coords are present it can try the xui path with local coords
+ // and fallback to global coordinates only if needed.
+ // The drawback to this approach is sometimes a valid xui path will appear to work fine, but NOT interact with the UI element
+ // (VITA support not implemented yet or not visible to VITA due to widget further up xui path not being visible to VITA)
+ // For this reason it's best to provide hints where possible here by leaving out local coordinates
+ LLViewerEventRecorder::instance().setMouseLocalCoords(-1,-1);
+ LLViewerEventRecorder::instance().logMouseEvent(buttonstatestr,buttonname);
+
if (LLView::sDebugMouseHandling)
{
- llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLView::sMouseHandlerMessage << llendl;
- }
+ llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLViewerEventRecorder::instance().get_xui() << llendl;
+ }
return TRUE;
- }
- else if (LLView::sDebugMouseHandling)
- {
- llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl;
- }
+ } else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl;
+ }
}
// Do not allow tool manager to handle mouseclicks if we have disconnected
if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) )
{
+ LLViewerEventRecorder::instance().clear_xui();
return TRUE;
}
-
+
// If we got this far on a down-click, it wasn't handled.
// Up-clicks, though, are always handled as far as the OS is concerned.
BOOL default_rtn = !down;
@@ -1335,7 +1345,8 @@ BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask)
void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
{
LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true);
- return gViewerKeyboard.scanKey(key, key_down, key_up, key_level);
+ gViewerKeyboard.scanKey(key, key_down, key_up, key_level);
+ return; // Be clear this function returns nothing
}
@@ -2479,6 +2490,8 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
||(gLoginMenuBarView && gLoginMenuBarView->handleKey(key, mask, TRUE))
||(gMenuHolder && gMenuHolder->handleKey(key, mask, TRUE)))
{
+ lldebugs << "LLviewerWindow::handleKey handle nav keys for nav" << llendl;
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
}
@@ -2493,12 +2506,14 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
&& keyboard_focus
&& keyboard_focus->handleKey(key,mask,FALSE))
{
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
}
if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask))
||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask)))
{
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
}
}
@@ -2508,6 +2523,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
// if nothing has focus, go to first or last UI element as appropriate
if (key == KEY_TAB && (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL))
{
+ llwarns << "LLviewerWindow::handleKey give floaters first chance at tab key " << llendl;
if (gMenuHolder) gMenuHolder->hideMenus();
// if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode
@@ -2522,11 +2538,13 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
{
mRootView->focusNextRoot();
}
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
}
// hidden edit menu for cut/copy/paste
if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask))
{
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
}
@@ -2566,18 +2584,27 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
if (keyboard_focus->handleKey(key, mask, FALSE))
{
+
+ lldebugs << "LLviewerWindow::handleKey - in 'traverse up' - no loops seen... just called keyboard_focus->handleKey an it returned true" << llendl;
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
+ } else {
+ lldebugs << "LLviewerWindow::handleKey - in 'traverse up' - no loops seen... just called keyboard_focus->handleKey an it returned FALSE" << llendl;
}
}
if( LLToolMgr::getInstance()->getCurrentTool()->handleKey(key, mask) )
{
+ lldebugs << "LLviewerWindow::handleKey toolbar handling?" << llendl;
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
}
// Try for a new-format gesture
if (LLGestureMgr::instance().triggerGesture(key, mask))
{
+ lldebugs << "LLviewerWindow::handleKey new gesture feature" << llendl;
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
}
@@ -2585,6 +2612,8 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
// don't pass it down to the menus.
if (gGestureList.trigger(key, mask))
{
+ lldebugs << "LLviewerWindow::handleKey check gesture trigger" << llendl;
+ LLViewerEventRecorder::instance().logKeyEvent(key,mask);
return TRUE;
}
@@ -2633,7 +2662,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
// HACK: Numeric keypad <enter> on Mac is Unicode 3
// HACK: Control-M on Windows is Unicode 13
if ((uni_char == 13 && mask != MASK_CONTROL)
- || (uni_char == 3 && mask == MASK_NONE))
+ || (uni_char == 3 && mask == MASK_NONE) )
{
if (mask != MASK_ALT)
{
@@ -2656,14 +2685,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
return TRUE;
}
- //// Topmost view gets a chance before the hierarchy
- //LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
- //if (top_ctrl && top_ctrl->handleUnicodeChar( uni_char, FALSE ) )
- //{
- // return TRUE;
- //}
-
- return TRUE;
+ return TRUE;
}
return FALSE;
@@ -2672,8 +2694,6 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
void LLViewerWindow::handleScrollWheel(S32 clicks)
{
- LLView::sMouseHandlerMessage.clear();
-
LLUI::resetMouseIdleTimer();
LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp
index 28f959eb71..a8e06511d7 100755
--- a/indra/newview/llwindowlistener.cpp
+++ b/indra/newview/llwindowlistener.cpp
@@ -265,7 +265,9 @@ void LLWindowListener::getPaths(LLSD const & request)
void LLWindowListener::keyDown(LLSD const & evt)
{
Response response(LLSD(), evt);
-
+ KEY key = getKEY(evt);
+ MASK mask = getMask(evt);
+
if (evt.has("path"))
{
std::string path(evt["path"]);
@@ -280,8 +282,6 @@ void LLWindowListener::keyDown(LLSD const & evt)
response.setResponse(target_view->getInfo());
gFocusMgr.setKeyboardFocus(target_view);
- KEY key = getKEY(evt);
- MASK mask = getMask(evt);
gViewerKeyboard.handleKey(key, mask, false);
if(key < 0x80) mWindow->handleUnicodeChar(key, mask);
}
@@ -294,7 +294,8 @@ void LLWindowListener::keyDown(LLSD const & evt)
}
else
{
- mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt));
+ gViewerKeyboard.handleKey(key, mask, false);
+ if(key < 0x80) mWindow->handleUnicodeChar(key, mask);
}
}
diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp
index 5fa380e0e3..bfae142812 100755
--- a/indra/newview/llworldmap.cpp
+++ b/indra/newview/llworldmap.cpp
@@ -522,6 +522,17 @@ bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID&
tooltip_fmt.setArg("[AREA]", llformat("%d", extra));
tooltip_fmt.setArg("[PRICE]", llformat("%d", extra2));
+
+ // Check for division by zero
+ if (extra != 0)
+ {
+ tooltip_fmt.setArg("[SQMPRICE]", llformat("%.1f", (F32)extra2 / (F32)extra));
+ }
+ else
+ {
+ tooltip_fmt.setArg("[SQMPRICE]", LLTrans::getString("Unknown"));
+ }
+
new_item.setTooltip(tooltip_fmt.getString());
if (type == MAP_ITEM_LAND_FOR_SALE)
diff --git a/indra/newview/skins/default/xui/en/menu_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml
index 5a13ef0a59..d8eb2f0ffd 100755
--- a/indra/newview/skins/default/xui/en/menu_conversation.xml
+++ b/indra/newview/skins/default/xui/en/menu_conversation.xml
@@ -47,6 +47,13 @@
<on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/>
</menu_item_call>
<menu_item_call
+ label="Request teleport"
+ layout="topleft"
+ name="request_teleport">
+ <on_click function="Avatar.DoToSelected" parameter="request_teleport"/>
+ <on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/>
+ </menu_item_call>
+ <menu_item_call
label="Voice call"
layout="topleft"
name="voice_call">
diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml
index 8796b87955..a1a3afbf68 100755
--- a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml
@@ -56,6 +56,16 @@
function="Calllog.Enable"
parameter="can_offer_teleport"/>
</menu_item_call>
+ <menu_item_call
+ label="Request Teleport"
+ name="request_teleport">
+ <on_click
+ function="Calllog.Action"
+ parameter="request_teleport"/>
+ <on_enable
+ function="Calllog.Enable"
+ parameter="can_offer_teleport"/>
+ </menu_item_call>
<menu_item_separator />
<menu_item_call
label="Add Friend"
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index 13dc0b941a..512205ba43 100755
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -591,6 +591,14 @@
parameter="lure" />
</menu_item_call>
<menu_item_call
+ label="Request Teleport..."
+ layout="topleft"
+ name="Request Teleport...">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="request_lure" />
+ </menu_item_call>
+ <menu_item_call
label="Start Conference Chat"
layout="topleft"
name="Conference Chat">
diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml
index 3abb5f7bc8..f12226ebeb 100755
--- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml
+++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml
@@ -29,6 +29,15 @@
parameter="can_offer_teleport"/>
</menu_item_call>
<menu_item_call
+ label="Request Teleport"
+ name="request_teleport">
+ <menu_item_call.on_click
+ function="Avatar.TeleportRequest"/>
+ <menu_item_call.on_enable
+ function="Avatar.EnableItem"
+ parameter="can_offer_teleport"/>
+ </menu_item_call>
+ <menu_item_call
label="Voice call"
layout="topleft"
name="voice_call">
@@ -134,5 +143,4 @@
function="Avatar.EnableItem"
parameter="can_block" />
</menu_item_check>
- <menu_item_separator />
</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index b01c3067ff..b71faa2d3e 100755
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2919,6 +2919,34 @@
label="Recorder"
name="Recorder"
tear_off="true">
+ <menu_item_call visible="false"
+ label="Start event recording"
+ name="Start event recording">
+ <menu_item_call.on_visible
+ function="displayViewerEventRecorderMenuItems" />
+ <menu_item_call.on_click
+ function="Advanced.EventRecorder"
+ parameter="start recording" />
+ </menu_item_call>
+ <menu_item_call visible="false"
+ label="Stop event recording"
+ name="Stop event recording">
+ <menu_item_call.on_visible
+ function="displayViewerEventRecorderMenuItems" />
+ <menu_item_call.on_click
+ function="Advanced.EventRecorder"
+ parameter="stop recording" />
+ </menu_item_call>
+ <menu_item_call visible="false"
+ label="Playback event recording"
+ name="Playback event recording">
+ <menu_item_call.on_visible
+ function="displayViewerEventRecorderMenuItems" />
+ <menu_item_call.on_click
+ function="Advanced.EventRecorder"
+ parameter="start playback" />
+ </menu_item_call>
+
<menu_item_call
label="Start Playback"
name="Start Playback">
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 2e0e8bf885..1867141758 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -506,6 +506,33 @@ Add this Ability to &apos;[ROLE_NAME]&apos;?
notext="No"
yestext="Yes"/>
</notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="EjectGroupMemberWarning"
+ type="alertmodal">
+ You are about to eject [AVATAR_NAME] from the group.
+ <tag>group</tag>
+ <tag>confirm</tag>
+ <usetemplate
+ ignoretext="Confirm ejecting a participant from group"
+ name="okcancelignore"
+ notext="Cancel"
+ yestext="Eject"/>
+ </notification>
+ <notification
+ icon="alertmodal.tga"
+ name="EjectGroupMembersWarning"
+ type="alertmodal">
+ You are about to eject [COUNT] members from the group.
+ <tag>group</tag>
+ <tag>confirm</tag>
+ <usetemplate
+ ignoretext="Confirm ejecting multiple members from group"
+ name="okcancelignore"
+ notext="Cancel"
+ yestext="Eject"/>
+ </notification>
<notification
icon="alertmodal.tga"
@@ -2682,7 +2709,7 @@ Please enter a higher price.
icon="alertmodal.tga"
name="ConfirmItemDeleteHasLinks"
type="alertmodal">
-At least one of the items you has link items that point to it. If you delete this item, its links will permanently stop working. It is strongly advised to delete the links first.
+At least one of the items has links that point to it. If you delete this item, its links will permanently stop working. It is strongly advised to delete the links first.
Are you sure you want to delete these items?
<tag>confirm</tag>
@@ -2724,7 +2751,7 @@ Are you sure you want to delete these items?
icon="alertmodal.tga"
name="ConfirmObjectDeleteNoOwn"
type="alertmodal">
-You do not own least one of the items you have selected.
+You do not own at least one of the items you have selected.
Are you sure you want to delete these items?
<tag>confirm</tag>
@@ -2754,7 +2781,7 @@ Are you sure you want to delete these items?
name="ConfirmObjectDeleteLockNoOwn"
type="alertmodal">
At least one object is locked.
-You do not own least one object.
+You do not own at least one object.
Are you sure you want to delete these items?
<tag>confirm</tag>
@@ -2769,7 +2796,7 @@ Are you sure you want to delete these items?
name="ConfirmObjectDeleteNoCopyNoOwn"
type="alertmodal">
At least one object is not copyable.
-You do not own least one object.
+You do not own at least one object.
Are you sure you want to delete these items?
<tag>confirm</tag>
@@ -2785,13 +2812,13 @@ Are you sure you want to delete these items?
type="alertmodal">
At least one object is locked.
At least one object is not copyable.
-You do not own least one object.
+You do not own at least one object.
Are you sure you want to delete these items?
<tag>confirm</tag>
<usetemplate
name="okcancelbuttons"
- notext="cancel"
+ notext="Cancel"
yestext="OK"/>
</notification>
@@ -4033,6 +4060,27 @@ Join me in [REGION]
<notification
icon="alertmodal.tga"
+ name="TeleportRequestPrompt"
+ type="alertmodal">
+Request a teleport to [NAME_SLURL] with the following message
+ <tag>confirm</tag>
+ <form name="form">
+ <input name="message" type="text">
+
+ </input>
+ <button
+ default="true"
+ index="0"
+ name="OK"
+ text="OK"/>
+ <button
+ index="1"
+ name="Cancel"
+ text="Cancel"/>
+ </form>
+ </notification>
+ <notification
+ icon="alertmodal.tga"
name="TooManyTeleportOffers"
type="alertmodal">
You attempted to make [OFFERS] teleport offers
@@ -6601,7 +6649,7 @@ Your object named &lt;nolink&gt;[OBJECTFROMNAME]&lt;/nolink&gt; has given you th
sound="UISndNewIncomingIMSession">
[NAME_SLURL] has offered to teleport you to their location:
-“[MESSAGE]”
+"[MESSAGE]”
&lt;icon&gt;[MATURITY_ICON]&lt;/icon&gt; - [MATURITY_STR]
<tag>confirm</tag>
<form name="form">
@@ -6666,6 +6714,27 @@ However, this region contains content accessible to adults only.
Teleport offer sent to [TO_NAME]
</notification>
+ <notification
+ icon="notify.tga"
+ name="TeleportRequest"
+ log_to_im="true"
+ type="offer">
+[NAME_SLURL] is requesting to be teleported to your location.
+[MESSAGE]
+
+Offer a teleport?
+ <tag>confirm</tag>
+ <form name="form">
+ <button
+ index="0"
+ name="Yes"
+ text="Yes"/>
+ <button
+ index="1"
+ name="No"
+ text="No"/>
+ </form>
+ </notification>
<notification
icon="notify.tga"
@@ -6714,7 +6783,6 @@ However, this region contains content accessible to adults only.
icon="notify.tga"
name="FriendshipOffered"
log_to_im="true"
- show_toast="false"
type="notify">
<tag>friendship</tag>
You have offered friendship to [TO_NAME]
@@ -6764,7 +6832,6 @@ However, this region contains content accessible to adults only.
icon="notify.tga"
name="FriendshipAcceptedByMe"
log_to_im="true"
- show_toast="false"
type="notify">
<tag>friendship</tag>
Friendship offer accepted.
@@ -6774,7 +6841,6 @@ Friendship offer accepted.
icon="notify.tga"
name="FriendshipDeclinedByMe"
log_to_im="true"
- show_toast="false"
type="notify">
<tag>friendship</tag>
Friendship offer declined.
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index d964ccaf79..a56213cdfe 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -378,7 +378,7 @@ Please try logging in again in a minute.</string>
<!-- world map -->
<string name="texture_loading">Loading...</string>
<string name="worldmap_offline">Offline</string>
- <string name="worldmap_item_tooltip_format">[AREA] m² L$[PRICE]</string>
+ <string name="worldmap_item_tooltip_format">[AREA] m² L$[PRICE] ([SQMPRICE] L$/m²)</string>
<string name="worldmap_results_none_found">None found.</string>
<!-- animations uploading status codes -->
@@ -2460,7 +2460,11 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale
all estates that you manage for [OWNER]
</string>
<string name="RegionInfoAllowedResidents">Allowed Residents: ([ALLOWEDAGENTS], max [MAXACCESS])</string>
- <string name="RegionInfoAllowedGroups">Allowed groups: ([ALLOWEDGROUPS], max [MAXACCESS])</string>
+ <string name="RegionInfoAllowedGroups">Allowed Groups: ([ALLOWEDGROUPS], max [MAXACCESS])</string>
+ <string name="RegionInfoEstateManagers">Estate Managers: ([ESTATEMANAGERS], max [MAXMANAGERS])</string>
+ <string name="RegionInfoBannedResidents">Banned Residents: ([BANNEDAGENTS], max [MAXBANNED])</string>
+ <string name="RegionInfoListTypeAllowedAgents">Allowed Residents</string>
+ <string name="RegionInfoListTypeBannedAgents">Banned Residents</string>
<!-- script limits floater -->
<string name="ScriptLimitsParcelScriptMemory">Parcel Script Memory</string>
diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp
index 1e768f52d9..0b105cc1b6 100755
--- a/indra/viewer_components/updater/llupdatechecker.cpp
+++ b/indra/viewer_components/updater/llupdatechecker.cpp
@@ -80,7 +80,6 @@ void LLUpdateChecker::checkVersion(std::string const & hostUrl,
//-----------------------------------------------------------------------------
-const char * LLUpdateChecker::Implementation::sLegacyProtocolVersion = "v1.0";
const char * LLUpdateChecker::Implementation::sProtocolVersion = "v1.1";
@@ -153,40 +152,11 @@ void LLUpdateChecker::Implementation::completed(U32 status,
server_error += content["error_text"].asString();
}
- if (status == 404)
- {
- if (mProtocol == sProtocolVersion)
- {
- mProtocol = sLegacyProtocolVersion;
- std::string retryUrl = buildUrl(mHostUrl, mServicePath, mChannel, mVersion, mPlatform, mPlatformVersion, mUniqueId, mWillingToTest);
-
- LL_WARNS("UpdaterService")
- << "update response using " << sProtocolVersion
- << " was HTTP 404 (" << server_error
- << "); retry with legacy protocol " << mProtocol
- << "\n at " << retryUrl
- << LL_ENDL;
-
- mHttpClient.get(retryUrl, this);
- }
- else
- {
- LL_WARNS("UpdaterService")
- << "update response using " << sLegacyProtocolVersion
- << " was 404 (" << server_error
- << "); request failed"
- << LL_ENDL;
- mClient.error(reason);
- }
- }
- else
- {
- LL_WARNS("UpdaterService") << "response error " << status
- << " " << reason
- << " (" << server_error << ")"
- << LL_ENDL;
- mClient.error(reason);
- }
+ LL_WARNS("UpdaterService") << "response error " << status
+ << " " << reason
+ << " (" << server_error << ")"
+ << LL_ENDL;
+ mClient.error(reason);
}
else
{
@@ -218,11 +188,8 @@ std::string LLUpdateChecker::Implementation::buildUrl(std::string const & hostUr
path.append(channel);
path.append(version);
path.append(platform);
- if (mProtocol != sLegacyProtocolVersion)
- {
- path.append(platform_version);
- path.append(willing_to_test ? "testok" : "testno");
- path.append((char*)uniqueid);
- }
+ path.append(platform_version);
+ path.append(willing_to_test ? "testok" : "testno");
+ path.append((char*)uniqueid);
return LLURI::buildHTTP(hostUrl, path).asString();
}