diff --git a/.hgtags b/.hgtags index 2d73837b00..f593899abd 100755 --- a/.hgtags +++ b/.hgtags @@ -480,3 +480,4 @@ fe4f7c5e9fd27e09d03deb1cc9ab3e5093f6309e 3.6.3-release 83357f31d8dbf048a8bfdc323f363bf4d588aca1 CHOP-951-a 91ed595b716f14f07409595b734fda891a59379e 3.6.4-release bf6d453046011a11de2643fac610cc5258650f82 3.6.5-release +ae457ece77001767ae9613148c495e7b98cc0f4a 3.6.7-release diff --git a/BuildParams b/BuildParams index 9752e5298d..31e7e841ad 100755 --- a/BuildParams +++ b/BuildParams @@ -27,9 +27,6 @@ Linux.distcc_version = Linux.gcc_version = /usr/bin/gcc-4.6 Linux.cxx_version = /usr/bin/g++-4.6 -# Setup default sourceid so Windows can pick up the TeamCity override -sourceid = "" - ################################################################ #### Examples of how to set the viewer_channel #### # @@ -50,6 +47,18 @@ sourceid = "" ################################################################ viewer_channel = "Second Life Test" +# Setup default packaging parameters. +sourceid = "" +additional_packages = "Amazon Desura B C" +Amazon_sourceid = "1207v_Amazon" +Amazon_viewer_channel_suffix = " Amazon" +Desura_sourceid = "1208_desura" +Desura_viewer_channel_suffix = " Desura" +B_sourceid = "1301_B" +B_viewer_channel_suffix = " B" +C_sourceid = "1302_C" +C_viewer_channel_suffix = " C" + # Report changes since... viewer-development.show_changes_since = last_sprint diff --git a/build.sh b/build.sh index 35c4daad31..18a8e7eec0 100755 --- a/build.sh +++ b/build.sh @@ -38,22 +38,22 @@ build_dir_CYGWIN() installer_Darwin() { - ls -1td "$(build_dir_Darwin ${last_built_variant:-Release})/newview/"*.dmg 2>/dev/null | sed 1q + ls -1tr "$(build_dir_Darwin ${last_built_variant:-Release})/newview/"*"$additional_package_name"*.dmg 2>/dev/null | sed 1q } installer_Linux() { - ls -1td "$(build_dir_Linux ${last_built_variant:-Release})/newview/"*.tar.bz2 2>/dev/null | sed 1q + ls -1tr "$(build_dir_Linux ${last_built_variant:-Release})/newview/"*"$additional_package_name"*.tar.bz2 2>/dev/null | grep -v symbols | sed 1q } installer_CYGWIN() { v=${last_built_variant:-Release} d=$(build_dir_CYGWIN $v) - if [ -r "$d/newview/$v/touched.bat" ] + if [ -r "$d/newview/$additional_package_name$v/touched.bat" ] then - p=$(sed 's:.*=::' "$d/newview/$v/touched.bat") - echo "$d/newview/$v/$p" + p=$(sed 's:.*=::' "$d/newview/$additional_package_name$v/touched.bat") + echo "$d/newview/$additional_package_name$v/$p" fi } @@ -367,10 +367,28 @@ then # Coverity doesn't package, so it's ok, anything else is fail succeeded=$build_coverity else + # Upload base package. upload_item installer "$package" binary/octet-stream upload_item quicklink "$package" binary/octet-stream [ -f $build_dir/summary.json ] && upload_item installer $build_dir/summary.json text/plain + # Upload additional packages. + for package_id in $additional_packages + do + case $arch in + CYGWIN) export additional_package_name="$package_id/" ;; + *) export additional_package_name=$package_id ;; + esac + package=$(installer_$arch) + if [ x"$package" != x ] + then + upload_item installer "$package" binary/octet-stream + else + record_failure "Failed to upload $package_id package." + fi + done + export additional_package_name="" + case "$last_built_variant" in Release) # Upload crash reporter files diff --git a/doc/contributions.txt b/doc/contributions.txt index 237bfc99c9..c261bff923 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -175,8 +175,11 @@ Ansariel Hiller STORM-1685 STORM-1713 STORM-1899 + STORM-1932 + STORM-1933 MAINT-2368 STORM-1931 + MAINT-2773 Aralara Rajal Arare Chantilly CHUIBUG-191 @@ -304,9 +307,13 @@ Christopher Organiser Ciaran Laval Cinder Roxley BUG-2326 + OPEN-185 STORM-1703 STORM-1948 + STORM-1888 + STORM-1958 STORM-1952 + STORM-1951 Clara Young Coaldust Numbers VWR-1095 @@ -653,7 +660,7 @@ Jonathan Yap STORM-1809 STORM-1793 STORM-1810 - STORM-1877 + STORM-1838 STORM-1892 STORM-1894 STORM-1860 @@ -663,8 +670,11 @@ Jonathan Yap STORM-1858 STORM-1862 STORM-1918 + STORM-1929 STORM-1953 OPEN-161 + STORM-1953 + STORM-1957 Kadah Coba STORM-1060 STORM-1843 @@ -680,6 +690,7 @@ Kagehi Kohn Kaimen Takahe Katharine Berry STORM-1900 + OPEN-149 STORM-1940 STORM-1941 Keklily Longfall @@ -929,6 +940,7 @@ Nicky Dasmijn STORM-1936 BUG-3605 CHUIBUG-197 + OPEN-187 STORM-1937 Nicky Perian OPEN-1 @@ -1298,6 +1310,7 @@ Westley Streeter Whimsy Winx Whirly Fizzle STORM-1895 + VWR-29543 MAINT-873 STORM-1930 Whoops Babii @@ -1361,6 +1374,7 @@ YongYong Francois Zak Westminster Zai Lynch VWR-19505 + STORM-1902 Zana Kohime Zaren Alexander Zarkonnen Decosta diff --git a/indra/cmake/BuildVersion.cmake b/indra/cmake/BuildVersion.cmake index 9f9401114e..f73ddd762f 100755 --- a/indra/cmake/BuildVersion.cmake +++ b/indra/cmake/BuildVersion.cmake @@ -16,7 +16,9 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n else (DEFINED ENV{revision}) find_program(MERCURIAL hg) - if (DEFINED MERCURIAL) + find_program(WORDCOUNT wc) + find_program(SED sed) + if (DEFINED MERCURIAL AND DEFINED WORDCOUNT AND DEFINED SED) execute_process( # FIRE-11737: Reverting to old revisions shows tip in build string # This command gets the revision number of the current @@ -24,7 +26,9 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n # building an earlier revision. Instead, we use # "hg identify -n" to get the local revision number # of the actual state of the repository. - #COMMAND ${MERCURIAL} log -r tip --template "{rev}" + #COMMAND ${MERCURIAL} log -r tip:0 --template '\\n' + #COMMAND ${WORDCOUNT} -l + #COMMAND ${SED} "s/ //g" COMMAND ${MERCURIAL} identify -n OUTPUT_VARIABLE VIEWER_VERSION_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE @@ -36,13 +40,13 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n if ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") message("Revision (from hg) ${VIEWER_VERSION_REVISION}") else ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") + message("Revision not set (repository not found?); using 0") set(VIEWER_VERSION_REVISION 0 ) - message("Revision not set, repository not found, using ${VIEWER_VERSION_REVISION}") endif ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") - else (DEFINED MERCURIAL) + else (DEFINED MERCURIAL AND DEFINED WORDCOUNT AND DEFINED SED) + message("Revision not set: 'hg', 'wc' or 'sed' not found; using 0") set(VIEWER_VERSION_REVISION 0) - message("Revision not set, 'hg' not found (${MERCURIAL}), using ${VIEWER_VERSION_REVISION}") - endif (DEFINED MERCURIAL) + endif (DEFINED MERCURIAL AND DEFINED WORDCOUNT AND DEFINED SED) endif (DEFINED ENV{revision}) message("Building '${VIEWER_CHANNEL}' Version ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") else ( EXISTS ${VIEWER_VERSION_BASE_FILE} ) diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 133540ce54..eba0d7abfa 100755 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -229,15 +229,98 @@ def main(): for opt in args: print "Option:", opt, "=", args[opt] + # pass in sourceid as an argument now instead of an environment variable + try: + args['sourceid'] = os.environ["sourceid"] + except KeyError: + args['sourceid'] = "" + + # Build base package. + touch = args.get('touch') + if touch: + print 'Creating base package' + args['package_id'] = "" # base package has no package ID wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) wm.do(*args['actions']) + # Store package file for later if making touched file. + base_package_file = "" + if touch: + print 'Created base package ', wm.package_file + base_package_file = "" + wm.package_file + # handle multiple packages if set + try: + additional_packages = os.environ["additional_packages"] + except KeyError: + additional_packages = "" + if additional_packages: + # Determine destination prefix / suffix for additional packages. + base_dest_postfix = args['dest'] + base_dest_prefix = "" + base_dest_parts = args['dest'].split(os.sep) + if len(base_dest_parts) > 1: + base_dest_postfix = base_dest_parts[len(base_dest_parts) - 1] + base_dest_prefix = base_dest_parts[0] + i = 1 + while i < len(base_dest_parts) - 1: + base_dest_prefix = base_dest_prefix + os.sep + base_dest_parts[i] + i = i + 1 + # Determine touched prefix / suffix for additional packages. + base_touch_postfix = "" + base_touch_prefix = "" + if touch: + base_touch_postfix = touch + base_touch_parts = touch.split('/') + if "arwin" in args['platform']: + if len(base_touch_parts) > 1: + base_touch_postfix = base_touch_parts[len(base_touch_parts) - 1] + base_touch_prefix = base_touch_parts[0] + i = 1 + while i < len(base_touch_parts) - 1: + base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i] + i = i + 1 + else: + if len(base_touch_parts) > 2: + base_touch_postfix = base_touch_parts[len(base_touch_parts) - 2] + '/' + base_touch_parts[len(base_touch_parts) - 1] + base_touch_prefix = base_touch_parts[0] + i = 1 + while i < len(base_touch_parts) - 2: + base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i] + i = i + 1 + # Store base channel name. + base_channel_name = args['channel'] + # Build each additional package. + package_id_list = additional_packages.split(" ") + for package_id in package_id_list: + try: + args['package_id'] = package_id + args['channel'] = base_channel_name + os.environ[package_id + "_viewer_channel_suffix"] + if package_id + "_sourceid" in os.environ: + args['sourceid'] = os.environ[package_id + "_sourceid"] + else: + args['sourceid'] = "" + args['dest'] = base_dest_prefix + os.sep + package_id + os.sep + base_dest_postfix + except KeyError: + sys.stderr.write("Failed to create package for package_id: %s" % package_id) + sys.stderr.flush() + continue + if touch: + print 'Creating additional package for ', package_id, ' in ', args['dest'] + wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) + wm.do(*args['actions']) + if touch: + print 'Created additional package ', wm.package_file, ' for ', package_id + faketouch = base_touch_prefix + '/' + package_id + '/' + base_touch_postfix + fp = open(faketouch, 'w') + fp.write('set package_file=%s\n' % wm.package_file) + fp.close() + # Write out the package file in this format, so that it can easily be called # and used in a .bat file - yeah, it sucks, but this is the simplest... touch = args.get('touch') if touch: fp = open(touch, 'w') - fp.write('set package_file=%s\n' % wm.package_file) + fp.write('set package_file=%s\n' % base_package_file) fp.close() print 'touched', touch return 0 diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h index b047f86e6e..8c8c315808 100755 --- a/indra/llmath/v4color.h +++ b/indra/llmath/v4color.h @@ -50,7 +50,7 @@ class LLColor4 LLColor4(F32 r, F32 g, F32 b); // Initializes LLColor4 to (r, g, b, 1) LLColor4(F32 r, F32 g, F32 b, F32 a); // Initializes LLColor4 to (r. g, b, a) LLColor4(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc)) - LLColor4(const F32 *vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1) + LLColor4(const F32 *vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], vec[3]) LLColor4(const LLColor3 &vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a) explicit LLColor4(const LLSD& sd); explicit LLColor4(const LLColor4U& color4u); // "explicit" to avoid automatic conversion diff --git a/indra/llmessage/llinstantmessage.h b/indra/llmessage/llinstantmessage.h index f040e9b1b8..87f6c32d0f 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 59fc003ace..d9dae69f54 100755 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -79,6 +79,7 @@ set(llui_SOURCE_FILES llmultislider.cpp llmultisliderctrl.cpp llnotifications.cpp + llnotificationslistener.cpp llnotificationsutil.cpp llpanel.cpp llprogressbar.cpp @@ -132,6 +133,7 @@ set(llui_SOURCE_FILES llviewmodel.cpp llview.cpp llviewquery.cpp + llviewereventrecorder.cpp llwindowshade.cpp llxuiparser.cpp ) @@ -189,6 +191,7 @@ set(llui_HEADER_FILES llmultislider.h llnotificationptr.h llnotifications.h + llnotificationslistener.h llnotificationsutil.h llnotificationtemplate.h llnotificationvisibilityrule.h @@ -246,6 +249,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 ecf7f408ea..23034b24b4 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" #include "llcheckboxctrl.h" // Add checkbox control toggle @@ -479,6 +480,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(); @@ -509,6 +512,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 ad9847f8cd..4001942561 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" @@ -671,7 +671,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 @@ -741,6 +744,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; @@ -1607,6 +1611,17 @@ BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks) return TRUE;//always } +// 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) { @@ -1627,7 +1642,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 d4dc07ac37..e3f99bfbec 100755 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -291,6 +291,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 9a0086f726..9b528e2401 100755 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1214,6 +1214,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 2154ad0c7d..51d566d46f 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 { @@ -983,6 +985,8 @@ private: bool mIgnoreAllNotifications; + boost::scoped_ptr mListener; + std::vector 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 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 +#include +#include + +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 > ForwarderMap; + ForwarderMap mForwarders; + LLNotifications & mNotifications; +}; + +#endif // LL_LLNOTIFICATIONSLISTENER_H diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 8bab6b5222..e176b18462 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" @@ -632,6 +632,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; } @@ -683,30 +688,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); } } @@ -730,6 +738,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; } @@ -1160,21 +1172,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); } // Enable tab flashing diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 20e728ca8c..64d2f88ac1 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" @@ -316,22 +316,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 4dd93826d7..375467ef76 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" @@ -697,13 +699,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; + } } @@ -752,6 +768,7 @@ bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y) && getEnabled(); } +// This is NOT event recording related void LLView::logMouseEvent() { if (sDebugMouseHandling) @@ -798,7 +815,14 @@ 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 ))) { + 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; } } @@ -821,6 +845,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; } @@ -879,6 +904,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; } @@ -968,10 +994,12 @@ 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) + if (handled) { - llinfos << "Key handled by " << getName() << llendl; + llwarns << "Key handled by " << getName() << llendl; } } } @@ -1019,6 +1047,11 @@ 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; } @@ -1048,12 +1081,16 @@ 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) @@ -1126,7 +1163,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 + +#include "llsingleton.h" // includes llerror which we need here so we can skip the include here + +class LLViewerEventRecorder : public LLSingleton +{ + + 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 2de5ed7c88..a4de9edebe 100755 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1308,6 +1308,8 @@ void LLWindowMacOSX::setupFailure(const std::string& text, const std::string& ca OSMessageBox(text, caption, type); } + // 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 const char* cursorIDToName(int id) { BOOL use_legacy_cursors = gSavedSettings.getBOOL("UseLegacyCursors"); diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp index 9d1ba41267..def121c162 100755 --- a/indra/llxml/llcontrol.cpp +++ b/indra/llxml/llcontrol.cpp @@ -477,7 +477,7 @@ LLControlVariable* LLControlGroup::declareControl(const std::string& name, eCont } else { - llwarns << "Control named " << name << " already exists, ignoring new declaration." << llendl; + LL_WARNS("Settings") << "Control named " << name << " already exists, ignoring new declaration." << LL_ENDL; } return existing_control; } @@ -719,14 +719,14 @@ U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, BOOL require if (!xml_controls.parseFile(filename)) { - llwarns << "Unable to open control file " << filename << llendl; + LL_WARNS("Settings") << "Unable to open control file " << filename << LL_ENDL; return 0; } LLXmlTreeNode* rootp = xml_controls.getRoot(); if (!rootp || !rootp->hasAttribute("version")) { - llwarns << "No valid settings header found in control file " << filename << llendl; + LL_WARNS("Settings") << "No valid settings header found in control file " << filename << LL_ENDL; return 0; } @@ -739,7 +739,7 @@ U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, BOOL require // Check file version if (version != CURRENT_VERSION) { - llinfos << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << llendl; + LL_INFOS("Settings") << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << LL_ENDL; return 0; } @@ -757,7 +757,7 @@ U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, BOOL require if (!name.empty()) { //read in to end of line - llwarns << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << llendl; + LL_WARNS("Settings") << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << LL_ENDL; } child_nodep = rootp->getNextChild(); continue; @@ -911,7 +911,7 @@ U32 LLControlGroup::saveToFile(const std::string& filename, BOOL nondefault_only LLControlVariable* control = iter->second; if (!control) { - llwarns << "Tried to save invalid control: " << iter->first << llendl; + LL_WARNS("Settings") << "Tried to save invalid control: " << iter->first << LL_ENDL; } else if( control->shouldSave(nondefault_only) ) { @@ -928,12 +928,12 @@ U32 LLControlGroup::saveToFile(const std::string& filename, BOOL nondefault_only { LLSDSerialize::toPrettyXML(settings, file); file.close(); - llinfos << "Saved to " << filename << llendl; + LL_INFOS("Settings") << "Saved to " << filename << LL_ENDL; } else { // This is a warning because sometime we want to use settings files which can't be written... - llwarns << "Unable to open settings file: " << filename << llendl; + LL_WARNS("Settings") << "Unable to open settings file: " << filename << LL_ENDL; return 0; } return num_saved; @@ -946,14 +946,14 @@ U32 LLControlGroup::loadFromFile(const std::string& filename, bool set_default_v infile.open(filename); if(!infile.is_open()) { - llwarns << "Cannot find file " << filename << " to load." << llendl; + LL_WARNS("Settings") << "Cannot find file " << filename << " to load." << LL_ENDL; return 0; } if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(settings, infile)) { infile.close(); - llwarns << "Unable to parse LLSD control file " << filename << ". Trying Legacy Method." << llendl; + LL_WARNS("Settings") << "Unable to parse LLSD control file " << filename << ". Trying Legacy Method." << LL_ENDL; return loadFromFileLegacy(filename, TRUE, TYPE_STRING); } @@ -1079,6 +1079,7 @@ U32 LLControlGroup::loadFromFile(const std::string& filename, bool set_default_v ++validitems; } + LL_DEBUGS("Settings") << "Loaded " << validitems << " settings from " << filename << LL_ENDL; return validitems; } @@ -1115,7 +1116,7 @@ void main() BOOL_CONTROL baz; U32 count = gGlobals.loadFromFile("controls.ini"); - llinfos << "Loaded " << count << " controls" << llendl; + LL_INFOS("Settings") << "Loaded " << count << " controls" << LL_ENDL; // test insertion foo = new LLControlVariable("gFoo", 5.f, 1.f, 20.f); @@ -1376,19 +1377,19 @@ LLColor4 convert_from_llsd(const LLSD& sd, eControlType type, const st LLColor4 color(sd); if (color.mV[VRED] < 0.f || color.mV[VRED] > 1.f) { - llwarns << "Color " << control_name << " red value out of range: " << color << llendl; + LL_WARNS("Settings") << "Color " << control_name << " red value out of range: " << color << LL_ENDL; } else if (color.mV[VGREEN] < 0.f || color.mV[VGREEN] > 1.f) { - llwarns << "Color " << control_name << " green value out of range: " << color << llendl; + LL_WARNS("Settings") << "Color " << control_name << " green value out of range: " << color << LL_ENDL; } else if (color.mV[VBLUE] < 0.f || color.mV[VBLUE] > 1.f) { - llwarns << "Color " << control_name << " blue value out of range: " << color << llendl; + LL_WARNS("Settings") << "Color " << control_name << " blue value out of range: " << color << LL_ENDL; } else if (color.mV[VALPHA] < 0.f || color.mV[VALPHA] > 1.f) { - llwarns << "Color " << control_name << " alpha value out of range: " << color << llendl; + LL_WARNS("Settings") << "Color " << control_name << " alpha value out of range: " << color << LL_ENDL; } return LLColor4(sd); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index dd7db3c05d..4d492d4654 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -6947,6 +6947,16 @@ Value + LeapPlaybackEventsCommand + + Comment + Command line to use leap to launch playback of event recordings + Persist + 0 + Type + LLSD + Value + LSLFindCaseInsensitivity Comment @@ -12998,6 +13008,17 @@ Change of this parameter will affect the layout of buttons in notification toast Type Boolean Value + 0 + + ShowEventRecorderMenuItems + + Comment + Whether or not Event Recorder menu choices - Start / Stop event recording should appear in the (currently) Develop menu + Persist + 0 + Type + Boolean + Value 1 ShowHoverTips diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 24ed53af85..e01de6f571 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -241,6 +241,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 @@ -768,6 +772,7 @@ LLAppViewer::LLAppViewer() : LLAppViewer::~LLAppViewer() { delete mSettingsLocationList; + LLViewerEventRecorder::instance().~LLViewerEventRecorder(); LLLoginInstance::instance().setUpdaterService(0); @@ -2565,13 +2570,13 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, } // - llinfos << "Attempting to load settings for the group " << file.name() - << " - from location " << location_key << llendl; + LL_INFOS("Settings") << "Attempting to load settings for the group " << file.name() + << " - from location " << location_key << LL_ENDL; LLControlGroup* settings_group = LLControlGroup::getInstance(file.name); if(!settings_group) { - llwarns << "No matching settings group for name " << file.name() << llendl; + LL_WARNS("Settings") << "No matching settings group for name " << file.name() << LL_ENDL; continue; } @@ -2600,7 +2605,7 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, if(settings_group->loadFromFile(full_settings_path, set_defaults, file.persistent)) { // success! - llinfos << "Loaded settings file " << full_settings_path << llendl; + LL_INFOS("Settings") << "Loaded settings file " << full_settings_path << LL_ENDL; } else { // failed to load @@ -2614,7 +2619,7 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, // only complain if we actually have a filename at this point if (!full_settings_path.empty()) { - llinfos << "Cannot load " << full_settings_path << " - No settings found." << llendl; + LL_INFOS("Settings") << "Cannot load " << full_settings_path << " - No settings found." << LL_ENDL; } } } @@ -2780,8 +2785,8 @@ bool LLAppViewer::initConfiguration() LLFile::remove(user_settings_filename); } - llinfos << "Using command line specified settings filename: " - << user_settings_filename << llendl; + LL_INFOS("Settings") << "Using command line specified settings filename: " + << user_settings_filename << LL_ENDL; } else { @@ -2839,8 +2844,8 @@ bool LLAppViewer::initConfiguration() { std::string session_settings_filename = clp.getOption("sessionsettings")[0]; gSavedSettings.setString("SessionSettingsFile", session_settings_filename); - llinfos << "Using session settings filename: " - << session_settings_filename << llendl; + LL_INFOS("Settings") << "Using session settings filename: " + << session_settings_filename << LL_ENDL; } loadSettingsFromDirectory("Session",true); // AO The session file turns into the new defaults @@ -2848,10 +2853,10 @@ bool LLAppViewer::initConfiguration() { std::string user_session_settings_filename = clp.getOption("usersessionsettings")[0]; gSavedSettings.setString("UserSessionSettingsFile", user_session_settings_filename); - llinfos << "Using user session settings filename: " << user_session_settings_filename << llendl; - } + LL_INFOS("Settings") << "Using user session settings filename: " + << user_session_settings_filename << LL_ENDL; - + } loadSettingsFromDirectory("UserSession"); //AO: Re-read user settings again. This is a Firestorm hack to get user settings to override modes @@ -2942,6 +2947,10 @@ bool LLAppViewer::initConfiguration() } } + if (clp.hasOption("logevents")) { + LLViewerEventRecorder::instance().setEventLoggingOn(); + } + std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel")); if(! CmdLineChannel.empty()) { diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index c8c54a390f..5dac433b27 100755 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -175,21 +175,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 feb6506b95..c79118dd1d 100644 --- 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" // Firestorm includes #include "fsfloaterim.h" @@ -508,6 +510,72 @@ 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; + LLAvatarName av_name; + if (!LLAvatarNameCache::get(id, &av_name)) + { + // unlikely ... they just picked this name from somewhere... + LLAvatarNameCache::get(id, boost::bind(&LLAvatarActions::teleportRequest, id)); + return; // reinvoke this when the name resolves + } + notification["NAME"] = av_name.getCompleteName(); + + 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 7ab4980b83..5a61fa039f 100755 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -114,6 +114,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. */ diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 77c64a2071..9bfadeda06 100755 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -341,25 +341,25 @@ void LLNotificationChiclet::setCounter(S32 counter) bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification ) { - bool display_notification; + 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())) ) { - display_notification = false; + displayNotification = false; } else if( !(notification->canLogToIM() && notification->hasFormElements()) && (!notification->getPayload().has("give_inventory_notification") || notification->getPayload()["give_inventory_notification"])) { - display_notification = true; + displayNotification = true; } else { - display_notification = false; + displayNotification = false; } - return display_notification; + return displayNotification; } ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp index 6f03de345e..b7310465b9 100755 --- a/indra/newview/llconversationloglist.cpp +++ b/indra/newview/llconversationloglist.cpp @@ -316,6 +316,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 ab3db7ac39..1fc821e3f2 100755 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -134,6 +134,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 1a583c2314..2429809892 100755 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -1081,6 +1081,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/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 3bfbc8ac71..ca68ae89af 100755 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -2257,7 +2257,6 @@ S32 LLPanelLandOptions::getDirectoryFee() // virtual void LLPanelLandOptions::draw() { - refreshSearch(); // Is this necessary? JC LLPanel::draw(); } @@ -2271,13 +2270,8 @@ void LLPanelLandOptions::refreshSearch() mCheckShowDirectory->set(FALSE); mCheckShowDirectory->setEnabled(FALSE); - // *TODO:Translate - // FIRE-7773: Parcel categories don't stay selected - //const std::string& none_string = LLParcel::getCategoryUIString(LLParcel::C_NONE); - //mCategoryCombo->setSimple(none_string); const std::string& none_string = LLParcel::getCategoryString(LLParcel::C_NONE); mCategoryCombo->setValue(none_string); - // mCategoryCombo->setEnabled(FALSE); return; } @@ -2304,14 +2298,9 @@ void LLPanelLandOptions::refreshSearch() mCheckShowDirectory ->set(show_directory); // Set by string in case the order in UI doesn't match the order by index. - // *TODO:Translate LLParcel::ECategory cat = parcel->getCategory(); - // FIRE-7773: Parcel categories don't stay selected - //const std::string& category_string = LLParcel::getCategoryUIString(cat); - //mCategoryCombo->setSimple(category_string); const std::string& category_string = LLParcel::getCategoryString(cat); mCategoryCombo->setValue(category_string); - // std::string tooltip; bool enable_show_directory = false; diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index d2a2eb6dbe..2b7f0b0b32 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -837,6 +837,9 @@ void LLFloaterPreference::cancel() // hide autoreplace settings floater LLFloaterReg::hideInstance("prefs_autoreplace"); + // hide spellchecker settings folder + LLFloaterReg::hideInstance("prefs_spellchecker"); + // STORM-1888 // hide spellchecker settings floater LLFloaterReg::hideInstance("prefs_spellchecker"); diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index b51675a464..00451852a6 100755 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -1912,10 +1912,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); - // Fixed unlocalizable strings - //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; @@ -1931,10 +1928,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); - // Fixed unlocalizable strings - //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; @@ -2999,15 +2993,10 @@ bool LLDispatchSetEstateAccess::operator()( } - // FIRE-5998: Unlocalizable label - //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("ban_resident_label")->setValue(LLSD(msg)); if (banned_agent_name_list) @@ -3027,15 +3016,10 @@ bool LLDispatchSetEstateAccess::operator()( if (access_flags & ESTATE_ACCESS_MANAGERS) { - // FIRE-5998: Unlocalizable label - //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("estate_manager_label")->setValue(LLSD(msg)); LLNameListCtrl* estate_manager_name_list = diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 1650d989ea..09947ea5a7 100755 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -5062,6 +5062,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); } @@ -5148,6 +5158,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) @@ -5157,6 +5168,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 8d364ba37d..ca6096866b 100755 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -558,13 +558,11 @@ void LLNetMap::draw() // Draw avatars for (U32 i = 0; i < avatar_ids.size(); i++) { - // FIRE-11118 - skip our dot, we'll draw one later. - //pos_map = globalPosToView(positions[i]); LLUUID uuid = avatar_ids[i]; - if (uuid == gAgent.getID()) - continue; + // Skip self, we'll draw it later + if (uuid == gAgent.getID()) continue; + pos_map = globalPosToView(positions[i]); - // // Check for unknown Z-offset => AVATAR_UNKNOWN_Z_OFFSET //unknown_relative_z = positions[i].mdV[VZ] == COARSEUPDATE_MAX_Z && diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index 548ff619df..eac8e8a134 100755 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -80,6 +80,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)); // Firestorm additions registrar.add("Avatar.GroupInvite", boost::bind(&LLAvatarActions::inviteToGroup, id)); @@ -133,6 +134,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")); @@ -269,6 +271,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(), @@ -298,6 +307,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/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 8516c22a46..7a813306a7 100755 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -1057,12 +1057,8 @@ void LLBasicCertificateStore::validate(int validation_policy, throw LLInvalidCertificate((*current_cert)); } std::string sha1_hash((const char *)cert_x509->sha1_hash, SHA_DIGEST_LENGTH); - - // LLBasicCertificate::getOpenSSLX509 returns a copy made with X509_dup X509_free( cert_x509 ); - cert_x509 = 0; - // - + cert_x509 = NULL; t_cert_cache::iterator cache_entry = mTrustedCertCache.find(sha1_hash); if(cache_entry != mTrustedCertCache.end()) { diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 2453a84408..f1e58ad9a2 100755 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -750,7 +750,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 72682e82ce..a8cb6b7625 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" @@ -2202,6 +2203,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 // @@ -10308,6 +10346,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"); // [FS communication UI] @@ -10601,6 +10641,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 1d78ab1569..5ec9c31594 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2545,7 +2545,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 @@ -3611,6 +3611,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) break; case IM_LURE_USER: + case IM_TELEPORT_REQUEST: { // [RLVa:KB] - Checked: 2010-12-11 (RLVa-1.2.2c) | Added: RLVa-1.2.2c // If the lure sender is a specific @accepttp exception they will override muted and busy status @@ -3650,7 +3651,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); @@ -3744,7 +3746,18 @@ 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; @@ -8490,28 +8503,13 @@ void send_group_notice(const LLUUID& group_id, bin_bucket_size); } -bool handle_lure_callback(const LLSD& notification, const LLSD& response) +void send_lures(const LLSD& notification, const LLSD& response) { - static const unsigned OFFER_RECIPIENT_LIMIT = 250; - if(notification["payload"]["ids"].size() > OFFER_RECIPIENT_LIMIT) - { - // More than OFFER_RECIPIENT_LIMIT targets will overload the message - // producing an llerror. - LLSD args; - args["OFFERS"] = notification["payload"]["ids"].size(); - args["LIMIT"] = static_cast(OFFER_RECIPIENT_LIMIT); - 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) - { // [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0) if ( (RlvActions::hasBehaviour(RLV_BHVR_SENDIM)) || (RlvActions::hasBehaviour(RLV_BHVR_SENDIMTO)) ) { @@ -8528,41 +8526,63 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response) } // [/RLVa:KB] - 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) + 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. { - 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(); + 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; + LLSD payload; - //*TODO please rewrite all keys to the same case, lower or upper - payload["from_id"] = target_id; - LLNotificationsUtil::add("TeleportOfferSent", args, 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); - } + // Add the recepient to the recent people list. + LLRecentPeople::instance().add(target_id); } - gAgent.sendReliableMessage(); + } + gAgent.sendReliableMessage(); +} + +bool handle_lure_callback(const LLSD& notification, const LLSD& response) +{ + static const unsigned OFFER_RECIPIENT_LIMIT = 250; + if(notification["payload"]["ids"].size() > OFFER_RECIPIENT_LIMIT) + { + // More than OFFER_RECIPIENT_LIMIT targets will overload the message + // producing an llerror. + LLSD args; + args["OFFERS"] = notification["payload"]["ids"].size(); + args["LIMIT"] = static_cast(OFFER_RECIPIENT_LIMIT); + LLNotificationsUtil::add("TooManyTeleportOffers", args); + return false; + } + + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if(0 == option) + { + send_lures(notification, response); } return false; @@ -8618,6 +8638,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/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 7237eb84dd..18c6c22f5b 100755 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -1016,21 +1016,17 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) objectp = *idle_iter; llassert(objectp->isActive()); objectp->idleUpdate(agent, world, frame_time); - - } + } //update flexible objects LLVolumeImplFlexible::updateClass(); //update animated textures - // FIRE-10557 / BUG-2814 / MAINT-2773: Disable texture animation doesn't work - //LLViewerTextureAnim::updateClass(); if (gAnimateTextures) { LLViewerTextureAnim::updateClass(); } - // - } + } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 57f68cebc8..e474cd415d 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -202,6 +202,8 @@ #include "llagentui.h" #include "llwearablelist.h" +#include "llviewereventrecorder.h" + #include "llnotifications.h" #include "llnotificationsutil.h" #include "llnotificationmanager.h" @@ -1010,27 +1012,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) ) @@ -1038,27 +1031,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; @@ -1433,7 +1443,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 } @@ -2743,6 +2754,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; } @@ -2757,12 +2770,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; } } @@ -2772,6 +2787,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 @@ -2786,11 +2802,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; } @@ -2859,18 +2877,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; } @@ -2878,6 +2905,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; } @@ -2937,7 +2966,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) // HACK: Numeric keypad 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) { @@ -2960,14 +2989,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; @@ -2976,8 +2998,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 5fa100c079..631c2bfa65 100755 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -573,6 +573,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 b3d28788da..31b1d091ee 100755 --- a/indra/newview/skins/default/xui/en/menu_conversation.xml +++ b/indra/newview/skins/default/xui/en/menu_conversation.xml @@ -52,6 +52,13 @@ + + + + + + + + + + + + + + + - diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 34b06e2d0f..616c0d9b43 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3957,6 +3957,34 @@ label="Recorder" name="Recorder" tear_off="true"> + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index a7471f91d6..825b71dc7f 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4221,6 +4221,27 @@ Join me in [REGION] + +Request a teleport to [NAME] with the following message + confirm +
+ + + +