summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.hgtags2
-rwxr-xr-xBuildParams15
-rwxr-xr-xautobuild.xml6
-rwxr-xr-xbuild.sh28
-rwxr-xr-xdoc/contributions.txt19
-rwxr-xr-xindra/CMakeLists.txt9
-rwxr-xr-xindra/cmake/BuildVersion.cmake16
-rwxr-xr-xindra/lib/python/indra/util/llmanifest.py87
-rw-r--r--indra/llcommon/llversionviewer.h41
-rwxr-xr-xindra/llmath/v4color.h2
-rwxr-xr-xindra/llmessage/llhttpclient.cpp35
-rwxr-xr-xindra/llmessage/llhttpclient.h19
-rwxr-xr-xindra/llmessage/llinstantmessage.h2
-rwxr-xr-xindra/llmessage/llurlrequest.cpp21
-rwxr-xr-xindra/llmessage/llurlrequest.h9
-rwxr-xr-xindra/llplugin/llplugincookiestore.cpp28
-rwxr-xr-xindra/llplugin/llplugincookiestore.h3
-rwxr-xr-xindra/llui/CMakeLists.txt4
-rwxr-xr-xindra/llui/llbutton.cpp4
-rwxr-xr-xindra/llui/llchatentry.h4
-rwxr-xr-xindra/llui/llfloater.cpp31
-rwxr-xr-xindra/llui/llfloater.h1
-rwxr-xr-xindra/llui/llfocusmgr.cpp4
-rwxr-xr-xindra/llui/llfocusmgr.h2
-rwxr-xr-xindra/llui/llfolderview.cpp10
-rw-r--r--indra/llui/llfolderviewitem.cpp14
-rwxr-xr-xindra/llui/llnotifications.cpp1
-rwxr-xr-xindra/llui/llnotifications.h4
-rw-r--r--indra/llui/llnotificationslistener.cpp359
-rw-r--r--indra/llui/llnotificationslistener.h69
-rwxr-xr-xindra/llui/lltabcontainer.cpp56
-rwxr-xr-xindra/llui/lluictrl.cpp20
-rwxr-xr-xindra/llui/llview.cpp51
-rw-r--r--indra/llui/llviewereventrecorder.cpp296
-rw-r--r--indra/llui/llviewereventrecorder.h103
-rwxr-xr-xindra/llwindow/llwindowmacosx-objc.h5
-rwxr-xr-xindra/llwindow/llwindowmacosx.cpp2
-rwxr-xr-xindra/llxml/llcontrol.cpp31
-rwxr-xr-xindra/newview/CMakeLists.txt6
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rwxr-xr-xindra/newview/app_settings/commands.xml10
-rwxr-xr-xindra/newview/app_settings/settings.xml32
-rwxr-xr-xindra/newview/app_settings/toolbars.xml1
-rwxr-xr-xindra/newview/llagentui.cpp10
-rwxr-xr-xindra/newview/llagentui.h1
-rwxr-xr-xindra/newview/llappviewer.cpp75
-rwxr-xr-xindra/newview/llappviewerwin32.cpp17
-rwxr-xr-xindra/newview/llavataractions.cpp68
-rwxr-xr-xindra/newview/llavataractions.h6
-rwxr-xr-xindra/newview/llchiclet.cpp23
-rwxr-xr-xindra/newview/llconversationloglist.cpp4
-rwxr-xr-xindra/newview/llconversationmodel.cpp1
-rw-r--r--indra/newview/llfacebookconnect.cpp577
-rw-r--r--indra/newview/llfacebookconnect.h106
-rwxr-xr-xindra/newview/llfloaterimcontainer.cpp22
-rwxr-xr-xindra/newview/llfloaterland.cpp11
-rwxr-xr-xindra/newview/llfloaterpreference.cpp5
-rwxr-xr-xindra/newview/llfloaterregioninfo.cpp18
-rwxr-xr-xindra/newview/llfloatersnapshot.cpp1004
-rwxr-xr-xindra/newview/llfloatersnapshot.h1
-rw-r--r--indra/newview/llfloatersocial.cpp920
-rw-r--r--indra/newview/llfloatersocial.h165
-rwxr-xr-xindra/newview/llfloaterwebcontent.cpp88
-rwxr-xr-xindra/newview/llfloaterwebcontent.h6
-rwxr-xr-xindra/newview/llimview.cpp146
-rwxr-xr-xindra/newview/llinventorybridge.cpp12
-rwxr-xr-xindra/newview/llnetmap.cpp5
-rwxr-xr-xindra/newview/llnotificationscripthandler.cpp5
-rwxr-xr-xindra/newview/llpanelpeople.cpp124
-rwxr-xr-xindra/newview/llpanelpeople.h11
-rwxr-xr-xindra/newview/llpanelpeoplemenus.cpp43
-rwxr-xr-xindra/newview/llpanelpeoplemenus.h14
-rwxr-xr-xindra/newview/llpanelprimmediacontrols.cpp18
-rwxr-xr-xindra/newview/llpanelprimmediacontrols.h1
-rwxr-xr-xindra/newview/llparticipantlist.cpp3
-rwxr-xr-xindra/newview/llsechandler_basic.cpp10
-rw-r--r--indra/newview/llsnapshotlivepreview.cpp874
-rw-r--r--indra/newview/llsnapshotlivepreview.h164
-rwxr-xr-xindra/newview/llsyswellwindow.cpp5
-rwxr-xr-xindra/newview/llsyswellwindow.h1
-rwxr-xr-xindra/newview/lltoastimpanel.cpp4
-rwxr-xr-xindra/newview/llurlhistory.cpp23
-rwxr-xr-xindra/newview/llviewerfloaterreg.cpp4
-rwxr-xr-xindra/newview/llviewerkeyboard.cpp5
-rwxr-xr-xindra/newview/llviewermedia.cpp21
-rwxr-xr-xindra/newview/llviewermenu.cpp43
-rwxr-xr-xindra/newview/llviewermessage.cpp159
-rwxr-xr-xindra/newview/llviewerobjectlist.cpp10
-rwxr-xr-xindra/newview/llviewerregion.cpp2
-rwxr-xr-xindra/newview/llviewerwindow.cpp102
-rwxr-xr-xindra/newview/llwebprofile.cpp5
-rwxr-xr-xindra/newview/llwindowlistener.cpp9
-rwxr-xr-xindra/newview/llworldmap.cpp11
-rw-r--r--indra/newview/skins/default/textures/icons/Facebook.pngbin0 -> 365 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/map_placeholder.pngbin0 -> 7694 bytes
-rwxr-xr-xindra/newview/skins/default/textures/textures.xml6
-rw-r--r--indra/newview/skins/default/textures/toolbar_icons/facebook.pngbin0 -> 2974 bytes
-rwxr-xr-xindra/newview/skins/default/xui/de/strings.xml2
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_about_land.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_fbc_web.xml9
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_snapshot.xml18
-rw-r--r--indra/newview/skins/default/xui/en/floater_social.xml101
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_web_content.xml4
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_conversation.xml7
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_conversation_log_gear.xml10
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_inventory.xml8
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_people_nearby.xml10
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_viewer.xml45
-rwxr-xr-xindra/newview/skins/default/xui/en/notifications.xml54
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_people.xml17
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_preferences_move.xml12
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_prim_media_controls.xml26
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_snapshot_options.xml4
-rw-r--r--indra/newview/skins/default/xui/en/panel_social_account.xml75
-rw-r--r--indra/newview/skins/default/xui/en/panel_social_photo.xml152
-rw-r--r--indra/newview/skins/default/xui/en/panel_social_place.xml132
-rw-r--r--indra/newview/skins/default/xui/en/panel_social_status.xml67
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_status_bar.xml2
-rwxr-xr-xindra/newview/skins/default/xui/en/strings.xml23
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_tab_view.xml13
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_view.xml127
-rwxr-xr-xindra/newview/skins/default/xui/es/strings.xml2
-rwxr-xr-xindra/newview/skins/default/xui/fr/strings.xml2
-rwxr-xr-xindra/newview/skins/default/xui/it/strings.xml2
-rwxr-xr-xindra/newview/skins/default/xui/ja/strings.xml2
-rwxr-xr-xindra/newview/skins/default/xui/pt/strings.xml2
-rwxr-xr-xindra/newview/skins/default/xui/ru/strings.xml2
-rwxr-xr-xindra/newview/skins/default/xui/tr/strings.xml2
-rwxr-xr-xindra/newview/tests/lltranslate_test.cpp4
-rwxr-xr-xindra/newview/viewer_manifest.py62
-rw-r--r--indra/test_apps/llfbconnecttest/CMakeLists.txt304
-rw-r--r--indra/test_apps/llfbconnecttest/bookmarks.txt4
-rw-r--r--indra/test_apps/llfbconnecttest/llfbconnecttest.cpp2394
-rw-r--r--indra/test_apps/llfbconnecttest/llfbconnecttest.h207
-rwxr-xr-xindra/viewer_components/updater/llupdatechecker.cpp49
135 files changed, 8830 insertions, 1560 deletions
diff --git a/.hgtags b/.hgtags
index d993cb70b9..c81c919d1a 100755
--- a/.hgtags
+++ b/.hgtags
@@ -466,3 +466,5 @@ fe4f7c5e9fd27e09d03deb1cc9ab3e5093f6309e 3.6.3-release
91ed595b716f14f07409595b734fda891a59379e 3.6.4-release
bf6d453046011a11de2643fac610cc5258650f82 3.6.5-release
ae457ece77001767ae9613148c495e7b98cc0f4a 3.6.7-release
+d40c66e410741de7e90b1ed6dac28dd8a2d7e1f6 3.6.8-release
+70eda3721d36df3e00730629c42a1304e5bc65b8 3.6.9-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/autobuild.xml b/autobuild.xml
index b1478a46b2..8194b3d4eb 100755
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1965,11 +1965,11 @@
<key>package_description</key>
<map>
<key>description</key>
- <string>Spell checking dictionaries</string>
+ <string>Second Life Viewer</string>
<key>license</key>
- <string>various open</string>
+ <string>LGPL</string>
<key>name</key>
- <string>dictionaries</string>
+ <string>Second Life Viewer</string>
<key>platforms</key>
<map>
<key>common</key>
diff --git a/build.sh b/build.sh
index a78f368e47..4875ef39f7 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
}
@@ -355,10 +355,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 931211f622..99527c0587 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
@@ -652,7 +659,7 @@ Jonathan Yap
STORM-1809
STORM-1793
STORM-1810
- STORM-1877
+ STORM-1838
STORM-1892
STORM-1894
STORM-1860
@@ -662,8 +669,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
@@ -679,7 +689,9 @@ Kagehi Kohn
Kaimen Takahe
Katharine Berry
STORM-1900
+ OPEN-149
STORM-1940
+ OPEN-149
STORM-1941
Keklily Longfall
Ken Lavender
@@ -928,7 +940,9 @@ Nicky Dasmijn
STORM-1936
BUG-3605
CHUIBUG-197
+ OPEN-187
STORM-1937
+ OPEN-187
Nicky Perian
OPEN-1
STORM-1087
@@ -1125,6 +1139,7 @@ Slee Mayo
snowy Sidran
Sovereign Engineer
MAINT-2334
+ OPEN-189
SpacedOut Frye
VWR-34
VWR-45
@@ -1293,6 +1308,7 @@ Westley Streeter
Whimsy Winx
Whirly Fizzle
STORM-1895
+ VWR-29543
MAINT-873
STORM-1930
Whoops Babii
@@ -1356,6 +1372,7 @@ YongYong Francois
Zak Westminster
Zai Lynch
VWR-19505
+ STORM-1902
Zana Kohime
Zaren Alexander
Zarkonnen Decosta
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index bdbfc55fa2..410d25ad97 100755
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -63,10 +63,11 @@ add_subdirectory(${VIEWER_PREFIX}test)
# viewer media plugins
add_subdirectory(${LIBS_OPEN_PREFIX}media_plugins)
-# llplugin testbed code (is this the right way to include it?)
-if (LL_TESTS AND NOT LINUX)
- add_subdirectory(${VIEWER_PREFIX}test_apps/llplugintest)
-endif (LL_TESTS AND NOT LINUX)
+ # llplugin testbed code (is this the right way to include it?)
+ if (LL_TESTS AND NOT LINUX)
+ add_subdirectory(${VIEWER_PREFIX}test_apps/llplugintest)
+ add_subdirectory(${VIEWER_PREFIX}test_apps/llfbconnecttest)
+ endif (LL_TESTS AND NOT LINUX)
if (LINUX)
add_subdirectory(${VIEWER_PREFIX}linux_crash_logger)
diff --git a/indra/cmake/BuildVersion.cmake b/indra/cmake/BuildVersion.cmake
index af2063ce6d..e4b63dc7cb 100755
--- a/indra/cmake/BuildVersion.cmake
+++ b/indra/cmake/BuildVersion.cmake
@@ -16,22 +16,26 @@ 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(
- COMMAND ${MERCURIAL} log -r tip --template "{rev}"
+ COMMAND ${MERCURIAL} log -r tip:0 --template '\\n'
+ COMMAND ${WORDCOUNT} -l
+ COMMAND ${SED} "s/ //g"
OUTPUT_VARIABLE VIEWER_VERSION_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
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 54049b5545..52b4acbc94 100755
--- a/indra/lib/python/indra/util/llmanifest.py
+++ b/indra/lib/python/indra/util/llmanifest.py
@@ -224,15 +224,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/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
deleted file mode 100644
index 6a5ff314e4..0000000000
--- a/indra/llcommon/llversionviewer.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * @file llversionviewer.h
- * @brief
- *
- * $LicenseInfo:firstyear=2002&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_LLVERSIONVIEWER_H
-#define LL_LLVERSIONVIEWER_H
-
-const S32 LL_VERSION_MAJOR = 3;
-const S32 LL_VERSION_MINOR = 4;
-const S32 LL_VERSION_PATCH = 6;
-const S32 LL_VERSION_BUILD = 0;
-
-const char * const LL_CHANNEL = "Second Life Developer";
-
-#if LL_DARWIN
-const char * const LL_VERSION_BUNDLE_ID = "com.secondlife.indra.viewer";
-#endif
-
-#endif
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/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp
index 6110b035dc..11648717ad 100755
--- a/indra/llmessage/llhttpclient.cpp
+++ b/indra/llmessage/llhttpclient.cpp
@@ -217,7 +217,8 @@ static void request(
Injector* body_injector,
LLCurl::ResponderPtr responder,
const F32 timeout = HTTP_REQUEST_EXPIRY_SECS,
- const LLSD& headers = LLSD()
+ const LLSD& headers = LLSD(),
+ bool follow_redirects = true
)
{
if (!LLHTTPClient::hasPump())
@@ -231,7 +232,7 @@ static void request(
}
LLPumpIO::chain_t chain;
- LLURLRequest* req = new LLURLRequest(method, url);
+ LLURLRequest* req = new LLURLRequest(method, url, follow_redirects);
if(!req->isValid())//failed
{
if (responder)
@@ -334,7 +335,8 @@ void LLHTTPClient::getByteRange(
S32 bytes,
ResponderPtr responder,
const LLSD& hdrs,
- const F32 timeout)
+ const F32 timeout,
+ bool follow_redirects /* = true */)
{
LLSD headers = hdrs;
if(offset > 0 || bytes > 0)
@@ -342,37 +344,42 @@ void LLHTTPClient::getByteRange(
std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1);
headers["Range"] = range;
}
- request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers);
+ request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers, follow_redirects);
}
void LLHTTPClient::head(
const std::string& url,
ResponderPtr responder,
const LLSD& headers,
- const F32 timeout)
+ const F32 timeout,
+ bool follow_redirects /* = true */)
{
- request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers);
+ request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers, follow_redirects);
}
-void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout)
+void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout,
+ bool follow_redirects /* = true */)
{
- request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers);
+ request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers, follow_redirects);
}
-void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout)
+void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const LLSD& headers,
+ const F32 timeout, bool follow_redirects /* = true */)
{
- request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers);
+ request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers, follow_redirects);
}
-void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const F32 timeout)
+void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const F32 timeout,
+ bool follow_redirects /* = true */)
{
- getHeaderOnly(url, responder, LLSD(), timeout);
+ getHeaderOnly(url, responder, LLSD(), timeout, follow_redirects);
}
-void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers, const F32 timeout)
+void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers,
+ const F32 timeout, bool follow_redirects /* = true */)
{
LLURI uri;
uri = LLURI::buildHTTP(url, LLSD::emptyArray(), query);
- get(uri.asString(), responder, headers, timeout);
+ get(uri.asString(), responder, headers, timeout, follow_redirects);
}
// A simple class for managing data returned from a curl http request.
diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h
index a7236ba169..5de257a4f6 100755
--- a/indra/llmessage/llhttpclient.h
+++ b/indra/llmessage/llhttpclient.h
@@ -63,10 +63,15 @@ public:
const std::string& url,
ResponderPtr,
const LLSD& headers = LLSD(),
- const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
- static void getByteRange(const std::string& url, S32 offset, S32 bytes, ResponderPtr, const LLSD& headers=LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
- static void get(const std::string& url, ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
- static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
+ const F32 timeout=HTTP_REQUEST_EXPIRY_SECS,
+ bool follow_redirects = true);
+ static void getByteRange(const std::string& url, S32 offset, S32 bytes, ResponderPtr,
+ const LLSD& headers=LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS,
+ bool follow_redirects = true);
+ static void get(const std::string& url, ResponderPtr, const LLSD& headers = LLSD(),
+ const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true);
+ static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers = LLSD(),
+ const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true);
static void put(
const std::string& url,
@@ -74,8 +79,10 @@ public:
ResponderPtr,
const LLSD& headers = LLSD(),
const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
- static void getHeaderOnly(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
- static void getHeaderOnly(const std::string& url, ResponderPtr, const LLSD& headers, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
+ static void getHeaderOnly(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS,
+ bool follow_redirects = true);
+ static void getHeaderOnly(const std::string& url, ResponderPtr, const LLSD& headers,
+ const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true);
static void post(
const std::string& url,
diff --git a/indra/llmessage/llinstantmessage.h b/indra/llmessage/llinstantmessage.h
index db4a38ea9e..f7118f8ccf 100755
--- a/indra/llmessage/llinstantmessage.h
+++ b/indra/llmessage/llinstantmessage.h
@@ -126,7 +126,7 @@ enum EInstantMessage
IM_LURE_ACCEPTED = 23,
IM_LURE_DECLINED = 24,
IM_GODLIKE_LURE_USER = 25,
- IM_YET_TO_BE_USED = 26,
+ IM_TELEPORT_REQUEST = 26,
// IM that notifie of a new group election.
// Name is name of person who called vote.
diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp
index de9e2fe294..683065357d 100755
--- a/indra/llmessage/llurlrequest.cpp
+++ b/indra/llmessage/llurlrequest.cpp
@@ -150,16 +150,19 @@ std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action)
return VERBS[action];
}
-LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
- mAction(action)
+LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, bool follow_redirects /* = true */) :
+ mAction(action),
+ mFollowRedirects(follow_redirects)
{
initialize();
}
LLURLRequest::LLURLRequest(
LLURLRequest::ERequestAction action,
- const std::string& url) :
- mAction(action)
+ const std::string& url,
+ bool follow_redirects /* = true */) :
+ mAction(action),
+ mFollowRedirects(follow_redirects)
{
initialize();
setURL(url);
@@ -479,12 +482,18 @@ bool LLURLRequest::configure()
case HTTP_HEAD:
mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1);
mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1);
- mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
+ if (mFollowRedirects)
+ {
+ mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
+ }
rv = true;
break;
case HTTP_GET:
mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1);
- mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
+ if (mFollowRedirects)
+ {
+ mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
+ }
// Set Accept-Encoding to allow response compression
mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h
index 44d358d906..20d6e30d17 100755
--- a/indra/llmessage/llurlrequest.h
+++ b/indra/llmessage/llurlrequest.h
@@ -95,7 +95,7 @@ public:
*
* @param action One of the ERequestAction enumerations.
*/
- LLURLRequest(ERequestAction action);
+ LLURLRequest(ERequestAction action, bool follow_redirects = true);
/**
* @brief Constructor.
@@ -103,7 +103,7 @@ public:
* @param action One of the ERequestAction enumerations.
* @param url The url of the request. It should already be encoded.
*/
- LLURLRequest(ERequestAction action, const std::string& url);
+ LLURLRequest(ERequestAction action, const std::string& url, bool follow_redirects = true);
/**
* @brief Destructor.
@@ -219,10 +219,11 @@ protected:
};
EState mState;
ERequestAction mAction;
+ bool mFollowRedirects;
LLURLRequestDetail* mDetail;
LLIOPipe::ptr_t mCompletionCallback;
- S32 mRequestTransferedBytes;
- S32 mResponseTransferedBytes;
+ S32 mRequestTransferedBytes;
+ S32 mResponseTransferedBytes;
static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param);
diff --git a/indra/llplugin/llplugincookiestore.cpp b/indra/llplugin/llplugincookiestore.cpp
index 82017ab3fa..9f4d65e723 100755
--- a/indra/llplugin/llplugincookiestore.cpp
+++ b/indra/llplugin/llplugincookiestore.cpp
@@ -87,6 +87,16 @@ std::string LLPluginCookieStore::Cookie::getKey() const
return result;
}
+std::string LLPluginCookieStore::Cookie::getDomain() const
+{
+ std::string result;
+ if(mDomainEnd > mDomainStart)
+ {
+ result += mCookie.substr(mDomainStart, mDomainEnd - mDomainStart);
+ }
+ return result;
+}
+
bool LLPluginCookieStore::Cookie::parse(const std::string &host)
{
bool first_field = true;
@@ -662,3 +672,21 @@ void LLPluginCookieStore::removeCookie(const std::string &key)
}
}
+void LLPluginCookieStore::removeCookiesByDomain(const std::string &domain)
+{
+ cookie_map_t::iterator iter = mCookies.begin();
+ while(iter != mCookies.end())
+ {
+ if(iter->second->getDomain() == domain)
+ {
+ cookie_map_t::iterator doErase = iter;
+ iter++;
+ delete doErase->second;
+ mCookies.erase(doErase);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+}
diff --git a/indra/llplugin/llplugincookiestore.h b/indra/llplugin/llplugincookiestore.h
index 91289d38a5..a2fdeab647 100755
--- a/indra/llplugin/llplugincookiestore.h
+++ b/indra/llplugin/llplugincookiestore.h
@@ -67,6 +67,8 @@ public:
// quote or unquote a string as per the definition of 'quoted-string' in rfc2616
static std::string quoteString(const std::string &s);
static std::string unquoteString(const std::string &s);
+
+ void removeCookiesByDomain(const std::string &domain);
private:
@@ -79,6 +81,7 @@ private:
// Construct a string from the cookie that uniquely represents it, to be used as a key in a std::map.
std::string getKey() const;
+ std::string getDomain() const;
const std::string &getCookie() const { return mCookie; };
bool isSessionCookie() const { return mDate.isNull(); };
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 34a08603fa..589ceac501 100755
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -75,6 +75,7 @@ set(llui_SOURCE_FILES
llmultislider.cpp
llmultisliderctrl.cpp
llnotifications.cpp
+ llnotificationslistener.cpp
llnotificationsutil.cpp
llpanel.cpp
llprogressbar.cpp
@@ -128,6 +129,7 @@ set(llui_SOURCE_FILES
llviewmodel.cpp
llview.cpp
llviewquery.cpp
+ llviewereventrecorder.cpp
llwindowshade.cpp
llxuiparser.cpp
)
@@ -183,6 +185,7 @@ set(llui_HEADER_FILES
llmultislider.h
llnotificationptr.h
llnotifications.h
+ llnotificationslistener.h
llnotificationsutil.h
llnotificationtemplate.h
llnotificationvisibilityrule.h
@@ -240,6 +243,7 @@ set(llui_HEADER_FILES
llviewinject.h
llviewmodel.h
llview.h
+ llviewereventrecorder.h
llviewquery.h
llwindowshade.h
llxuiparser.h
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 44f2c1efe9..50ac511d18 100755
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -49,6 +49,7 @@
#include "lluictrlfactory.h"
#include "llhelp.h"
#include "lldockablefloater.h"
+#include "llviewereventrecorder.h"
static LLDefaultChildRegistry::Register<LLButton> r("button");
@@ -443,6 +444,8 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
*/
LLUICtrl::handleMouseDown(x, y, mask);
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+
if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
mMouseDownTimer.start();
@@ -473,6 +476,7 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
* by calling LLUICtrl::mMouseUpSignal(x, y, mask);
*/
LLUICtrl::handleMouseUp(x, y, mask);
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
// Regardless of where mouseup occurs, handle callback
if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h
index e67f39b21b..3f13691a30 100755
--- a/indra/llui/llchatentry.h
+++ b/indra/llui/llchatentry.h
@@ -62,8 +62,8 @@ public:
virtual void draw();
virtual void onCommit();
- /*virtual*/ void onFocusReceived();
- /*virtual*/ void onFocusLost();
+ /*virtual*/ void onFocusReceived();
+ /*virtual*/ void onFocusLost();
void enableSingleLineMode(bool single_line_mode);
boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb);
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 273ceb4038..182c5cfd8a 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"
@@ -509,8 +509,8 @@ LLFloater::~LLFloater()
if( gFocusMgr.childHasKeyboardFocus(this))
{
- // Just in case we might still have focus here, release it.
- releaseFocus();
+ // Just in case we might still have focus here, release it.
+ releaseFocus();
}
// This is important so that floaters with persistent rects (i.e., those
@@ -642,7 +642,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
@@ -696,6 +699,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;
@@ -1335,7 +1339,7 @@ void LLFloater::setFocus( BOOL b )
{
return;
}
- LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this);
+ LLView* last_focus = gFocusMgr.getLastFocusForGroup(this);
// a descendent already has focus
BOOL child_had_focus = hasFocus();
@@ -1543,6 +1547,17 @@ BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks)
}
// virtual
+BOOL LLFloater::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ lldebugs << "LLFloater::handleMouseUp calling LLPanel (really LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+ BOOL handled = LLPanel::handleMouseUp(x,y,mask); // Not implemented in LLPanel so this actually calls LLView
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+ }
+ return handled;
+}
+
+// virtual
BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
{
if( mMinimized )
@@ -1562,7 +1577,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 59448530d9..953689498f 100755
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -289,6 +289,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/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp
index 724d190307..f03c8d444b 100755
--- a/indra/llui/llfocusmgr.cpp
+++ b/indra/llui/llfocusmgr.cpp
@@ -469,7 +469,7 @@ void LLFocusMgr::setAppHasFocus(BOOL focus)
mAppHasFocus = focus;
}
-LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const
+LLView* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const
{
if (subtree_root)
{
@@ -477,7 +477,7 @@ LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const
if (found_it != mImpl->mFocusHistory.end())
{
// found last focus for this subtree
- return static_cast<LLUICtrl*>(found_it->second.get());
+ return found_it->second.get();
}
}
return NULL;
diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h
index 25ae1d2579..1c7326260c 100755
--- a/indra/llui/llfocusmgr.h
+++ b/indra/llui/llfocusmgr.h
@@ -97,7 +97,7 @@ public:
void triggerFocusFlash();
BOOL getAppHasFocus() const { return mAppHasFocus; }
void setAppHasFocus(BOOL focus);
- LLUICtrl* getLastFocusForGroup(LLView* subtree_root) const;
+ LLView* getLastFocusForGroup(LLView* subtree_root) const;
void clearLastFocusForGroup(LLView* subtree_root);
// If setKeyboardFocus(NULL) is called, and there is a non-NULL default
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index cf449217f5..f32a52e6c6 100755
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -185,7 +185,7 @@ LLFolderView::LLFolderView(const Params& p)
mAutoOpenCandidate = NULL;
mAutoOpenTimer.stop();
mKeyboardSelection = FALSE;
- mIndentation = p.folder_indentation;
+ mIndentation = getParentFolder() ? getParentFolder()->getIndentation() + mLocalIndentation : 0;
//clear label
// go ahead and render root folder as usual
@@ -1613,7 +1613,7 @@ void LLFolderView::update()
{
getFolderViewModel()->getFilter().clearModified();
}
-
+
// automatically show matching items, and select first one if we had a selection
if (mNeedsAutoSelect)
{
@@ -1653,13 +1653,13 @@ void LLFolderView::update()
BOOL is_visible = isInVisibleChain();
- // Puts folders/items in proper positions
+ //Puts folders/items in proper positions
// arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849)
// It also handles the open/close folder animation
- if (is_visible)
+ if ( is_visible )
{
sanitizeSelection();
- if (needsArrange())
+ if( needsArrange() )
{
S32 height = 0;
S32 width = 0;
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
index 6c147ccc12..f061313645 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -307,7 +307,12 @@ std::set<LLFolderViewItem*> LLFolderViewItem::getSelectionList() const
// addToFolder() returns TRUE if it succeeds. FALSE otherwise
void LLFolderViewItem::addToFolder(LLFolderViewFolder* folder)
{
- folder->addItem(this);
+ folder->addItem(this);
+
+ // Compute indentation since parent folder changed
+ mIndentation = (getParentFolder())
+ ? getParentFolder()->getIndentation() + mLocalIndentation
+ : 0;
}
@@ -940,6 +945,11 @@ LLFolderViewFolder::~LLFolderViewFolder( void )
void LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder)
{
folder->addFolder(this);
+
+ // Compute indentation since parent folder changed
+ mIndentation = (getParentFolder())
+ ? getParentFolder()->getIndentation() + mLocalIndentation
+ : 0;
}
static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange");
@@ -1109,7 +1119,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height )
BOOL LLFolderViewFolder::needsArrange()
{
- return mLastArrangeGeneration < getRoot()->getArrangeGeneration();
+ return mLastArrangeGeneration < getRoot()->getArrangeGeneration();
}
// Passes selection information on to children and record selection
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 3186c5460a..5c288c3f03 100755
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -1206,6 +1206,7 @@ LLNotifications::LLNotifications()
: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
mIgnoreAllNotifications(false)
{
+ mListener.reset(new LLNotificationsListener(*this));
LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
}
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 3b620084ee..6ac4a98806 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
{
@@ -978,6 +980,8 @@ private:
bool mIgnoreAllNotifications;
+ boost::scoped_ptr<LLNotificationsListener> mListener;
+
std::vector<LLNotificationChannelPtr> mDefaultChannels;
};
diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp
new file mode 100644
index 0000000000..9e8e943ee6
--- /dev/null
+++ b/indra/llui/llnotificationslistener.cpp
@@ -0,0 +1,359 @@
+/**
+ * @file llnotificationslistener.cpp
+ * @author Brad Kittenbrink
+ * @date 2009-07-08
+ * @brief Implementation for llnotificationslistener.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llnotificationslistener.h"
+#include "llnotifications.h"
+#include "llnotificationtemplate.h"
+#include "llsd.h"
+#include "llui.h"
+
+LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) :
+ LLEventAPI("LLNotifications",
+ "LLNotifications listener to (e.g.) pop up a notification"),
+ mNotifications(notifications)
+{
+ add("requestAdd",
+ "Add a notification with specified [\"name\"], [\"substitutions\"] and [\"payload\"].\n"
+ "If optional [\"reply\"] specified, arrange to send user response on that LLEventPump.",
+ &LLNotificationsListener::requestAdd);
+ /* add("listChannels",
+ "Post to [\"reply\"] a map of info on existing channels",
+ &LLNotificationsListener::listChannels,
+ LLSD().with("reply", LLSD()));
+ */
+ add("listChannelNotifications",
+ "Post to [\"reply\"] an array of info on notifications in channel [\"channel\"]",
+ &LLNotificationsListener::listChannelNotifications,
+ LLSD().with("reply", LLSD()).with("channel", LLSD()));
+ add("respond",
+ "Respond to notification [\"uuid\"] with data in [\"response\"]",
+ &LLNotificationsListener::respond,
+ LLSD().with("uuid", LLSD()));
+ add("cancel",
+ "Cancel notification [\"uuid\"]",
+ &LLNotificationsListener::cancel,
+ LLSD().with("uuid", LLSD()));
+ add("ignore",
+ "Ignore future notification [\"name\"]\n"
+ "(from <notification name= > in notifications.xml)\n"
+ "according to boolean [\"ignore\"].\n"
+ "If [\"name\"] is omitted or undefined, [un]ignore all future notifications.\n"
+ "Note that ignored notifications are not forwarded unless intercepted before\n"
+ "the \"Ignore\" channel.",
+ &LLNotificationsListener::ignore);
+ add("forward",
+ "Forward to [\"pump\"] future notifications on channel [\"channel\"]\n"
+ "according to boolean [\"forward\"]. When enabled, only types matching\n"
+ "[\"types\"] are forwarded, as follows:\n"
+ "omitted or undefined: forward all notifications\n"
+ "string: forward only the specific named [sig]type\n"
+ "array of string: forward any notification matching any named [sig]type.\n"
+ "When boolean [\"respond\"] is true, we auto-respond to each forwarded\n"
+ "notification.",
+ &LLNotificationsListener::forward,
+ LLSD().with("channel", LLSD()));
+}
+
+// This is here in the .cpp file so we don't need the definition of class
+// Forwarder in the header file.
+LLNotificationsListener::~LLNotificationsListener()
+{
+}
+
+void LLNotificationsListener::requestAdd(const LLSD& event_data) const
+{
+ if(event_data.has("reply"))
+ {
+ mNotifications.add(event_data["name"],
+ event_data["substitutions"],
+ event_data["payload"],
+ boost::bind(&LLNotificationsListener::NotificationResponder,
+ this,
+ event_data["reply"].asString(),
+ _1, _2
+ )
+ );
+ }
+ else
+ {
+ mNotifications.add(event_data["name"],
+ event_data["substitutions"],
+ event_data["payload"]);
+ }
+}
+
+void LLNotificationsListener::NotificationResponder(const std::string& reply_pump,
+ const LLSD& notification,
+ const LLSD& response) const
+{
+ LLSD reponse_event;
+ reponse_event["notification"] = notification;
+ reponse_event["response"] = response;
+ LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event);
+}
+/*
+void LLNotificationsListener::listChannels(const LLSD& params) const
+{
+ LLReqID reqID(params);
+ LLSD response(reqID.makeResponse());
+ for (LLNotifications::
+
+
+
+ for (LLNotifications::ChannelMap::const_iterator cmi(mNotifications.mChannels.begin()),
+ cmend(mNotifications.mChannels.end());
+ cmi != cmend; ++cmi)
+ {
+ LLSD channelInfo;
+ channelInfo["parent"] = cmi->second->getParentChannelName();
+ response[cmi->first] = channelInfo;
+ }
+ LLEventPumps::instance().obtain(params["reply"]).post(response);
+}
+*/
+void LLNotificationsListener::listChannelNotifications(const LLSD& params) const
+{
+ LLReqID reqID(params);
+ LLSD response(reqID.makeResponse());
+ LLNotificationChannelPtr channel(mNotifications.getChannel(params["channel"]));
+ if (channel)
+ {
+ LLSD notifications(LLSD::emptyArray());
+ for (LLNotificationChannel::Iterator ni(channel->begin()), nend(channel->end());
+ ni != nend; ++ni)
+ {
+ notifications.append(asLLSD(*ni));
+ }
+ response["notifications"] = notifications;
+ }
+ LLEventPumps::instance().obtain(params["reply"]).post(response);
+}
+
+void LLNotificationsListener::respond(const LLSD& params) const
+{
+ LLNotificationPtr notification(mNotifications.find(params["uuid"]));
+ if (notification)
+ {
+ notification->respond(params["response"]);
+ }
+}
+
+void LLNotificationsListener::cancel(const LLSD& params) const
+{
+ LLNotificationPtr notification(mNotifications.find(params["uuid"]));
+ if (notification)
+ {
+ mNotifications.cancel(notification);
+ }
+}
+
+void LLNotificationsListener::ignore(const LLSD& params) const
+{
+ // Calling a method named "ignore", but omitting its "ignore" Boolean
+ // argument, should by default cause something to be ignored. Explicitly
+ // pass ["ignore"] = false to cancel ignore.
+ bool ignore = true;
+ if (params.has("ignore"))
+ {
+ ignore = params["ignore"].asBoolean();
+ }
+ // This method can be used to affect either a single notification name or
+ // all future notifications. The two use substantially different mechanisms.
+ if (params["name"].isDefined())
+ {
+ // ["name"] was passed: ignore just that notification
+ LLNotificationTemplatePtr templatep = mNotifications.getTemplate(params["name"]);
+ if (templatep)
+ {
+ templatep->mForm->setIgnored(ignore);
+ }
+ }
+ else
+ {
+ // no ["name"]: ignore all future notifications
+ mNotifications.setIgnoreAllNotifications(ignore);
+ }
+}
+
+class LLNotificationsListener::Forwarder: public LLEventTrackable
+{
+ LOG_CLASS(LLNotificationsListener::Forwarder);
+public:
+ Forwarder(LLNotifications& llnotifications, const std::string& channel):
+ mNotifications(llnotifications),
+ mRespond(false)
+ {
+ // Connect to the specified channel on construction. Because
+ // LLEventTrackable is a base, we should automatically disconnect when
+ // destroyed.
+ LLNotificationChannelPtr channelptr(llnotifications.getChannel(channel));
+ if (channelptr)
+ {
+ // Insert our processing as a "passed filter" listener. This way
+ // we get to run before all the "changed" listeners, and we get to
+ // swipe it (hide it from the other listeners) if desired.
+ channelptr->connectPassedFilter(boost::bind(&Forwarder::handle, this, _1));
+ }
+ }
+
+ void setPumpName(const std::string& name) { mPumpName = name; }
+ void setTypes(const LLSD& types) { mTypes = types; }
+ void setRespond(bool respond) { mRespond = respond; }
+
+private:
+ bool handle(const LLSD& notification) const;
+ bool matchType(const LLSD& filter, const std::string& type) const;
+
+ LLNotifications& mNotifications;
+ std::string mPumpName;
+ LLSD mTypes;
+ bool mRespond;
+};
+
+void LLNotificationsListener::forward(const LLSD& params)
+{
+ std::string channel(params["channel"]);
+ // First decide whether we're supposed to start forwarding or stop it.
+ // Default to true.
+ bool forward = true;
+ if (params.has("forward"))
+ {
+ forward = params["forward"].asBoolean();
+ }
+ if (! forward)
+ {
+ // This is a request to stop forwarding notifications on the specified
+ // channel. The rest of the params don't matter.
+ // Because mForwarders contains scoped_ptrs, erasing the map entry
+ // DOES delete the heap Forwarder object. Because Forwarder derives
+ // from LLEventTrackable, destroying it disconnects it from the
+ // channel.
+ mForwarders.erase(channel);
+ return;
+ }
+ // From here on, we know we're being asked to start (or modify) forwarding
+ // on the specified channel. Find or create an appropriate Forwarder.
+ ForwarderMap::iterator
+ entry(mForwarders.insert(ForwarderMap::value_type(channel, ForwarderMap::mapped_type())).first);
+ if (! entry->second)
+ {
+ entry->second.reset(new Forwarder(mNotifications, channel));
+ }
+ // Now, whether this Forwarder is brand-new or not, update it with the new
+ // request info.
+ Forwarder& fwd(*entry->second);
+ fwd.setPumpName(params["pump"]);
+ fwd.setTypes(params["types"]);
+ fwd.setRespond(params["respond"]);
+}
+
+bool LLNotificationsListener::Forwarder::handle(const LLSD& notification) const
+{
+ LL_INFOS("LLNotificationsListener") << "handle(" << notification << ")" << LL_ENDL;
+ if (notification["sigtype"].asString() == "delete")
+ {
+ LL_INFOS("LLNotificationsListener") << "ignoring delete" << LL_ENDL;
+ // let other listeners see the "delete" operation
+ return false;
+ }
+ LLNotificationPtr note(mNotifications.find(notification["id"]));
+ if (! note)
+ {
+ LL_INFOS("LLNotificationsListener") << notification["id"] << " not found" << LL_ENDL;
+ return false;
+ }
+ if (! matchType(mTypes, note->getType()))
+ {
+ LL_INFOS("LLNotificationsListener") << "didn't match types " << mTypes << LL_ENDL;
+ // We're not supposed to intercept this particular notification. Let
+ // other listeners process it.
+ return false;
+ }
+ LL_INFOS("LLNotificationsListener") << "sending via '" << mPumpName << "'" << LL_ENDL;
+ // This is a notification we care about. Forward it through specified
+ // LLEventPump.
+ LLEventPumps::instance().obtain(mPumpName).post(asLLSD(note));
+ // Are we also being asked to auto-respond?
+ if (mRespond)
+ {
+ LL_INFOS("LLNotificationsListener") << "should respond" << LL_ENDL;
+ note->respond(LLSD::emptyMap());
+ // Did that succeed in removing the notification? Only cancel() if
+ // it's still around -- otherwise we get an LL_ERRS crash!
+ note = mNotifications.find(notification["id"]);
+ if (note)
+ {
+ LL_INFOS("LLNotificationsListener") << "respond() didn't clear, canceling" << LL_ENDL;
+ mNotifications.cancel(note);
+ }
+ }
+ // If we've auto-responded to this notification, then it's going to be
+ // deleted. Other listeners would get the change operation, try to look it
+ // up and be baffled by lookup failure. So when we auto-respond, suppress
+ // this notification: don't pass it to other listeners.
+ return mRespond;
+}
+
+bool LLNotificationsListener::Forwarder::matchType(const LLSD& filter, const std::string& type) const
+{
+ // Decide whether this notification matches filter:
+ // undefined: forward all notifications
+ if (filter.isUndefined())
+ {
+ return true;
+ }
+ // array of string: forward any notification matching any named type
+ if (filter.isArray())
+ {
+ for (LLSD::array_const_iterator ti(filter.beginArray()), tend(filter.endArray());
+ ti != tend; ++ti)
+ {
+ if (ti->asString() == type)
+ {
+ return true;
+ }
+ }
+ // Didn't match any entry in the array
+ return false;
+ }
+ // string: forward only the specific named type
+ return (filter.asString() == type);
+}
+
+LLSD LLNotificationsListener::asLLSD(LLNotificationPtr note)
+{
+ LLSD notificationInfo(note->asLLSD());
+ // For some reason the following aren't included in LLNotification::asLLSD().
+ notificationInfo["summary"] = note->summarize();
+ notificationInfo["id"] = note->id();
+ notificationInfo["type"] = note->getType();
+ notificationInfo["message"] = note->getMessage();
+ notificationInfo["label"] = note->getLabel();
+ return notificationInfo;
+}
diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h
new file mode 100644
index 0000000000..f9f7641de6
--- /dev/null
+++ b/indra/llui/llnotificationslistener.h
@@ -0,0 +1,69 @@
+/**
+ * @file llnotificationslistener.h
+ * @author Brad Kittenbrink
+ * @date 2009-07-08
+ * @brief Wrap subset of LLNotifications API in event API for test scripts.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLNOTIFICATIONSLISTENER_H
+#define LL_LLNOTIFICATIONSLISTENER_H
+
+#include "lleventapi.h"
+#include "llnotificationptr.h"
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <string>
+
+class LLNotifications;
+class LLSD;
+
+class LLNotificationsListener : public LLEventAPI
+{
+public:
+ LLNotificationsListener(LLNotifications & notifications);
+ ~LLNotificationsListener();
+
+private:
+ void requestAdd(LLSD const & event_data) const;
+
+ void NotificationResponder(const std::string& replypump,
+ const LLSD& notification,
+ const LLSD& response) const;
+
+ void listChannels(const LLSD& params) const;
+ void listChannelNotifications(const LLSD& params) const;
+ void respond(const LLSD& params) const;
+ void cancel(const LLSD& params) const;
+ void ignore(const LLSD& params) const;
+ void forward(const LLSD& params);
+
+ static LLSD asLLSD(LLNotificationPtr);
+
+ class Forwarder;
+ typedef std::map<std::string, boost::shared_ptr<Forwarder> > ForwarderMap;
+ ForwarderMap mForwarders;
+ LLNotifications & mNotifications;
+};
+
+#endif // LL_LLNOTIFICATIONSLISTENER_H
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index fd98155704..76ba53ec32 100755
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -27,7 +27,7 @@
#include "linden_common.h"
#include "lltabcontainer.h"
-
+#include "llviewereventrecorder.h"
#include "llfocusmgr.h"
#include "lllocalcliprect.h"
#include "llrect.h"
@@ -578,6 +578,11 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
tab_button->setFocus(TRUE);
}
}
+ if (handled) {
+ // Note: May need to also capture local coords right here ?
+ LLViewerEventRecorder::instance().update_xui(getPathname( ));
+ }
+
return handled;
}
@@ -629,30 +634,33 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
BOOL handled = FALSE;
BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
+ S32 local_x = x - getRect().mLeft;
+ S32 local_y = y - getRect().mBottom;
+
if (has_scroll_arrows)
{
if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
- S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
+ local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
+ local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
- S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
+ local_x = x - mJumpNextArrowBtn->getRect().mLeft;
+ local_y = y - mJumpNextArrowBtn->getRect().mBottom;
handled = mJumpNextArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
- S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
+ local_x = x - mPrevArrowBtn->getRect().mLeft;
+ local_y = y - mPrevArrowBtn->getRect().mBottom;
handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mNextArrowBtn->getRect().mLeft;
- S32 local_y = y - mNextArrowBtn->getRect().mBottom;
+ local_x = x - mNextArrowBtn->getRect().mLeft;
+ local_y = y - mNextArrowBtn->getRect().mBottom;
handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
}
}
@@ -676,6 +684,10 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
}
gFocusMgr.setMouseCapture(NULL);
}
+ if (handled) {
+ // Note: may need to capture local coords here
+ LLViewerEventRecorder::instance().update_xui(getPathname( ));
+ }
return handled;
}
@@ -1059,21 +1071,21 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
if (mIsVertical)
{
- p.name(std::string("vert tab button"));
- p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
- p.image_selected(mMiddleTabParams.tab_left_image_selected);
- p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
+ p.name("vtab_"+std::string(child->getName()));
+ p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
+ p.image_selected(mMiddleTabParams.tab_left_image_selected);
+ p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
}
else
- {
- p.name(std::string(child->getName()) + " tab");
- p.visible(false);
- p.image_unselected(tab_img);
- p.image_selected(tab_selected_img);
- p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM);
- // Try to squeeze in a bit more text
- p.pad_left( mLabelPadLeft );
- p.pad_right(2);
+ {
+ p.name("htab_"+std::string(child->getName()));
+ p.visible(false);
+ p.image_unselected(tab_img);
+ p.image_selected(tab_selected_img);
+ p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM);
+ // Try to squeeze in a bit more text
+ p.pad_left( mLabelPadLeft );
+ p.pad_right(2);
}
// *TODO : It seems wrong not to use p in both cases considering the way p is initialized
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index b9c843e931..1722bf27bd 100755
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -29,7 +29,7 @@
#define LLUICTRL_CPP
#include "lluictrl.h"
-
+#include "llviewereventrecorder.h"
#include "llfocusmgr.h"
#include "llpanel.h"
#include "lluictrlfactory.h"
@@ -308,22 +308,40 @@ void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask)
//virtual
BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask)
{
+
+ lldebugs << "LLUICtrl::handleMouseDown calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+
BOOL handled = LLView::handleMouseDown(x,y,mask);
+
if (mMouseDownSignal)
{
(*mMouseDownSignal)(this,x,y,mask);
}
+ lldebugs << "LLUICtrl::handleMousedown - handled is returning as: " << handled << " " << llendl;
+
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname());
+ }
return handled;
}
//virtual
BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask)
{
+
+ lldebugs << "LLUICtrl::handleMouseUp calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << llendl;
+
BOOL handled = LLView::handleMouseUp(x,y,mask);
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname());
+ }
if (mMouseUpSignal)
{
(*mMouseUpSignal)(this,x,y,mask);
}
+
+ lldebugs << "LLUICtrl::handleMouseUp - handled for xui " << getPathname() << " - is returning as: " << handled << " " << llendl;
+
return handled;
}
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 3613a40e2c..20015dca1a 100755
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -48,7 +48,9 @@
#include "lluictrlfactory.h"
#include "lltooltip.h"
#include "llsdutil.h"
-
+#include "llsdserialize.h"
+#include "llviewereventrecorder.h"
+#include "llkeyboard.h"
// for ui edit hack
#include "llbutton.h"
#include "lllineeditor.h"
@@ -642,13 +644,27 @@ void LLView::setVisible(BOOL visible)
// virtual
void LLView::handleVisibilityChange ( BOOL new_visibility )
{
+ BOOL old_visibility;
BOOST_FOREACH(LLView* viewp, mChildList)
{
// only views that are themselves visible will have their overall visibility affected by their ancestors
- if (viewp->getVisible())
+ old_visibility=viewp->getVisible();
+
+ if (old_visibility!=new_visibility)
+ {
+ LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget");
+ }
+
+ if (old_visibility)
{
viewp->handleVisibilityChange ( new_visibility );
}
+
+ // Consider changing returns to confirm success and know which widget grabbed it
+ // For now assume success and log at highest xui possible
+ // NOTE we log actual state - which may differ if it somehow failed to set visibility
+ lldebugs << "LLView::handleVisibilityChange - now: " << getVisible() << " xui: " << viewp->getPathname() << " name: " << viewp->getName() << llendl;
+
}
}
@@ -697,6 +713,7 @@ bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y)
&& getEnabled();
}
+// This is NOT event recording related
void LLView::logMouseEvent()
{
if (sDebugMouseHandling)
@@ -743,7 +760,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;
}
}
@@ -766,6 +790,7 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
if (viewp->handleToolTip(local_x, local_y, mask)
|| viewp->blockMouseEvent(local_x, local_y))
{
+ // This is NOT event recording related
viewp->logMouseEvent();
return viewp;
}
@@ -824,6 +849,7 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
if (viewp->handleHover(local_x, local_y, mask)
|| viewp->blockMouseEvent(local_x, local_y))
{
+ // This is NOT event recording related
viewp->logMouseEvent();
return viewp;
}
@@ -907,10 +933,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;
}
}
}
@@ -958,6 +986,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;
}
@@ -987,12 +1020,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)
@@ -1065,7 +1102,7 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
{
- return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
+ return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
}
LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp
new file mode 100644
index 0000000000..a352f621eb
--- /dev/null
+++ b/indra/llui/llviewereventrecorder.cpp
@@ -0,0 +1,296 @@
+/**
+ * @file llviewereventrecorder.cpp
+ * @brief Viewer event recording and playback support for mouse and keyboard events
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ *
+ * Copyright (c) 2013, Linden Research, Inc.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "llviewereventrecorder.h"
+#include "llui.h"
+#include "llleap.h"
+
+LLViewerEventRecorder::LLViewerEventRecorder() {
+
+ clear(UNDEFINED);
+
+ // Remove any previous event log file
+ std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old");
+ LLFile::remove(old_log_ui_events_to_llsd_file);
+
+
+ mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
+ LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file);
+
+}
+
+
+bool LLViewerEventRecorder::displayViewerEventRecorderMenuItems() {
+ return LLUI::sSettingGroups["config"]->getBOOL("ShowEventRecorderMenuItems");
+}
+
+
+void LLViewerEventRecorder::setEventLoggingOn() {
+ if (! mLog.is_open()) {
+ mLog.open(mLogFilename, llofstream::out);
+ }
+ logEvents=true;
+ lldebugs << "LLViewerEventRecorder::setEventLoggingOn event logging turned on" << llendl;
+}
+
+void LLViewerEventRecorder::setEventLoggingOff() {
+ logEvents=false;
+ mLog.flush();
+ mLog.close();
+ lldebugs << "LLViewerEventRecorder::setEventLoggingOff event logging turned off" << llendl;
+}
+
+
+ LLViewerEventRecorder::~LLViewerEventRecorder() {
+ if (mLog.is_open()) {
+ mLog.close();
+ }
+}
+
+void LLViewerEventRecorder::clear_xui() {
+ xui.clear();
+}
+
+void LLViewerEventRecorder::clear(S32 r) {
+
+ xui.clear();
+
+ local_x=r;
+ local_y=r;
+
+ global_x=r;
+ global_y=r;
+
+
+}
+
+void LLViewerEventRecorder::setMouseLocalCoords(S32 x, S32 y) {
+ local_x=x;
+ local_y=y;
+}
+
+void LLViewerEventRecorder::setMouseGlobalCoords(S32 x, S32 y) {
+ global_x=x;
+ global_y=y;
+}
+
+void LLViewerEventRecorder::updateMouseEventInfo(S32 local_x, S32 local_y, S32 global_x, S32 global_y, std::string mName) {
+
+ LLView * target_view = LLUI::resolvePath(LLUI::getRootView(), xui);
+ if (! target_view) {
+ lldebugs << "LLViewerEventRecorder::updateMouseEventInfo - xui path on file at moment is NOT valid - so DO NOT record these local coords" << llendl;
+ return;
+ }
+ lldebugs << "LLViewerEventRecorder::updateMouseEventInfo b4 updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << llendl;
+
+
+ if (this->local_x < 1 && this->local_y<1 && local_x && local_y) {
+ this->local_x=local_x;
+ this->local_y=local_y;
+ }
+ this->global_x=global_x;
+ this->global_y=global_y;
+
+ // ONLY record deepest xui path for hierarchy searches - or first/only xui for floaters/panels reached via mouse captor - and llmousehandler
+ if (mName!="" && mName!="/" && xui=="") {
+ // xui=std::string("/")+mName+xui;
+ //xui=mName+xui;
+ xui = mName; // TODO review confirm we never call with partial path - also cAN REMOVE CHECK FOR "" - ON OTHER HAND IT'S PRETTY HARMLESS
+ }
+
+ lldebugs << "LLViewerEventRecorder::updateMouseEventInfo after updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << llendl;
+}
+
+void LLViewerEventRecorder::logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype) {
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("event",LLSD(std::string("visibility")));
+
+ if (visibility) {
+ event.insert("visibility",LLSD(true));
+ } else {
+ event.insert("visibility",LLSD(false));
+ }
+
+ if (event_subtype!="") {
+ event.insert("event_subtype", LLSD(event_subtype));
+ }
+
+ if(name!="") {
+ event.insert("name",LLSD(name));
+ }
+
+ if (xui!="") {
+ event.insert("path",LLSD(xui));
+ }
+
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+ recordEvent(event);
+}
+
+
+std::string LLViewerEventRecorder::get_xui() {
+ return xui;
+}
+void LLViewerEventRecorder::update_xui(std::string xui) {
+ if (xui!="" && this->xui=="" ) {
+ lldebugs << "LLViewerEventRecorder::update_xui to " << xui << llendl;
+ this->xui=xui;
+ } else {
+ lldebugs << "LLViewerEventRecorder::update_xui called with empty string" << llendl;
+ }
+}
+
+void LLViewerEventRecorder::logKeyEvent(KEY key, MASK mask) {
+
+ // NOTE: Event recording only logs keydown events - the viewer itself hides keyup events at a fairly low level in the code and does not appear to care about them anywhere
+
+ LLSD event = LLSD::emptyMap();
+
+ event.insert("event",LLSD("type"));
+
+ // keysym ...or
+ // keycode...or
+ // char
+ event.insert("keysym",LLSD(LLKeyboard::stringFromKey(key)));
+
+ // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
+ // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
+ // break the test script and it would be useful to have more context to make these sorts of edits safer
+
+ // TODO replace this with a call which extracts to an array of names of masks (just like vita expects during playback)
+ // This is looking more and more like an object is a good idea, for this part a handy method call to setMask(mask) would be nice :-)
+ // call the func - llkeyboard::llsdStringarrayFromMask
+
+ LLSD key_mask=LLSD::emptyArray();
+
+ if (mask & MASK_CONTROL) { key_mask.append(LLSD("CTL")); } // Mac command key - has code of 0x1 in llcommon/indra_contstants
+ if (mask & MASK_ALT) { key_mask.append(LLSD("ALT")); }
+ if (mask & MASK_SHIFT) { key_mask.append(LLSD("SHIFT")); }
+ if (mask & MASK_MAC_CONTROL) { key_mask.append(LLSD("MAC_CONTROL")); }
+
+ event.insert("mask",key_mask);
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+
+ // Although vita has keyDown and keyUp requests it does not have type as a high-level concept
+ // (maybe it should) - instead it has a convenience method that generates the keydown and keyup events
+ // Here we will use "type" as our event type
+
+ lldebugs << "LLVIewerEventRecorder::logKeyEvent Serialized LLSD for event " << event.asString() << "\n" << llendl;
+
+
+ //lldebugs << "[VITA] key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << "handled by " << getName() << llendl;
+ lldebugs << "LLVIewerEventRecorder::logKeyEvent key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << llendl;
+
+
+ recordEvent(event);
+
+}
+
+void LLViewerEventRecorder::playbackRecording() {
+
+ LLSD LeapCommand;
+
+ // ivita sets this on startup, it also sends commands to the viewer to make start, stop, and playback menu items visible in viewer
+ LeapCommand =LLUI::sSettingGroups["config"]->getLLSD("LeapPlaybackEventsCommand");
+
+ lldebugs << "[VITA] launching playback - leap command is: " << LLSDXMLStreamer(LeapCommand) << llendl;
+ LLLeap::create("", LeapCommand, false); // exception=false
+
+}
+
+
+void LLViewerEventRecorder::recordEvent(LLSD event) {
+ lldebugs << "LLViewerEventRecorder::recordEvent event written to log: " << LLSDXMLStreamer(event) << llendl;
+ mLog << event << std::endl;
+
+}
+void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) {
+ if (! logEvents) return;
+
+ // Note: keyUp is not captured since the viewer seems to not care about keyUp events
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+
+
+ // keysym ...or
+ // keycode...or
+ // char
+
+ lldebugs << "Wrapped in conversion to wstring " << wstring_to_utf8str(LLWString( 1, uni_char)) << "\n" << llendl;
+
+ event.insert("char",
+ LLSD( wstring_to_utf8str(LLWString( 1,uni_char)) )
+ );
+
+ // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
+ // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
+ // break the test script and it would be useful to have more context to make these sorts of edits safer
+
+ // TODO need to consider mask keys too? Doesn't seem possible - at least not easily at this point
+
+ event.insert("event",LLSD("keyDown"));
+
+ lldebugs << "[VITA] unicode key: " << uni_char << llendl;
+ lldebugs << "[VITA] dumpxml " << LLSDXMLStreamer(event) << "\n" << llendl;
+
+
+ recordEvent(event);
+
+}
+
+void LLViewerEventRecorder::logMouseEvent(std::string button_state,std::string button_name)
+{
+ if (! logEvents) return;
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("event",LLSD(std::string("mouse"+ button_state)));
+ event.insert("button",LLSD(button_name));
+ if (xui!="") {
+ event.insert("path",LLSD(xui));
+ }
+
+ if (local_x>0 && local_y>0) {
+ event.insert("local_x",LLSD(local_x));
+ event.insert("local_y",LLSD(local_y));
+ }
+
+ if (global_x>0 && global_y>0) {
+ event.insert("global_x",LLSD(global_x));
+ event.insert("global_y",LLSD(global_y));
+ }
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+ recordEvent(event);
+
+
+ clear(UNDEFINED);
+
+
+}
diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h
new file mode 100644
index 0000000000..72ca643ced
--- /dev/null
+++ b/indra/llui/llviewereventrecorder.h
@@ -0,0 +1,103 @@
+/**
+ * @file llviewereventrecorder.h
+ * @brief Viewer event recording and playback support for mouse and keyboard events
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ *
+ * Copyright (c) 2013, Linden Research, Inc.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_VIEWER_EVENT_RECORDER
+#define LL_VIEWER_EVENT_RECORDER
+
+
+#include "linden_common.h"
+
+#include "lldir.h"
+#include "llsd.h"
+#include "llfile.h"
+#include "llvfile.h"
+#include "lldate.h"
+#include "llsdserialize.h"
+#include "llkeyboard.h"
+#include "llstring.h"
+
+#include <sstream>
+
+#include "llsingleton.h" // includes llerror which we need here so we can skip the include here
+
+class LLViewerEventRecorder : public LLSingleton<LLViewerEventRecorder>
+{
+
+ public:
+
+ LLViewerEventRecorder(); // TODO Protect constructor better if we can (not happy in private section) - could add a factory... - we are singleton
+ ~LLViewerEventRecorder();
+
+
+ void updateMouseEventInfo(S32 local_x,S32 local_y, S32 global_x, S32 global_y, std::string mName);
+ void setMouseLocalCoords(S32 x,S32 y);
+ void setMouseGlobalCoords(S32 x,S32 y);
+
+ void logMouseEvent(std::string button_state, std::string button_name );
+ void logKeyEvent(KEY key, MASK mask);
+ void logKeyUnicodeEvent(llwchar uni_char);
+
+ void logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype);
+
+ void clear_xui();
+ std::string get_xui();
+ void update_xui(std::string xui);
+
+ bool getLoggingStatus();
+ void setEventLoggingOn();
+ void setEventLoggingOff();
+
+ void playbackRecording();
+
+ bool displayViewerEventRecorderMenuItems();
+
+
+ protected:
+ // On if we wish to log events at the moment - toggle via Develop/Recorder submenu
+ bool logEvents;
+
+ std::string mLogFilename;
+ llofstream mLog;
+
+
+ private:
+
+ // Mouse event info
+ S32 global_x;
+ S32 global_y;
+ S32 local_x;
+ S32 local_y;
+
+ // XUI path of UI element
+ std::string xui;
+
+ // Actually write the event out to llsd log file
+ void recordEvent(LLSD event);
+
+ void clear(S32 r);
+
+ static const S32 UNDEFINED=-1;
+};
+#endif
diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h
index 2cae6f3f72..81b25601a9 100755
--- a/indra/llwindow/llwindowmacosx-objc.h
+++ b/indra/llwindow/llwindowmacosx-objc.h
@@ -25,6 +25,9 @@
* $/LicenseInfo$
*/
+#ifndef LL_LLWINDOWMACOSX_OBJC_H
+#define LL_LLWINDOWMACOSX_OBJC_H
+
#include <map>
#include <vector>
@@ -144,3 +147,5 @@ NSWindowRef getMainAppWindow();
GLViewRef getGLView();
unsigned int getModifiers();
+
+#endif // LL_LLWINDOWMACOSX_OBJC_H
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 56472e6b45..d5b62bd3a7 100755
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -1300,6 +1300,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)
{
switch (id)
diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp
index 16f2290787..97fad7feb0 100755
--- a/indra/llxml/llcontrol.cpp
+++ b/indra/llxml/llcontrol.cpp
@@ -391,7 +391,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;
}
@@ -630,14 +630,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;
}
@@ -650,7 +650,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;
}
@@ -668,7 +668,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;
@@ -822,7 +822,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) )
{
@@ -838,12 +838,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;
@@ -856,14 +856,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);
}
@@ -976,6 +976,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;
}
@@ -1012,7 +1013,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<F32>("gFoo", 5.f, 1.f, 20.f);
@@ -1273,19 +1274,19 @@ LLColor4 convert_from_llsd<LLColor4>(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/CMakeLists.txt b/indra/newview/CMakeLists.txt
index d3835a5b2a..1fea6dea9f 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -191,6 +191,7 @@ set(viewer_SOURCE_FILES
llexpandabletextbox.cpp
llexternaleditor.cpp
llface.cpp
+ llfacebookconnect.cpp
llfasttimerview.cpp
llfavoritesbar.cpp
llfeaturemanager.cpp
@@ -273,6 +274,7 @@ set(viewer_SOURCE_FILES
llfloatersettingsdebug.cpp
llfloatersidepanelcontainer.cpp
llfloatersnapshot.cpp
+ llfloatersocial.cpp
llfloatersounddevices.cpp
llfloaterspellchecksettings.cpp
llfloatertelehub.cpp
@@ -508,6 +510,7 @@ set(viewer_SOURCE_FILES
llsidetraypanelcontainer.cpp
llsky.cpp
llslurl.cpp
+ llsnapshotlivepreview.cpp
llspatialpartition.cpp
llspeakers.cpp
llspeakingindicatormanager.cpp
@@ -776,6 +779,7 @@ set(viewer_HEADER_FILES
llexpandabletextbox.h
llexternaleditor.h
llface.h
+ llfacebookconnect.h
llfasttimerview.h
llfavoritesbar.h
llfeaturemanager.h
@@ -858,6 +862,7 @@ set(viewer_HEADER_FILES
llfloatersettingsdebug.h
llfloatersidepanelcontainer.h
llfloatersnapshot.h
+ llfloatersocial.h
llfloatersounddevices.h
llfloaterspellchecksettings.h
llfloatertelehub.h
@@ -1082,6 +1087,7 @@ set(viewer_HEADER_FILES
llsidetraypanelcontainer.h
llsky.h
llslurl.h
+ llsnapshotlivepreview.h
llspatialpartition.h
llspeakers.h
llspeakingindicatormanager.h
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 424e1794de..c47e8b5872 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-3.6.8
+3.6.10
diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml
index 4659673333..60c942094a 100755
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -216,6 +216,16 @@
is_running_function="Floater.IsOpen"
is_running_parameters="snapshot"
/>
+ <command name="social"
+ available_in_toybox="true"
+ icon="Command_Social_Icon"
+ label_ref="Command_Social_Label"
+ tooltip_ref="Command_Social_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="social"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="social"
+ />
<command name="speak"
available_in_toybox="true"
icon="Command_Speak_Icon"
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index eeed12499d..41aac583d7 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -4941,6 +4941,16 @@
<key>Value</key>
<array />
</map>
+ <key>LeapPlaybackEventsCommand</key>
+ <map>
+ <key>Comment</key>
+ <string>Command line to use leap to launch playback of event recordings</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>LLSD</string>
+ <key>Value</key>
+ </map>
<key>LSLFindCaseInsensitivity</key>
<map>
<key>Comment</key>
@@ -10444,6 +10454,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>ShowEventRecorderMenuItems</key>
+ <map>
+ <key>Comment</key>
+ <string>Whether or not Event Recorder menu choices - Start / Stop event recording should appear in the (currently) Develop menu</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>ShowGestureButton</key>
<map>
<key>Comment</key>
@@ -13084,6 +13105,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>SocialPhotoResolution</key>
+ <map>
+ <key>Comment</key>
+ <string>Default resolution when sharing photo using the social floater</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>[i800,i600]</string>
+ </map>
<key>sourceid</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/app_settings/toolbars.xml b/indra/newview/app_settings/toolbars.xml
index 29c019719d..86f9912815 100755
--- a/indra/newview/app_settings/toolbars.xml
+++ b/indra/newview/app_settings/toolbars.xml
@@ -6,6 +6,7 @@
<command name="speak"/>
<command name="destinations"/>
<command name="people"/>
+ <command name="social"/>
<command name="profile"/>
<command name="move"/>
<command name="view"/>
diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp
index b9ec304b7e..3410a37890 100755
--- a/indra/newview/llagentui.cpp
+++ b/indra/newview/llagentui.cpp
@@ -112,6 +112,11 @@ BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const
case LOCATION_FORMAT_NORMAL:
buffer = llformat("%s", region_name.c_str());
break;
+ case LOCATION_FORMAT_NORMAL_COORDS:
+ buffer = llformat("%s (%d, %d, %d)",
+ region_name.c_str(),
+ pos_x, pos_y, pos_z);
+ break;
case LOCATION_FORMAT_NO_COORDS:
buffer = llformat("%s%s%s",
region_name.c_str(),
@@ -143,6 +148,11 @@ BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const
case LOCATION_FORMAT_NORMAL:
buffer = llformat("%s, %s", parcel_name.c_str(), region_name.c_str());
break;
+ case LOCATION_FORMAT_NORMAL_COORDS:
+ buffer = llformat("%s (%d, %d, %d)",
+ parcel_name.c_str(),
+ pos_x, pos_y, pos_z);
+ break;
case LOCATION_FORMAT_NO_MATURITY:
buffer = llformat("%s, %s (%d, %d, %d)",
parcel_name.c_str(),
diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h
index dda5dc1fd1..bb48dad14c 100755
--- a/indra/newview/llagentui.h
+++ b/indra/newview/llagentui.h
@@ -35,6 +35,7 @@ public:
enum ELocationFormat
{
LOCATION_FORMAT_NORMAL, // Parcel
+ LOCATION_FORMAT_NORMAL_COORDS, // Parcel (x, y, z)
LOCATION_FORMAT_LANDMARK, // Parcel, Region
LOCATION_FORMAT_NO_MATURITY, // Parcel, Region (x, y, z)
LOCATION_FORMAT_NO_COORDS, // Parcel, Region - Maturity
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index f67142d1ed..539d186441 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -223,6 +223,10 @@
#include "llmachineid.h"
#include "llmainlooprepeater.h"
+
+#include "llviewereventrecorder.h"
+
+
// *FIX: These extern globals should be cleaned up.
// The globals either represent state/config/resource-storage of either
// this app, or another 'component' of the viewer. App globals should be
@@ -696,6 +700,7 @@ LLAppViewer::LLAppViewer() :
LLAppViewer::~LLAppViewer()
{
delete mSettingsLocationList;
+ LLViewerEventRecorder::instance().~LLViewerEventRecorder();
LLLoginInstance::instance().setUpdaterService(0);
@@ -2250,13 +2255,13 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
BOOST_FOREACH(const SettingsFile& file, group.files)
{
- 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;
}
@@ -2285,7 +2290,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
@@ -2299,7 +2304,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;
}
}
}
@@ -2395,8 +2400,6 @@ bool LLAppViewer::initConfiguration()
gSavedSettings.setString("ClientSettingsFile",
gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global")));
- gSavedSettings.setString("VersionChannelName", LLVersionInfo::getChannel());
-
#ifndef LL_RELEASE_FOR_DOWNLOAD
// provide developer build only overrides for these control variables that are not
// persisted to settings.xml
@@ -2460,8 +2463,8 @@ bool LLAppViewer::initConfiguration()
gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
clp.getOption("settings")[0]);
gSavedSettings.setString("ClientSettingsFile", 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;
}
// - load overrides from user_settings
@@ -2477,8 +2480,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");
@@ -2486,8 +2489,8 @@ 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");
@@ -2575,9 +2578,13 @@ bool LLAppViewer::initConfiguration()
}
}
+ if (clp.hasOption("logevents")) {
+ LLViewerEventRecorder::instance().setEventLoggingOn();
+ }
+
std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel"));
if(! CmdLineChannel.empty())
- {
+ {
LLVersionInfo::resetChannel(CmdLineChannel);
}
@@ -2589,16 +2596,16 @@ bool LLAppViewer::initConfiguration()
LLFastTimer::sLog = TRUE;
LLFastTimer::sLogName = std::string("performance");
}
-
+
std::string test_name(gSavedSettings.getString("LogMetrics"));
if (! test_name.empty())
- {
- LLFastTimer::sMetricLog = TRUE ;
+ {
+ LLFastTimer::sMetricLog = TRUE ;
// '--logmetrics' is specified with a named test metric argument so the data gathering is done only on that test
// In the absence of argument, every metric would be gathered (makes for a rather slow run and hard to decipher report...)
llinfos << "'--logmetrics' argument : " << test_name << llendl;
- LLFastTimer::sLogName = test_name;
- }
+ LLFastTimer::sLogName = test_name;
+ }
if (clp.hasOption("graphicslevel"))
{
@@ -2607,14 +2614,14 @@ bool LLAppViewer::initConfiguration()
// that value for validity.
U32 graphicslevel = gSavedSettings.getU32("RenderQualityPerformance");
if (LLFeatureManager::instance().isValidGraphicsLevel(graphicslevel))
- {
+ {
// graphicslevel is valid: save it and engage it later. Capture
// the requested value separately from the settings variable
// because, if this is the first run, LLViewerWindow's constructor
// will call LLFeatureManager::applyRecommendedSettings(), which
// overwrites this settings variable!
mForceGraphicsLevel = graphicslevel;
- }
+ }
}
LLFastTimerView::sAnalyzePerformance = gSavedSettings.getBOOL("AnalyzePerformance");
@@ -2645,16 +2652,32 @@ bool LLAppViewer::initConfiguration()
// What can happen is that someone can use IE (or potentially
// other browsers) and do the rough equivalent of command
// injection and steal passwords. Phoenix. SL-55321
+ LLSLURL start_slurl;
std::string CmdLineLoginLocation(gSavedSettings.getString("CmdLineLoginLocation"));
if(! CmdLineLoginLocation.empty())
- {
- LLSLURL start_slurl(CmdLineLoginLocation);
+ {
+ start_slurl = CmdLineLoginLocation;
LLStartUp::setStartSLURL(start_slurl);
if(start_slurl.getType() == LLSLURL::LOCATION)
{
LLGridManager::getInstance()->setGridChoice(start_slurl.getGrid());
+ }
+ }
+
+ //RN: if we received a URL, hand it off to the existing instance.
+ // don't call anotherInstanceRunning() when doing URL handoff, as
+ // it relies on checking a marker file which will not work when running
+ // out of different directories
+
+ if (start_slurl.isValid() &&
+ (gSavedSettings.getBOOL("SLURLPassToOtherInstance")))
+ {
+ if (sendURLToOtherInstance(start_slurl.getSLURLString()))
+ {
+ // successfully handed off URL to existing instance, exit
+ return false;
}
- }
+ }
const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent");
if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString())
@@ -2795,7 +2818,7 @@ bool LLAppViewer::initConfiguration()
LL_DEBUGS("AppInit")<<"set start from NextLoginLocation: "<<nextLoginLocation<<LL_ENDL;
LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation));
}
- else if ((clp.hasOption("login") || clp.hasOption("autologin"))
+ else if ( ( clp.hasOption("login") || clp.hasOption("autologin"))
&& gSavedSettings.getString("CmdLineLoginLocation").empty())
{
// If automatic login from command line with --login switch
@@ -3179,7 +3202,7 @@ bool LLAppViewer::initWindow()
LLFeatureManager::getInstance()->setGraphicsLevel(*mForceGraphicsLevel, false);
gSavedSettings.setU32("RenderQualityPerformance", *mForceGraphicsLevel);
}
-
+
// Set this flag in case we crash while initializing GL
gSavedSettings.setBOOL("RenderInitError", TRUE);
gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 3cf3c739d9..80a80f4298 100755
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -172,21 +172,20 @@ void ll_nvapi_init(NvDRSSessionHandle hSession)
nvapi_error(status);
return;
}
+
+ // (5) Now we apply (or save) our changes to the system
+ status = NvAPI_DRS_SaveSettings(hSession);
+ if (status != NVAPI_OK)
+ {
+ nvapi_error(status);
+ return;
+ }
}
else if (status != NVAPI_OK)
{
nvapi_error(status);
return;
}
-
-
-
- // (5) Now we apply (or save) our changes to the system
- status = NvAPI_DRS_SaveSettings(hSession);
- if (status != NVAPI_OK)
- {
- nvapi_error(status);
- }
}
//#define DEBUGGING_SEH_FILTER 1
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index b513a52ff7..70cc48f12b 100755
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -73,6 +73,8 @@
#include "llcallingcard.h"
#include "llslurl.h" // IDEVO
#include "llsidepanelinventory.h"
+#include "llavatarname.h"
+#include "llagentui.h"
// static
void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::string& name)
@@ -391,6 +393,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 6e1198cd09..403414558e 100755
--- a/indra/newview/llavataractions.h
+++ b/indra/newview/llavataractions.h
@@ -111,6 +111,12 @@ public:
static void pay(const LLUUID& id);
/**
+ * Request teleport from other avatar
+ */
+ static void teleportRequest(const LLUUID& id);
+ static void teleport_request_callback(const LLSD& notification, const LLSD& response);
+
+ /**
* Share items with the avatar.
*/
static void share(const LLUUID& id);
diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp
index 88884042d4..131aea9da3 100755
--- a/indra/newview/llchiclet.cpp
+++ b/indra/newview/llchiclet.cpp
@@ -220,18 +220,25 @@ void LLNotificationChiclet::setCounter(S32 counter)
bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification )
{
- if (notification->getName() == "ScriptDialog")
+ bool displayNotification;
+ if ( (notification->getName() == "ScriptDialog") // special case for scripts
+ // if there is no toast window for the notification, filter it
+ || (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID()))
+ )
{
- return false;
+ displayNotification = false;
}
-
- if( !(notification->canLogToIM() && notification->hasFormElements())
- && (!notification->getPayload().has("give_inventory_notification")
- || notification->getPayload()["give_inventory_notification"]))
+ else if( !(notification->canLogToIM() && notification->hasFormElements())
+ && (!notification->getPayload().has("give_inventory_notification")
+ || notification->getPayload()["give_inventory_notification"]))
{
- return true;
+ displayNotification = true;
}
- return false;
+ else
+ {
+ displayNotification = false;
+ }
+ return displayNotification;
}
//////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp
index 5ab108b39f..44212298cf 100755
--- a/indra/newview/llconversationloglist.cpp
+++ b/indra/newview/llconversationloglist.cpp
@@ -313,6 +313,10 @@ void LLConversationLogList::onCustomAction(const LLSD& userdata)
{
LLAvatarActions::offerTeleport(selected_conversation_participant_id);
}
+ else if ("request_teleport" == command_name)
+ {
+ LLAvatarActions::teleportRequest(selected_conversation_participant_id);
+ }
else if("add_friend" == command_name)
{
if (!LLAvatarActions::isFriend(selected_conversation_participant_id))
diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp
index 192a594c9d..affa24f78c 100755
--- a/indra/newview/llconversationmodel.cpp
+++ b/indra/newview/llconversationmodel.cpp
@@ -132,6 +132,7 @@ void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32
items.push_back(std::string("view_profile"));
items.push_back(std::string("im"));
items.push_back(std::string("offer_teleport"));
+ items.push_back(std::string("request_teleport"));
items.push_back(std::string("voice_call"));
items.push_back(std::string("chat_history"));
items.push_back(std::string("separator_chat_history"));
diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp
new file mode 100644
index 0000000000..611d18d6d6
--- /dev/null
+++ b/indra/newview/llfacebookconnect.cpp
@@ -0,0 +1,577 @@
+/**
+ * @file llfacebookconnect.h
+ * @author Merov, Cho, Gil
+ * @brief Connection to Facebook Service
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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 "llviewerprecompiledheaders.h"
+
+#include "llfacebookconnect.h"
+
+#include "llagent.h"
+#include "llcallingcard.h" // for LLAvatarTracker
+#include "llcommandhandler.h"
+#include "llhttpclient.h"
+#include "llnotificationsutil.h"
+#include "llurlaction.h"
+#include "llimagepng.h"
+#include "llimagejpeg.h"
+#include "lltrans.h"
+#include "llevents.h"
+#include "llviewerregion.h"
+
+#include "llfloaterwebcontent.h"
+#include "llfloaterreg.h"
+
+boost::scoped_ptr<LLEventPump> LLFacebookConnect::sStateWatcher(new LLEventStream("FacebookConnectState"));
+boost::scoped_ptr<LLEventPump> LLFacebookConnect::sInfoWatcher(new LLEventStream("FacebookConnectInfo"));
+boost::scoped_ptr<LLEventPump> LLFacebookConnect::sContentWatcher(new LLEventStream("FacebookConnectContent"));
+
+// Local functions
+void log_facebook_connect_error(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description)
+{
+ // Note: 302 (redirect) is *not* an error that warrants logging
+ if (status != 302)
+ {
+ LL_WARNS("FacebookConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL;
+ }
+}
+
+void toast_user_for_success()
+{
+ LLSD args;
+ args["MESSAGE"] = LLTrans::getString("facebook_post_success");
+ LLNotificationsUtil::add("FacebookConnect", args);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookConnectHandler : public LLCommandHandler
+{
+public:
+ LLFacebookConnectHandler() : LLCommandHandler("fbc", UNTRUSTED_THROTTLE) { }
+
+ bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
+ {
+ if (tokens.size() > 0)
+ {
+ if (tokens[0].asString() == "connect")
+ {
+ // this command probably came from the fbc_web browser, so close it
+ LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web");
+ if (fbc_web)
+ {
+ fbc_web->closeFloater();
+ }
+
+ // connect to facebook
+ if (query_map.has("code"))
+ {
+ LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state"));
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+};
+LLFacebookConnectHandler gFacebookConnectHandler;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookConnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookConnectResponder);
+public:
+
+ LLFacebookConnectResponder()
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FacebookConnect") << "Connect successful. content: " << content << LL_ENDL;
+
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED);
+ }
+ else if (status != 302)
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
+ log_facebook_connect_error("Connect", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFacebookConnect::instance().openFacebookWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookShareResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookShareResponder);
+public:
+
+ LLFacebookShareResponder()
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTING);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ toast_user_for_success();
+ LL_DEBUGS("FacebookConnect") << "Post successful. content: " << content << LL_ENDL;
+
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED);
+ }
+ else if (status == 404)
+ {
+ LLFacebookConnect::instance().connectToFacebook();
+ }
+ else
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POST_FAILED);
+ log_facebook_connect_error("Share", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFacebookConnect::instance().openFacebookWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookDisconnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookDisconnectResponder);
+public:
+
+ LLFacebookDisconnectResponder()
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECTING);
+ }
+
+ void setUserDisconnected()
+ {
+ // Clear data
+ LLFacebookConnect::instance().clearInfo();
+ LLFacebookConnect::instance().clearContent();
+ //Notify state change
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FacebookConnect") << "Disconnect successful. content: " << content << LL_ENDL;
+ setUserDisconnected();
+
+ }
+ //User not found so already disconnected
+ else if(status == 404)
+ {
+ LL_DEBUGS("FacebookConnect") << "Already disconnected. content: " << content << LL_ENDL;
+ setUserDisconnected();
+ }
+ else
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED);
+ log_facebook_connect_error("Disconnect", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookConnectedResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookConnectedResponder);
+public:
+
+ LLFacebookConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect)
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FacebookConnect") << "Connect successful. content: " << content << LL_ENDL;
+
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED);
+ }
+ else
+ {
+ // show the facebook login page if not connected yet
+ if (status == 404)
+ {
+ if (mAutoConnect)
+ {
+ LLFacebookConnect::instance().connectToFacebook();
+ }
+ else
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED);
+ }
+ }
+ else
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
+ log_facebook_connect_error("Connected", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+ }
+
+private:
+ bool mAutoConnect;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookInfoResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookInfoResponder);
+public:
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& info)
+ {
+ if (isGoodStatus(status))
+ {
+ llinfos << "Facebook: Info received" << llendl;
+ LL_DEBUGS("FacebookConnect") << "Getting Facebook info successful. info: " << info << LL_ENDL;
+ LLFacebookConnect::instance().storeInfo(info);
+ }
+ else
+ {
+ log_facebook_connect_error("Info", status, reason, info.get("error_code"), info.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFacebookConnect::instance().openFacebookWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFacebookFriendsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFacebookFriendsResponder);
+public:
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FacebookConnect") << "Getting Facebook friends successful. content: " << content << LL_ENDL;
+ LLFacebookConnect::instance().storeContent(content);
+ }
+ else
+ {
+ log_facebook_connect_error("Friends", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFacebookConnect::instance().openFacebookWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLFacebookConnect::LLFacebookConnect()
+: mConnectionState(FB_NOT_CONNECTED),
+ mConnected(false),
+ mInfo(),
+ mContent(),
+ mRefreshInfo(false),
+ mRefreshContent(false),
+ mReadFromMaster(false)
+{
+}
+
+void LLFacebookConnect::openFacebookWeb(std::string url)
+{
+ // Open the URL in an internal browser window without navigation UI
+ LLFloaterWebContent::Params p;
+ p.url(url).show_chrome(true);
+ p.url(url).allow_address_entry(false);
+ p.url(url).allow_back_forward_navigation(false);
+ p.url(url).trusted_content(true);
+ LLFloater *floater = LLFloaterReg::showInstance("fbc_web", p);
+ //the internal web browser has a bug that prevents it from gaining focus unless a mouse event occurs first (it seems).
+ //So when showing the internal web browser, set focus to it's containing floater "fbc_web". When a mouse event
+ //occurs on the "webbrowser" panel part of the floater, a mouse cursor will properly show and the "webbrowser" will gain focus.
+ //fbc_web floater contains the "webbrowser" panel. JIRA: ACME-744
+ gFocusMgr.setKeyboardFocus( floater );
+
+ //LLUrlAction::openURLExternal(url);
+}
+
+std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route, bool include_read_from_master)
+{
+ std::string url = gAgent.getRegion()->getCapability("FacebookConnect");
+ url += route;
+
+ if (include_read_from_master && mReadFromMaster)
+ {
+ url += "?read_from_master=true";
+ }
+ return url;
+}
+
+void LLFacebookConnect::connectToFacebook(const std::string& auth_code, const std::string& auth_state)
+{
+ LLSD body;
+ if (!auth_code.empty())
+ body["code"] = auth_code;
+ if (!auth_state.empty())
+ body["state"] = auth_state;
+
+ LLHTTPClient::put(getFacebookConnectURL("/connection"), body, new LLFacebookConnectResponder());
+}
+
+void LLFacebookConnect::disconnectFromFacebook()
+{
+ LLHTTPClient::del(getFacebookConnectURL("/connection"), new LLFacebookDisconnectResponder());
+}
+
+void LLFacebookConnect::checkConnectionToFacebook(bool auto_connect)
+{
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFacebookConnectURL("/connection", true), new LLFacebookConnectedResponder(auto_connect),
+ LLSD(), timeout, follow_redirects);
+}
+
+void LLFacebookConnect::loadFacebookInfo()
+{
+ if(mRefreshInfo)
+ {
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFacebookConnectURL("/info", true), new LLFacebookInfoResponder(),
+ LLSD(), timeout, follow_redirects);
+ }
+}
+
+void LLFacebookConnect::loadFacebookFriends()
+{
+ if(mRefreshContent)
+ {
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFacebookConnectURL("/friends", true), new LLFacebookFriendsResponder(),
+ LLSD(), timeout, follow_redirects);
+ }
+}
+
+void LLFacebookConnect::postCheckin(const std::string& location, const std::string& name, const std::string& description, const std::string& image, const std::string& message)
+{
+ LLSD body;
+ if (!location.empty())
+ body["location"] = location;
+ if (!name.empty())
+ body["name"] = name;
+ if (!description.empty())
+ body["description"] = description;
+ if (!image.empty())
+ body["image"] = image;
+ if (!message.empty())
+ body["message"] = message;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getFacebookConnectURL("/share/checkin", true), body, new LLFacebookShareResponder());
+}
+
+void LLFacebookConnect::sharePhoto(const std::string& image_url, const std::string& caption)
+{
+ LLSD body;
+ body["image"] = image_url;
+ body["caption"] = caption;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getFacebookConnectURL("/share/photo", true), body, new LLFacebookShareResponder());
+}
+
+void LLFacebookConnect::sharePhoto(LLPointer<LLImageFormatted> image, const std::string& caption)
+{
+ std::string imageFormat;
+ if (dynamic_cast<LLImagePNG*>(image.get()))
+ {
+ imageFormat = "png";
+ }
+ else if (dynamic_cast<LLImageJPEG*>(image.get()))
+ {
+ imageFormat = "jpg";
+ }
+ else
+ {
+ llwarns << "Image to upload is not a PNG or JPEG" << llendl;
+ return;
+ }
+
+ // All this code is mostly copied from LLWebProfile::post()
+ const std::string boundary = "----------------------------0123abcdefab";
+
+ LLSD headers;
+ headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
+
+ std::ostringstream body;
+
+ // *NOTE: The order seems to matter.
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"caption\"\r\n\r\n"
+ << caption << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << imageFormat << "\"\r\n"
+ << "Content-Type: image/" << imageFormat << "\r\n\r\n";
+
+ // Insert the image data.
+ // *FIX: Treating this as a string will probably screw it up ...
+ U8* image_data = image->getData();
+ for (S32 i = 0; i < image->getDataSize(); ++i)
+ {
+ body << image_data[i];
+ }
+
+ body << "\r\n--" << boundary << "--\r\n";
+
+ // postRaw() takes ownership of the buffer and releases it later.
+ size_t size = body.str().size();
+ U8 *data = new U8[size];
+ memcpy(data, body.str().data(), size);
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::postRaw(getFacebookConnectURL("/share/photo", true), data, size, new LLFacebookShareResponder(), headers);
+}
+
+void LLFacebookConnect::updateStatus(const std::string& message)
+{
+ LLSD body;
+ body["message"] = message;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getFacebookConnectURL("/share/wall", true), body, new LLFacebookShareResponder());
+}
+
+void LLFacebookConnect::storeInfo(const LLSD& info)
+{
+ mInfo = info;
+ mRefreshInfo = false;
+
+ sInfoWatcher->post(info);
+}
+
+const LLSD& LLFacebookConnect::getInfo() const
+{
+ return mInfo;
+}
+
+void LLFacebookConnect::clearInfo()
+{
+ mInfo = LLSD();
+}
+
+void LLFacebookConnect::storeContent(const LLSD& content)
+{
+ mContent = content;
+ mRefreshContent = false;
+
+ sContentWatcher->post(content);
+}
+
+const LLSD& LLFacebookConnect::getContent() const
+{
+ return mContent;
+}
+
+void LLFacebookConnect::clearContent()
+{
+ mContent = LLSD();
+}
+
+void LLFacebookConnect::setDataDirty()
+{
+ mRefreshInfo = true;
+ mRefreshContent = true;
+}
+
+void LLFacebookConnect::setConnectionState(LLFacebookConnect::EConnectionState connection_state)
+{
+ if(connection_state == FB_CONNECTED)
+ {
+ mReadFromMaster = true;
+ setConnected(true);
+ setDataDirty();
+ }
+ else if(connection_state == FB_NOT_CONNECTED)
+ {
+ setConnected(false);
+ }
+ else if(connection_state == FB_POSTED)
+ {
+ mReadFromMaster = false;
+ }
+
+ if (mConnectionState != connection_state)
+ {
+ LLSD state_info;
+ state_info["enum"] = connection_state;
+ sStateWatcher->post(state_info);
+ }
+
+ mConnectionState = connection_state;
+}
+
+void LLFacebookConnect::setConnected(bool connected)
+{
+ mConnected = connected;
+}
diff --git a/indra/newview/llfacebookconnect.h b/indra/newview/llfacebookconnect.h
new file mode 100644
index 0000000000..a77ac24167
--- /dev/null
+++ b/indra/newview/llfacebookconnect.h
@@ -0,0 +1,106 @@
+/**
+ * @file llfacebookconnect.h
+ * @author Merov, Cho, Gil
+ * @brief Connection to Facebook Service
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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_LLFACEBOOKCONNECT_H
+#define LL_LLFACEBOOKCONNECT_H
+
+#include "llsingleton.h"
+#include "llimage.h"
+
+class LLEventPump;
+
+/**
+ * @class LLFacebookConnect
+ *
+ * Manages authentication to, and interaction with, a web service allowing the
+ * the viewer to get Facebook OpenGraph data.
+ */
+class LLFacebookConnect : public LLSingleton<LLFacebookConnect>
+{
+ LOG_CLASS(LLFacebookConnect);
+public:
+ enum EConnectionState
+ {
+ FB_NOT_CONNECTED = 0,
+ FB_CONNECTION_IN_PROGRESS = 1,
+ FB_CONNECTED = 2,
+ FB_CONNECTION_FAILED = 3,
+ FB_POSTING = 4,
+ FB_POSTED = 5,
+ FB_POST_FAILED = 6,
+ FB_DISCONNECTING = 7,
+ FB_DISCONNECT_FAILED = 8
+ };
+
+ void connectToFacebook(const std::string& auth_code = "", const std::string& auth_state = ""); // Initiate the complete FB connection. Please use checkConnectionToFacebook() in normal use.
+ void disconnectFromFacebook(); // Disconnect from the FBC service.
+ void checkConnectionToFacebook(bool auto_connect = false); // Check if an access token is available on the FBC service. If not, call connectToFacebook().
+
+ void loadFacebookInfo();
+ void loadFacebookFriends();
+ void postCheckin(const std::string& location, const std::string& name, const std::string& description, const std::string& picture, const std::string& message);
+ void sharePhoto(const std::string& image_url, const std::string& caption);
+ void sharePhoto(LLPointer<LLImageFormatted> image, const std::string& caption);
+ void updateStatus(const std::string& message);
+
+ void storeInfo(const LLSD& info);
+ const LLSD& getInfo() const;
+ void clearInfo();
+ void storeContent(const LLSD& content);
+ const LLSD& getContent() const;
+ void clearContent();
+ void setDataDirty();
+
+ void setConnectionState(EConnectionState connection_state);
+ void setConnected(bool connected);
+ bool isConnected() { return mConnected; }
+ bool isTransactionOngoing() { return ((mConnectionState == FB_CONNECTION_IN_PROGRESS) || (mConnectionState == FB_POSTING) || (mConnectionState == FB_DISCONNECTING)); }
+ EConnectionState getConnectionState() { return mConnectionState; }
+
+ void openFacebookWeb(std::string url);
+
+private:
+ friend class LLSingleton<LLFacebookConnect>;
+
+ LLFacebookConnect();
+ ~LLFacebookConnect() {};
+ std::string getFacebookConnectURL(const std::string& route = "", bool include_read_from_master = false);
+
+ EConnectionState mConnectionState;
+ BOOL mConnected;
+ LLSD mInfo;
+ LLSD mContent;
+ bool mRefreshInfo;
+ bool mRefreshContent;
+ bool mReadFromMaster;
+
+ static boost::scoped_ptr<LLEventPump> sStateWatcher;
+ static boost::scoped_ptr<LLEventPump> sInfoWatcher;
+ static boost::scoped_ptr<LLEventPump> sContentWatcher;
+};
+
+#endif // LL_LLFACEBOOKCONNECT_H
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index 2c3b34e128..90b4490e1d 100755
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -662,10 +662,10 @@ void LLFloaterIMContainer::setVisible(BOOL visible)
LLFloater* session_floater = widget->getSessionFloater();
if (session_floater != nearby_chat)
{
- widget->setVisibleIfDetached(visible);
- }
+ widget->setVisibleIfDetached(visible);
}
}
+ }
// Now, do the normal multifloater show/hide
LLMultiFloater::setVisible(visible);
@@ -700,13 +700,13 @@ void LLFloaterIMContainer::setVisibleAndFrontmost(BOOL take_focus, const LLSD& k
// Only select other sessions
if (!getSelectedSession().isNull())
{
- selectConversationPair(getSelectedSession(), false, take_focus);
+ selectConversationPair(getSelectedSession(), false, take_focus);
}
if (mInitialized && mIsFirstLaunch)
{
collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed"));
mIsFirstLaunch = false;
- }
+}
}
void LLFloaterIMContainer::updateResizeLimits()
@@ -834,7 +834,7 @@ void LLFloaterIMContainer::assignResizeLimits()
S32 conv_pane_target_width = is_conv_pane_expanded
? ( is_msg_pane_expanded?mConversationsPane->getRect().getWidth():mConversationsPane->getExpandedMinDim() )
- : mConversationsPane->getMinDim();
+ : mConversationsPane->getMinDim();
S32 msg_pane_min_width = is_msg_pane_expanded ? mMessagesPane->getExpandedMinDim() : 0;
S32 new_min_width = conv_pane_target_width + msg_pane_min_width + summary_width_of_visible_borders;
@@ -995,7 +995,7 @@ void LLFloaterIMContainer::setSortOrder(const LLConversationSort& order)
conversation_floater->setSortOrder(order);
}
}
-
+
gSavedSettings.setU32("ConversationSortOrder", (U32)order);
}
@@ -1080,6 +1080,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);
@@ -1182,7 +1186,7 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command,
}
else if("chat_history" == command)
{
- if (selectedIDS.size() > 0)
+ if (selectedIDS.size() > 0)
{
LLAvatarActions::viewChatHistory(selectedIDS.front());
}
@@ -1204,7 +1208,7 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command,
{
LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true);
}
- }
+}
}
}
@@ -1235,7 +1239,7 @@ void LLFloaterIMContainer::doToSelectedGroup(const LLSD& userdata)
if (action == "group_profile")
{
- LLGroupActions::show(mSelectedSession);
+ LLGroupActions::show(mSelectedSession);
}
else if (action == "activate_group")
{
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 6ef4d8717d..8f20d4e082 100755
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -2097,7 +2097,6 @@ void LLPanelLandOptions::refresh()
// virtual
void LLPanelLandOptions::draw()
{
- refreshSearch(); // Is this necessary? JC
LLPanel::draw();
}
@@ -2111,9 +2110,8 @@ void LLPanelLandOptions::refreshSearch()
mCheckShowDirectory->set(FALSE);
mCheckShowDirectory->setEnabled(FALSE);
- // *TODO:Translate
- 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;
}
@@ -2140,10 +2138,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();
- 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 1969435ba1..87ae36716d 100755
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -618,9 +618,12 @@ void LLFloaterPreference::cancel()
// hide translation settings floater
LLFloaterReg::hideInstance("prefs_translation");
- // hide translation settings floater
+ // hide autoreplace settings floater
LLFloaterReg::hideInstance("prefs_autoreplace");
+ // hide spellchecker settings folder
+ LLFloaterReg::hideInstance("prefs_spellchecker");
+
// cancel hardware menu
LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::getTypedInstance<LLFloaterHardwareSettings>("prefs_hardware_settings");
if (hardware_settings)
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 50c013a49d..66bf49331b 100755
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -1734,7 +1734,7 @@ void LLPanelEstateInfo::accessAddCore3(const uuid_vec_t& ids, void* data)
LLSD args;
args["NUM_ADDED"] = llformat("%d",ids.size());
args["MAX_AGENTS"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
- args["LIST_TYPE"] = "Allowed Residents";
+ args["LIST_TYPE"] = LLTrans::getString("RegionInfoListTypeAllowedAgents");
args["NUM_EXCESS"] = llformat("%d",(ids.size()+currentCount)-ESTATE_MAX_ACCESS_IDS);
LLNotificationsUtil::add("MaxAgentOnRegionBatch", args);
delete change_info;
@@ -1750,7 +1750,7 @@ void LLPanelEstateInfo::accessAddCore3(const uuid_vec_t& ids, void* data)
LLSD args;
args["NUM_ADDED"] = llformat("%d",ids.size());
args["MAX_AGENTS"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
- args["LIST_TYPE"] = "Banned Residents";
+ args["LIST_TYPE"] = LLTrans::getString("RegionInfoListTypeBannedAgents");
args["NUM_EXCESS"] = llformat("%d",(ids.size()+currentCount)-ESTATE_MAX_ACCESS_IDS);
LLNotificationsUtil::add("MaxAgentOnRegionBatch", args);
delete change_info;
@@ -2815,9 +2815,10 @@ bool LLDispatchSetEstateAccess::operator()(
}
- std::string msg = llformat("Banned residents: (%d, max %d)",
- totalBannedAgents,
- ESTATE_MAX_ACCESS_IDS);
+ LLStringUtil::format_map_t args;
+ args["[BANNEDAGENTS]"] = llformat("%d", totalBannedAgents);
+ args["[MAXBANNED]"] = llformat("%d", ESTATE_MAX_ACCESS_IDS);
+ std::string msg = LLTrans::getString("RegionInfoBannedResidents", args);
panel->getChild<LLUICtrl>("ban_resident_label")->setValue(LLSD(msg));
if (banned_agent_name_list)
@@ -2837,9 +2838,10 @@ bool LLDispatchSetEstateAccess::operator()(
if (access_flags & ESTATE_ACCESS_MANAGERS)
{
- std::string msg = llformat("Estate Managers: (%d, max %d)",
- num_estate_managers,
- ESTATE_MAX_MANAGERS);
+ LLStringUtil::format_map_t args;
+ args["[ESTATEMANAGERS]"] = llformat("%d", num_estate_managers);
+ args["[MAXMANAGERS]"] = llformat("%d", ESTATE_MAX_MANAGERS);
+ std::string msg = LLTrans::getString("RegionInfoEstateManagers", args);
panel->getChild<LLUICtrl>("estate_manager_label")->setValue(LLSD(msg));
LLNameListCtrl* estate_manager_name_list =
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index d8d62e5bbb..ea385d7baf 100755
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -28,60 +28,23 @@
#include "llfloatersnapshot.h"
-#include "llfloaterreg.h"
-
-// Viewer includes
#include "llagent.h"
-#include "llagentcamera.h"
-#include "llcallbacklist.h"
-#include "llcriticaldamp.h"
-#include "llfloaterperms.h"
-#include "llui.h"
-#include "llfocusmgr.h"
-#include "llbutton.h"
+#include "llfacebookconnect.h"
+#include "llfloaterreg.h"
+#include "llfloatersocial.h"
+#include "llcheckboxctrl.h"
#include "llcombobox.h"
-#include "lleconomy.h"
-#include "lllandmarkactions.h"
-#include "llpanelsnapshot.h"
+#include "llpostcard.h"
+#include "llresmgr.h" // LLLocale
+#include "llsdserialize.h"
#include "llsidetraypanelcontainer.h"
-#include "llsliderctrl.h"
+#include "llsnapshotlivepreview.h"
#include "llspinctrl.h"
#include "llviewercontrol.h"
-#include "lluictrlfactory.h"
-#include "llviewerstats.h"
-#include "llviewercamera.h"
-#include "llviewerwindow.h"
-#include "llviewermenufile.h" // upload_new_resource()
-#include "llcheckboxctrl.h"
-#include "llslurl.h"
#include "lltoolfocus.h"
#include "lltoolmgr.h"
-#include "llwebsharing.h"
-#include "llworld.h"
-#include "llagentui.h"
-
-// Linden library includes
-#include "llfontgl.h"
-#include "llsys.h"
-#include "llrender.h"
-#include "v3dmath.h"
-#include "llmath.h"
-#include "lldir.h"
-#include "llsdserialize.h"
-#include "llgl.h"
-#include "llglheaders.h"
-#include "llimagejpeg.h"
-#include "llimagepng.h"
-#include "llimagebmp.h"
-#include "llimagej2c.h"
-#include "lllocalcliprect.h"
-#include "llnotificationsutil.h"
-#include "llpostcard.h"
-#include "llresmgr.h" // LLLocale
-#include "llvfile.h"
-#include "llvfs.h"
#include "llwebprofile.h"
-#include "llwindow.h"
+#include "llwebsharing.h"
///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
@@ -91,949 +54,12 @@ LLSnapshotFloaterView* gSnapshotFloaterView = NULL;
const F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f;
-F32 SHINE_TIME = 0.5f;
-F32 SHINE_WIDTH = 0.6f;
-F32 SHINE_OPACITY = 0.3f;
-F32 FALL_TIME = 0.6f;
-S32 BORDER_WIDTH = 6;
-
const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512
static LLDefaultChildRegistry::Register<LLSnapshotFloaterView> r("snapshot_floater_view");
-///----------------------------------------------------------------------------
-/// Class LLSnapshotLivePreview
-///----------------------------------------------------------------------------
-class LLSnapshotLivePreview : public LLView
-{
- LOG_CLASS(LLSnapshotLivePreview);
-public:
- enum ESnapshotType
- {
- SNAPSHOT_POSTCARD,
- SNAPSHOT_TEXTURE,
- SNAPSHOT_LOCAL,
- SNAPSHOT_WEB
- };
-
-
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Params()
- {
- name = "snapshot_live_preview";
- mouse_opaque = false;
- }
- };
-
-
- LLSnapshotLivePreview(const LLSnapshotLivePreview::Params& p);
- ~LLSnapshotLivePreview();
-
- /*virtual*/ void draw();
- /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
-
- void setSize(S32 w, S32 h);
- void setWidth(S32 w) { mWidth[mCurImageIndex] = w; }
- void setHeight(S32 h) { mHeight[mCurImageIndex] = h; }
- void getSize(S32& w, S32& h) const;
- S32 getWidth() const { return mWidth[mCurImageIndex]; }
- S32 getHeight() const { return mHeight[mCurImageIndex]; }
- S32 getDataSize() const { return mDataSize; }
- void setMaxImageSize(S32 size) ;
- S32 getMaxImageSize() {return mMaxImageSize ;}
-
- ESnapshotType getSnapshotType() const { return mSnapshotType; }
- LLFloaterSnapshot::ESnapshotFormat getSnapshotFormat() const { return mSnapshotFormat; }
- BOOL getSnapshotUpToDate() const { return mSnapshotUpToDate; }
- BOOL isSnapshotActive() { return mSnapshotActive; }
- LLViewerTexture* getThumbnailImage() const { return mThumbnailImage ; }
- S32 getThumbnailWidth() const { return mThumbnailWidth ; }
- S32 getThumbnailHeight() const { return mThumbnailHeight ; }
- BOOL getThumbnailLock() const { return mThumbnailUpdateLock ; }
- BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;}
- LLViewerTexture* getCurrentImage();
- F32 getImageAspect();
- F32 getAspect() ;
- const LLRect& getImageRect() const { return mImageRect[mCurImageIndex]; }
- BOOL isImageScaled() const { return mImageScaled[mCurImageIndex]; }
- void setImageScaled(BOOL scaled) { mImageScaled[mCurImageIndex] = scaled; }
- const LLVector3d& getPosTakenGlobal() const { return mPosTakenGlobal; }
-
- void setSnapshotType(ESnapshotType type) { mSnapshotType = type; }
- void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; }
- void setSnapshotQuality(S32 quality);
- void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
- void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f);
- void saveWeb();
- void saveTexture();
- BOOL saveLocal();
-
- LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; }
- LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; }
-
- /// Sets size of preview thumbnail image and thhe surrounding rect.
- BOOL setThumbnailImageSize() ;
- void generateThumbnailImage(BOOL force_update = FALSE) ;
- void resetThumbnailImage() { mThumbnailImage = NULL ; }
- void drawPreviewRect(S32 offset_x, S32 offset_y) ;
-
- // Returns TRUE when snapshot generated, FALSE otherwise.
- static BOOL onIdle( void* snapshot_preview );
-
- // callback for region name resolve
- void regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z);
-
-private:
- LLColor4 mColor;
- LLPointer<LLViewerTexture> mViewerImage[2]; //used to represent the scene when the frame is frozen.
- LLRect mImageRect[2];
- S32 mWidth[2];
- S32 mHeight[2];
- BOOL mImageScaled[2];
- S32 mMaxImageSize ;
-
- //thumbnail image
- LLPointer<LLViewerTexture> mThumbnailImage ;
- S32 mThumbnailWidth ;
- S32 mThumbnailHeight ;
- LLRect mPreviewRect ;
- BOOL mThumbnailUpdateLock ;
- BOOL mThumbnailUpToDate ;
-
- S32 mCurImageIndex;
- LLPointer<LLImageRaw> mPreviewImage;
- LLPointer<LLImageRaw> mPreviewImageEncoded;
- LLPointer<LLImageFormatted> mFormattedImage;
- LLFrameTimer mSnapshotDelayTimer;
- S32 mShineCountdown;
- LLFrameTimer mShineAnimTimer;
- F32 mFlashAlpha;
- BOOL mNeedsFlash;
- LLVector3d mPosTakenGlobal;
- S32 mSnapshotQuality;
- S32 mDataSize;
- ESnapshotType mSnapshotType;
- LLFloaterSnapshot::ESnapshotFormat mSnapshotFormat;
- BOOL mSnapshotUpToDate;
- LLFrameTimer mFallAnimTimer;
- LLVector3 mCameraPos;
- LLQuaternion mCameraRot;
- BOOL mSnapshotActive;
- LLViewerWindow::ESnapshotType mSnapshotBufferType;
-
-public:
- static std::set<LLSnapshotLivePreview*> sList;
- BOOL mKeepAspectRatio ;
-};
-
-std::set<LLSnapshotLivePreview*> LLSnapshotLivePreview::sList;
-
-LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Params& p)
-: LLView(p),
- mColor(1.f, 0.f, 0.f, 0.5f),
- mCurImageIndex(0),
- mPreviewImage(NULL),
- mThumbnailImage(NULL) ,
- mThumbnailWidth(0),
- mThumbnailHeight(0),
- mPreviewImageEncoded(NULL),
- mFormattedImage(NULL),
- mShineCountdown(0),
- mFlashAlpha(0.f),
- mNeedsFlash(TRUE),
- mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")),
- mDataSize(0),
- mSnapshotType(SNAPSHOT_POSTCARD),
- mSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))),
- mSnapshotUpToDate(FALSE),
- mCameraPos(LLViewerCamera::getInstance()->getOrigin()),
- mCameraRot(LLViewerCamera::getInstance()->getQuaternion()),
- mSnapshotActive(FALSE),
- mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR)
-{
- setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality"));
- mSnapshotDelayTimer.setTimerExpirySec(0.0f);
- mSnapshotDelayTimer.start();
-// gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
- sList.insert(this);
- setFollowsAll();
- mWidth[0] = gViewerWindow->getWindowWidthRaw();
- mWidth[1] = gViewerWindow->getWindowWidthRaw();
- mHeight[0] = gViewerWindow->getWindowHeightRaw();
- mHeight[1] = gViewerWindow->getWindowHeightRaw();
- mImageScaled[0] = FALSE;
- mImageScaled[1] = FALSE;
-
- mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
- mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;
- mThumbnailUpdateLock = FALSE ;
- mThumbnailUpToDate = FALSE ;
-}
-
-LLSnapshotLivePreview::~LLSnapshotLivePreview()
-{
- // delete images
- mPreviewImage = NULL;
- mPreviewImageEncoded = NULL;
- mFormattedImage = NULL;
-
-// gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
- sList.erase(this);
-}
-
-void LLSnapshotLivePreview::setMaxImageSize(S32 size)
-{
- if(size < MAX_SNAPSHOT_IMAGE_SIZE)
- {
- mMaxImageSize = size;
- }
- else
- {
- mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
- }
-}
-
-LLViewerTexture* LLSnapshotLivePreview::getCurrentImage()
-{
- return mViewerImage[mCurImageIndex];
-}
-
-F32 LLSnapshotLivePreview::getAspect()
-{
- F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
- F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
-
- if (!mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
- {
- return image_aspect_ratio;
- }
- else
- {
- return window_aspect_ratio;
- }
-}
-
-F32 LLSnapshotLivePreview::getImageAspect()
-{
- if (!getCurrentImage())
- {
- return 0.f;
- }
-
- return getAspect() ;
-}
-
-void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
-{
- // Invalidate current image.
- lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl;
- if (getSnapshotUpToDate())
- {
- S32 old_image_index = mCurImageIndex;
- mCurImageIndex = (mCurImageIndex + 1) % 2;
- setSize(mWidth[old_image_index], mHeight[old_image_index]);
- mFallAnimTimer.start();
- }
- mSnapshotUpToDate = FALSE;
-
- // Update snapshot source rect depending on whether we keep the aspect ratio.
- LLRect& rect = mImageRect[mCurImageIndex];
- rect.set(0, getRect().getHeight(), getRect().getWidth(), 0);
-
- F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
- F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
-
- if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
- {
- if (image_aspect_ratio > window_aspect_ratio)
- {
- // trim off top and bottom
- S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio);
- rect.mBottom += (getRect().getHeight() - new_height) / 2;
- rect.mTop -= (getRect().getHeight() - new_height) / 2;
- }
- else if (image_aspect_ratio < window_aspect_ratio)
- {
- // trim off left and right
- S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio);
- rect.mLeft += (getRect().getWidth() - new_width) / 2;
- rect.mRight -= (getRect().getWidth() - new_width) / 2;
- }
- }
-
- // Stop shining animation.
- mShineAnimTimer.stop();
-
- // Update snapshot if requested.
- if (new_snapshot)
- {
- mSnapshotDelayTimer.start();
- mSnapshotDelayTimer.setTimerExpirySec(delay);
- LLFloaterSnapshot::preUpdate();
- }
-
- // Update thumbnail if requested.
- if(new_thumbnail)
- {
- mThumbnailUpToDate = FALSE ;
- }
-}
-
-void LLSnapshotLivePreview::setSnapshotQuality(S32 quality)
-{
- llclamp(quality, 0, 100);
- if (quality != mSnapshotQuality)
- {
- mSnapshotQuality = quality;
- gSavedSettings.setS32("SnapshotQuality", quality);
- mSnapshotUpToDate = FALSE;
- }
-}
-
-void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y)
-{
- F32 line_width ;
- glGetFloatv(GL_LINE_WIDTH, &line_width) ;
- glLineWidth(2.0f * line_width) ;
- LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ;
- gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y,
- mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, FALSE ) ;
- glLineWidth(line_width) ;
-
- //draw four alpha rectangles to cover areas outside of the snapshot image
- if(!mKeepAspectRatio)
- {
- LLColor4 alpha_color(0.5f, 0.5f, 0.5f, 0.8f) ;
- S32 dwl = 0, dwr = 0 ;
- if(mThumbnailWidth > mPreviewRect.getWidth())
- {
- dwl = (mThumbnailWidth - mPreviewRect.getWidth()) >> 1 ;
- dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ;
-
- gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y,
- mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ;
- gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y,
- mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ;
- }
-
- if(mThumbnailHeight > mPreviewRect.getHeight())
- {
- S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ;
- gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y ,
- mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, TRUE ) ;
-
- dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ;
- gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh,
- mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, TRUE ) ;
- }
- }
-}
-
-//called when the frame is frozen.
-void LLSnapshotLivePreview::draw()
-{
- if (getCurrentImage() &&
- mPreviewImageEncoded.notNull() &&
- getSnapshotUpToDate())
- {
- LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f);
- gl_rect_2d(getRect(), bg_color);
- const LLRect& rect = getImageRect();
- LLRect shadow_rect = rect;
- shadow_rect.stretch(BORDER_WIDTH);
- gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10);
-
- LLColor4 image_color(1.f, 1.f, 1.f, 1.f);
- gGL.color4fv(image_color.mV);
- gGL.getTexUnit(0)->bind(getCurrentImage());
- // calculate UV scale
- F32 uv_width = isImageScaled() ? 1.f : llmin((F32)getWidth() / (F32)getCurrentImage()->getWidth(), 1.f);
- F32 uv_height = isImageScaled() ? 1.f : llmin((F32)getHeight() / (F32)getCurrentImage()->getHeight(), 1.f);
- gGL.pushMatrix();
- {
- gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom, 0.f);
- gGL.begin(LLRender::QUADS);
- {
- gGL.texCoord2f(uv_width, uv_height);
- gGL.vertex2i(rect.getWidth(), rect.getHeight() );
-
- gGL.texCoord2f(0.f, uv_height);
- gGL.vertex2i(0, rect.getHeight() );
-
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex2i(0, 0);
-
- gGL.texCoord2f(uv_width, 0.f);
- gGL.vertex2i(rect.getWidth(), 0);
- }
- gGL.end();
- }
- gGL.popMatrix();
-
- gGL.color4f(1.f, 1.f, 1.f, mFlashAlpha);
- gl_rect_2d(getRect());
- if (mNeedsFlash)
- {
- if (mFlashAlpha < 1.f)
- {
- mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f));
- }
- else
- {
- mNeedsFlash = FALSE;
- }
- }
- else
- {
- mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
- }
-
- // Draw shining animation if appropriate.
- if (mShineCountdown > 0)
- {
- mShineCountdown--;
- if (mShineCountdown == 0)
- {
- mShineAnimTimer.start();
- }
- }
- else if (mShineAnimTimer.getStarted())
- {
- lldebugs << "Drawing shining animation" << llendl;
- F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME);
-
- // draw "shine" effect
- LLLocalClipRect clip(getLocalRect());
- {
- // draw diagonal stripe with gradient that passes over screen
- S32 x1 = gViewerWindow->getWindowWidthScaled() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f)));
- S32 x2 = x1 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH);
- S32 x3 = x2 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH);
- S32 y1 = 0;
- S32 y2 = gViewerWindow->getWindowHeightScaled();
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.begin(LLRender::QUADS);
- {
- gGL.color4f(1.f, 1.f, 1.f, 0.f);
- gGL.vertex2i(x1, y1);
- gGL.vertex2i(x1 + gViewerWindow->getWindowWidthScaled(), y2);
- gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY);
- gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2);
- gGL.vertex2i(x2, y1);
-
- gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY);
- gGL.vertex2i(x2, y1);
- gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2);
- gGL.color4f(1.f, 1.f, 1.f, 0.f);
- gGL.vertex2i(x3 + gViewerWindow->getWindowWidthScaled(), y2);
- gGL.vertex2i(x3, y1);
- }
- gGL.end();
- }
-
- // if we're at the end of the animation, stop
- if (shine_interp >= 1.f)
- {
- mShineAnimTimer.stop();
- }
- }
- }
-
- // draw framing rectangle
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.color4f(1.f, 1.f, 1.f, 1.f);
- const LLRect& outline_rect = getImageRect();
- gGL.begin(LLRender::QUADS);
- {
- gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mRight, outline_rect.mTop);
- gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop);
-
- gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom);
- gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom);
- gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
-
- gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop);
- gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom);
- gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
-
- gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom);
- gGL.vertex2i(outline_rect.mRight, outline_rect.mTop);
- gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
- gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
- }
- gGL.end();
- }
-
- // draw old image dropping away
- if (mFallAnimTimer.getStarted())
- {
- S32 old_image_index = (mCurImageIndex + 1) % 2;
- if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME)
- {
- lldebugs << "Drawing fall animation" << llendl;
- F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME;
- F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f);
- LLColor4 image_color(1.f, 1.f, 1.f, alpha);
- gGL.color4fv(image_color.mV);
- gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]);
- // calculate UV scale
- // *FIX get this to work with old image
- BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull();
- F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f;
- F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f;
- gGL.pushMatrix();
- {
- LLRect& rect = mImageRect[old_image_index];
- gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - llround(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f);
- gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f);
- gGL.begin(LLRender::QUADS);
- {
- gGL.texCoord2f(uv_width, uv_height);
- gGL.vertex2i(rect.getWidth(), rect.getHeight() );
-
- gGL.texCoord2f(0.f, uv_height);
- gGL.vertex2i(0, rect.getHeight() );
-
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex2i(0, 0);
- gGL.texCoord2f(uv_width, 0.f);
- gGL.vertex2i(rect.getWidth(), 0);
- }
- gGL.end();
- }
- gGL.popMatrix();
- }
- }
-}
-
-/*virtual*/
-void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent)
-{
- LLRect old_rect = getRect();
- LLView::reshape(width, height, called_from_parent);
- if (old_rect.getWidth() != width || old_rect.getHeight() != height)
- {
- lldebugs << "window reshaped, updating thumbnail" << llendl;
- updateSnapshot(FALSE, TRUE);
- }
-}
-
-BOOL LLSnapshotLivePreview::setThumbnailImageSize()
-{
- if(getWidth() < 10 || getHeight() < 10)
- {
- return FALSE ;
- }
- S32 window_width = gViewerWindow->getWindowWidthRaw() ;
- S32 window_height = gViewerWindow->getWindowHeightRaw() ;
-
- F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height);
-
- // UI size for thumbnail
- // *FIXME: the rect does not change, so maybe there's no need to recalculate max w/h.
- const LLRect& thumbnail_rect = LLFloaterSnapshot::getThumbnailPlaceholderRect();
- S32 max_width = thumbnail_rect.getWidth();
- S32 max_height = thumbnail_rect.getHeight();
-
- if (window_aspect_ratio > (F32)max_width / max_height)
- {
- // image too wide, shrink to width
- mThumbnailWidth = max_width;
- mThumbnailHeight = llround((F32)max_width / window_aspect_ratio);
- }
- else
- {
- // image too tall, shrink to height
- mThumbnailHeight = max_height;
- mThumbnailWidth = llround((F32)max_height * window_aspect_ratio);
- }
-
- if(mThumbnailWidth > window_width || mThumbnailHeight > window_height)
- {
- return FALSE ;//if the window is too small, ignore thumbnail updating.
- }
-
- S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ;
- if(!mKeepAspectRatio)
- {
- F32 ratio_x = (F32)getWidth() / window_width ;
- F32 ratio_y = (F32)getHeight() / window_height ;
-
- //if(getWidth() > window_width ||
- // getHeight() > window_height )
- {
- if(ratio_x > ratio_y)
- {
- top = (S32)(top * ratio_y / ratio_x) ;
- }
- else
- {
- right = (S32)(right * ratio_x / ratio_y) ;
- }
- }
- //else
- //{
- // right = (S32)(right * ratio_x) ;
- // top = (S32)(top * ratio_y) ;
- //}
- left = (S32)((mThumbnailWidth - right) * 0.5f) ;
- bottom = (S32)((mThumbnailHeight - top) * 0.5f) ;
- top += bottom ;
- right += left ;
- }
- mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ;
-
- return TRUE ;
-}
-
-void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
-{
- if(mThumbnailUpdateLock) //in the process of updating
- {
- return ;
- }
- if(getThumbnailUpToDate() && !force_update)//already updated
- {
- return ;
- }
- if(getWidth() < 10 || getHeight() < 10)
- {
- return ;
- }
-
- ////lock updating
- mThumbnailUpdateLock = TRUE ;
-
- if(!setThumbnailImageSize())
- {
- mThumbnailUpdateLock = FALSE ;
- mThumbnailUpToDate = TRUE ;
- return ;
- }
-
- if(mThumbnailImage)
- {
- resetThumbnailImage() ;
- }
-
- LLPointer<LLImageRaw> raw = new LLImageRaw;
- if(!gViewerWindow->thumbnailSnapshot(raw,
- mThumbnailWidth, mThumbnailHeight,
- gSavedSettings.getBOOL("RenderUIInSnapshot"),
- FALSE,
- mSnapshotBufferType) )
- {
- raw = NULL ;
- }
-
- if(raw)
- {
- raw->expandToPowerOfTwo();
- mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
- mThumbnailUpToDate = TRUE ;
- }
-
- //unlock updating
- mThumbnailUpdateLock = FALSE ;
-}
-
-
-// Called often. Checks whether it's time to grab a new snapshot and if so, does it.
-// Returns TRUE if new snapshot generated, FALSE otherwise.
-//static
-BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
-{
- LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview;
- if (previewp->getWidth() == 0 || previewp->getHeight() == 0)
- {
- llwarns << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << llendl;
- return FALSE;
- }
-
- // If we're in freeze-frame mode and camera has moved, update snapshot.
- LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin();
- LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion();
- if (gSavedSettings.getBOOL("FreezeTime") &&
- (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))
- {
- previewp->mCameraPos = new_camera_pos;
- previewp->mCameraRot = new_camera_rot;
- // request a new snapshot whenever the camera moves, with a time delay
- BOOL autosnap = gSavedSettings.getBOOL("AutoSnapshot");
- lldebugs << "camera moved, updating thumbnail" << llendl;
- previewp->updateSnapshot(
- autosnap, // whether a new snapshot is needed or merely invalidate the existing one
- FALSE, // or if 1st arg is false, whether to produce a new thumbnail image.
- autosnap ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true.
- }
-
- // see if it's time yet to snap the shot and bomb out otherwise.
- previewp->mSnapshotActive =
- (previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired())
- && !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active
- if ( ! previewp->mSnapshotActive)
- {
- return FALSE;
- }
-
- // time to produce a snapshot
- previewp->setThumbnailImageSize();
-
- lldebugs << "producing snapshot" << llendl;
- if (!previewp->mPreviewImage)
- {
- previewp->mPreviewImage = new LLImageRaw;
- }
-
- if (!previewp->mPreviewImageEncoded)
- {
- previewp->mPreviewImageEncoded = new LLImageRaw;
- }
-
- previewp->setVisible(FALSE);
- previewp->setEnabled(FALSE);
-
- previewp->getWindow()->incBusyCount();
- previewp->setImageScaled(FALSE);
-
- // grab the raw image and encode it into desired format
- if(gViewerWindow->rawSnapshot(
- previewp->mPreviewImage,
- previewp->getWidth(),
- previewp->getHeight(),
- previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"),
- previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE,
- gSavedSettings.getBOOL("RenderUIInSnapshot"),
- FALSE,
- previewp->mSnapshotBufferType,
- previewp->getMaxImageSize()))
- {
- previewp->mPreviewImageEncoded->resize(
- previewp->mPreviewImage->getWidth(),
- previewp->mPreviewImage->getHeight(),
- previewp->mPreviewImage->getComponents());
-
- if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE)
- {
- lldebugs << "Encoding new image of format J2C" << llendl;
- LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
- LLPointer<LLImageRaw> scaled = new LLImageRaw(
- previewp->mPreviewImage->getData(),
- previewp->mPreviewImage->getWidth(),
- previewp->mPreviewImage->getHeight(),
- previewp->mPreviewImage->getComponents());
-
- scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
- previewp->setImageScaled(TRUE);
- if (formatted->encode(scaled, 0.f))
- {
- previewp->mDataSize = formatted->getDataSize();
- formatted->decode(previewp->mPreviewImageEncoded, 0);
- }
- }
- else
- {
- // delete any existing image
- previewp->mFormattedImage = NULL;
- // now create the new one of the appropriate format.
- LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat();
- lldebugs << "Encoding new image of format " << format << llendl;
-
- switch(format)
- {
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
- previewp->mFormattedImage = new LLImagePNG();
- break;
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
- previewp->mFormattedImage = new LLImageJPEG(previewp->mSnapshotQuality);
- break;
- case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
- previewp->mFormattedImage = new LLImageBMP();
- break;
- }
- if (previewp->mFormattedImage->encode(previewp->mPreviewImage, 0))
- {
- previewp->mDataSize = previewp->mFormattedImage->getDataSize();
- // special case BMP to copy instead of decode otherwise decode will crash.
- if(format == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP)
- {
- previewp->mPreviewImageEncoded->copy(previewp->mPreviewImage);
- }
- else
- {
- previewp->mFormattedImage->decode(previewp->mPreviewImageEncoded, 0);
- }
- }
- }
-
- LLPointer<LLImageRaw> scaled = new LLImageRaw(
- previewp->mPreviewImageEncoded->getData(),
- previewp->mPreviewImageEncoded->getWidth(),
- previewp->mPreviewImageEncoded->getHeight(),
- previewp->mPreviewImageEncoded->getComponents());
-
- if(!scaled->isBufferInvalid())
- {
- // leave original image dimensions, just scale up texture buffer
- if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024)
- {
- // go ahead and shrink image to appropriate power of 2 for display
- scaled->biasedScaleToPowerOfTwo(1024);
- previewp->setImageScaled(TRUE);
- }
- else
- {
- // expand image but keep original image data intact
- scaled->expandToPowerOfTwo(1024, FALSE);
- }
-
- previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE);
- LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex];
- gGL.getTexUnit(0)->bind(curr_preview_image);
- if (previewp->getSnapshotType() != SNAPSHOT_TEXTURE)
- {
- curr_preview_image->setFilteringOption(LLTexUnit::TFO_POINT);
- }
- else
- {
- curr_preview_image->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
- }
- curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP);
-
- previewp->mSnapshotUpToDate = TRUE;
- previewp->generateThumbnailImage(TRUE) ;
-
- previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal();
- previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
- }
- }
- previewp->getWindow()->decBusyCount();
- // only show fullscreen preview when in freeze frame mode
- previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame"));
- previewp->mSnapshotDelayTimer.stop();
- previewp->mSnapshotActive = FALSE;
-
- if(!previewp->getThumbnailUpToDate())
- {
- previewp->generateThumbnailImage() ;
- }
- lldebugs << "done creating snapshot" << llendl;
- LLFloaterSnapshot::postUpdate();
-
- return TRUE;
-}
-
-void LLSnapshotLivePreview::setSize(S32 w, S32 h)
-{
- lldebugs << "setSize(" << w << ", " << h << ")" << llendl;
- setWidth(w);
- setHeight(h);
-}
-
-void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
-{
- w = getWidth();
- h = getHeight();
-}
-
-void LLSnapshotLivePreview::saveTexture()
-{
- lldebugs << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << llendl;
- // gen a new uuid for this asset
- LLTransactionID tid;
- tid.generate();
- LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
-
- LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
- LLPointer<LLImageRaw> scaled = new LLImageRaw(mPreviewImage->getData(),
- mPreviewImage->getWidth(),
- mPreviewImage->getHeight(),
- mPreviewImage->getComponents());
-
- scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
- lldebugs << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << llendl;
-
- if (formatted->encode(scaled, 0.0f))
- {
- LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE);
- std::string pos_string;
- LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL);
- std::string who_took_it;
- LLAgentUI::buildFullname(who_took_it);
- LLAssetStorage::LLStoreAssetCallback callback = NULL;
- S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
- void *userdata = NULL;
- upload_new_resource(tid, // tid
- LLAssetType::AT_TEXTURE,
- "Snapshot : " + pos_string,
- "Taken by " + who_took_it + " at " + pos_string,
- 0,
- LLFolderType::FT_SNAPSHOT_CATEGORY,
- LLInventoryType::IT_SNAPSHOT,
- PERM_ALL, // Note: Snapshots to inventory is a special case of content upload
- LLFloaterPerms::getGroupPerms(), // that is more permissive than other uploads
- LLFloaterPerms::getEveryonePerms(),
- "Snapshot : " + pos_string,
- callback, expected_upload_cost, userdata);
- gViewerWindow->playSnapshotAnimAndSound();
- }
- else
- {
- LLNotificationsUtil::add("ErrorEncodingSnapshot");
- llwarns << "Error encoding snapshot" << llendl;
- }
-
- LLViewerStats::getInstance()->incStat(LLViewerStats::ST_SNAPSHOT_COUNT );
-
- mDataSize = 0;
-}
-
-BOOL LLSnapshotLivePreview::saveLocal()
-{
- BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage);
-
- if(success)
- {
- gViewerWindow->playSnapshotAnimAndSound();
- }
- return success;
-}
-
-void LLSnapshotLivePreview::saveWeb()
-{
- // *FIX: Will break if the window closes because of CloseSnapshotOnKeep!
- // Needs to pass on ownership of the image.
- LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get());
- if(!jpg)
- {
- llwarns << "Formatted image not a JPEG" << llendl;
- return;
- }
-
- LLSD metadata;
- metadata["description"] = getChild<LLLineEditor>("description")->getText();
-
- LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(gAgentCamera.getCameraPositionGlobal(),
- boost::bind(&LLSnapshotLivePreview::regionNameCallback, this, jpg, metadata, _1, _2, _3, _4));
-
- gViewerWindow->playSnapshotAnimAndSound();
-}
-
-void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z)
-{
- metadata["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString();
-
- LLWebSharing::instance().shareSnapshot(snapshot, metadata);
-}
///----------------------------------------------------------------------------
/// Class LLFloaterSnapshot::Impl
@@ -2037,7 +1063,7 @@ BOOL LLFloaterSnapshot::postBuild()
getChild<LLUICtrl>("auto_snapshot_check")->setValue(gSavedSettings.getBOOL("AutoSnapshot"));
childSetCommitCallback("auto_snapshot_check", Impl::onClickAutoSnap, this);
-
+
LLWebProfile::setImageUploadResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSnapshotUploadFinished, _1));
LLPostCard::setPostResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSendingPostcardFinished, _1));
@@ -2070,6 +1096,9 @@ BOOL LLFloaterSnapshot::postBuild()
impl.updateControls(this);
impl.updateLayout(this);
+
+ previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect());
+
return TRUE;
}
@@ -2235,7 +1264,9 @@ S32 LLFloaterSnapshot::notify(const LLSD& info)
void LLFloaterSnapshot::update()
{
LLFloaterSnapshot* inst = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
- if (!inst)
+ LLFloaterSocial* floater_social = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+
+ if (!inst && !floater_social)
return;
BOOL changed = FALSE;
@@ -2245,7 +1276,8 @@ void LLFloaterSnapshot::update()
{
changed |= LLSnapshotLivePreview::onIdle(*iter);
}
- if(changed)
+
+ if (inst && changed)
{
lldebugs << "changed" << llendl;
inst->impl.updateControls(inst);
diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h
index afe135fa40..82af8c7a9d 100755
--- a/indra/newview/llfloatersnapshot.h
+++ b/indra/newview/llfloatersnapshot.h
@@ -27,7 +27,6 @@
#ifndef LL_LLFLOATERSNAPSHOT_H
#define LL_LLFLOATERSNAPSHOT_H
-#include "llimage.h"
#include "llfloater.h"
class LLSpinCtrl;
diff --git a/indra/newview/llfloatersocial.cpp b/indra/newview/llfloatersocial.cpp
new file mode 100644
index 0000000000..2a74c8e3ea
--- /dev/null
+++ b/indra/newview/llfloatersocial.cpp
@@ -0,0 +1,920 @@
+/**
+* @file llfloatersocial.cpp
+* @brief Implementation of llfloatersocial
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* 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 "llviewerprecompiledheaders.h"
+
+#include "llfloatersocial.h"
+
+#include "llagent.h"
+#include "llagentui.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfacebookconnect.h"
+#include "llfloaterreg.h"
+#include "lliconctrl.h"
+#include "llresmgr.h" // LLLocale
+#include "llsdserialize.h"
+#include "llloadingindicator.h"
+#include "llplugincookiestore.h"
+#include "llslurl.h"
+#include "lltrans.h"
+#include "llsnapshotlivepreview.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+#include "llviewermedia.h"
+
+static LLRegisterPanelClassWrapper<LLSocialStatusPanel> t_panel_status("llsocialstatuspanel");
+static LLRegisterPanelClassWrapper<LLSocialPhotoPanel> t_panel_photo("llsocialphotopanel");
+static LLRegisterPanelClassWrapper<LLSocialCheckinPanel> t_panel_checkin("llsocialcheckinpanel");
+static LLRegisterPanelClassWrapper<LLSocialAccountPanel> t_panel_account("llsocialaccountpanel");
+
+const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
+const std::string DEFAULT_CHECKIN_LOCATION_URL = "http://maps.secondlife.com/";
+const std::string DEFAULT_CHECKIN_ICON_URL = "http://map.secondlife.com.s3.amazonaws.com/map_placeholder.png";
+const std::string DEFAULT_CHECKIN_QUERY_PARAMETERS = "?sourceid=slshare_checkin&utm_source=facebook&utm_medium=checkin&utm_campaign=slshare";
+const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=facebook&utm_medium=photo&utm_campaign=slshare";
+
+std::string get_map_url()
+{
+ LLVector3d center_agent;
+ if (gAgent.getRegion())
+ {
+ center_agent = gAgent.getRegion()->getCenterGlobal();
+ }
+ int x_pos = center_agent[0] / 256.0;
+ int y_pos = center_agent[1] / 256.0;
+ std::string map_url = gSavedSettings.getString("CurrentMapServerURL") + llformat("map-1-%d-%d-objects.jpg", x_pos, y_pos);
+ return map_url;
+}
+
+///////////////////////////
+//LLSocialStatusPanel//////
+///////////////////////////
+
+LLSocialStatusPanel::LLSocialStatusPanel() :
+ mMessageTextEditor(NULL),
+ mPostButton(NULL),
+ mCancelButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendStatus", boost::bind(&LLSocialStatusPanel::onSend, this));
+}
+
+BOOL LLSocialStatusPanel::postBuild()
+{
+ mMessageTextEditor = getChild<LLUICtrl>("status_message");
+ mPostButton = getChild<LLUICtrl>("post_status_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_status_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLSocialStatusPanel::draw()
+{
+ if (mMessageTextEditor && mPostButton && mCancelButton)
+ {
+ bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing());
+ std::string message = mMessageTextEditor->getValue().asString();
+ mMessageTextEditor->setEnabled(no_ongoing_connection);
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mPostButton->setEnabled(no_ongoing_connection && !message.empty());
+ }
+
+ LLPanel::draw();
+}
+
+void LLSocialStatusPanel::onSend()
+{
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialStatusPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialStatusPanel", boost::bind(&LLSocialStatusPanel::onFacebookConnectStateChange, this, _1));
+
+ // Connect to Facebook if necessary and then post
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ sendStatus();
+ }
+ else
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook(true);
+ }
+}
+
+bool LLSocialStatusPanel::onFacebookConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLFacebookConnect::FB_CONNECTED:
+ sendStatus();
+ break;
+
+ case LLFacebookConnect::FB_POSTED:
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialStatusPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLSocialStatusPanel::sendStatus()
+{
+ std::string message = mMessageTextEditor->getValue().asString();
+ if (!message.empty())
+ {
+ LLFacebookConnect::instance().updateStatus(message);
+ }
+}
+
+void LLSocialStatusPanel::clearAndClose()
+{
+ mMessageTextEditor->setValue("");
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+///////////////////////////
+//LLSocialPhotoPanel///////
+///////////////////////////
+
+LLSocialPhotoPanel::LLSocialPhotoPanel() :
+mSnapshotPanel(NULL),
+mResolutionComboBox(NULL),
+mRefreshBtn(NULL),
+mWorkingLabel(NULL),
+mThumbnailPlaceholder(NULL),
+mCaptionTextBox(NULL),
+mLocationCheckbox(NULL),
+mPostButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLSocialPhotoPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLSocialPhotoPanel::onClickNewSnapshot, this));
+}
+
+LLSocialPhotoPanel::~LLSocialPhotoPanel()
+{
+ if(mPreviewHandle.get())
+ {
+ mPreviewHandle.get()->die();
+ }
+}
+
+BOOL LLSocialPhotoPanel::postBuild()
+{
+ setVisibleCallback(boost::bind(&LLSocialPhotoPanel::onVisibilityChange, this, _2));
+
+ mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
+ mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
+ mResolutionComboBox->setCommitCallback(boost::bind(&LLSocialPhotoPanel::updateResolution, this, TRUE));
+ mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
+ mWorkingLabel = getChild<LLUICtrl>("working_lbl");
+ mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
+ mCaptionTextBox = getChild<LLUICtrl>("photo_caption");
+ mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
+ mPostButton = getChild<LLUICtrl>("post_photo_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLSocialPhotoPanel::draw()
+{
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+
+ // Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
+ bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing());
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mCaptionTextBox->setEnabled(no_ongoing_connection);
+ mResolutionComboBox->setEnabled(no_ongoing_connection);
+ mRefreshBtn->setEnabled(no_ongoing_connection);
+ mLocationCheckbox->setEnabled(no_ongoing_connection);
+
+ // Display the preview if one is available
+ if (previewp && previewp->getThumbnailImage())
+ {
+ const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
+ const S32 thumbnail_w = previewp->getThumbnailWidth();
+ const S32 thumbnail_h = previewp->getThumbnailHeight();
+
+ // calc preview offset within the preview rect
+ const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2 ;
+ const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2 ;
+
+ // calc preview offset within the floater rect
+ // Hack : To get the full offset, we need to take into account each and every offset of each widgets up to the floater.
+ // This is almost as arbitrary as using a fixed offset so that's what we do here for the sake of simplicity.
+ // *TODO : Get the offset looking through the hierarchy of widgets, should be done in postBuild() so to avoid traversing the hierarchy each time.
+ S32 offset_x = thumbnail_rect.mLeft + local_offset_x - 1;
+ S32 offset_y = thumbnail_rect.mBottom + local_offset_y - 39;
+
+ mSnapshotPanel->localPointToOtherView(offset_x, offset_y, &offset_x, &offset_y, getParentByType<LLFloater>());
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ // Apply floater transparency to the texture unless the floater is focused.
+ F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
+ LLColor4 color = LLColor4::white;
+ gl_draw_scaled_image(offset_x, offset_y,
+ thumbnail_w, thumbnail_h,
+ previewp->getThumbnailImage(), color % alpha);
+
+ previewp->drawPreviewRect(offset_x, offset_y) ;
+ }
+
+ // Update the visibility of the working (computing preview) label
+ mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate()));
+
+ // Enable Post if we have a preview to send and no on going connection being processed
+ mPostButton->setEnabled(no_ongoing_connection && (previewp && previewp->getSnapshotUpToDate()));
+
+ // Draw the rest of the panel on top of it
+ LLPanel::draw();
+}
+
+LLSnapshotLivePreview* LLSocialPhotoPanel::getPreviewView()
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
+ return previewp;
+}
+
+void LLSocialPhotoPanel::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+ if (visible)
+ {
+ if (mPreviewHandle.get())
+ {
+ LLSnapshotLivePreview* preview = getPreviewView();
+ if(preview)
+ {
+ lldebugs << "opened, updating snapshot" << llendl;
+ preview->updateSnapshot(TRUE);
+ }
+ }
+ else
+ {
+ LLRect full_screen_rect = getRootView()->getRect();
+ LLSnapshotLivePreview::Params p;
+ p.rect(full_screen_rect);
+ LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
+ mPreviewHandle = previewp->getHandle();
+
+ previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
+ previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
+ //previewp->setSnapshotQuality(98);
+ previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
+
+ updateControls();
+ }
+ }
+}
+
+void LLSocialPhotoPanel::onClickNewSnapshot()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (previewp)
+ {
+ //setStatus(Impl::STATUS_READY);
+ lldebugs << "updating snapshot" << llendl;
+ previewp->updateSnapshot(TRUE);
+ }
+}
+
+void LLSocialPhotoPanel::onSend()
+{
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialPhotoPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialPhotoPanel", boost::bind(&LLSocialPhotoPanel::onFacebookConnectStateChange, this, _1));
+
+ // Connect to Facebook if necessary and then post
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ sendPhoto();
+ }
+ else
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook(true);
+ }
+}
+
+bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLFacebookConnect::FB_CONNECTED:
+ sendPhoto();
+ break;
+
+ case LLFacebookConnect::FB_POSTED:
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialPhotoPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLSocialPhotoPanel::sendPhoto()
+{
+ // Get the caption
+ std::string caption = mCaptionTextBox->getValue().asString();
+
+ // Add the location if required
+ bool add_location = mLocationCheckbox->getValue().asBoolean();
+ if (add_location)
+ {
+ // Get the SLURL for the location
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl);
+ std::string slurl_string = slurl.getSLURLString();
+
+ // Add query parameters so Google Analytics can track incoming clicks!
+ slurl_string += DEFAULT_PHOTO_QUERY_PARAMETERS;
+
+ // Add it to the caption (pretty crude, but we don't have a better option with photos)
+ if (caption.empty())
+ caption = slurl_string;
+ else
+ caption = caption + " " + slurl_string;
+ }
+
+ // Get the image
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ // Post to Facebook
+ LLFacebookConnect::instance().sharePhoto(previewp->getFormattedImage(), caption);
+
+ updateControls();
+}
+
+void LLSocialPhotoPanel::clearAndClose()
+{
+ mCaptionTextBox->setValue("");
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+void LLSocialPhotoPanel::updateControls()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ BOOL got_bytes = previewp && previewp->getDataSize() > 0;
+ BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
+ LLSnapshotLivePreview::ESnapshotType shot_type = (previewp ? previewp->getSnapshotType() : LLSnapshotLivePreview::SNAPSHOT_POSTCARD);
+
+ // *TODO: Separate maximum size for Web images from postcards
+ lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ std::string bytes_string;
+ if (got_snap)
+ {
+ LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10 );
+ }
+
+ //getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : getString("unknown")); <---uses localized string
+ getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : "unknown");
+ getChild<LLUICtrl>("file_size_label")->setColor(
+ shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD
+ && got_bytes
+ && previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLUIColor(LLColor4::red) : LLUIColorTable::instance().getColor( "LabelTextColor" ));
+
+ updateResolution(FALSE);
+}
+
+void LLSocialPhotoPanel::updateResolution(BOOL do_update)
+{
+ LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
+
+ std::string sdstring = combobox->getSelectedValue();
+ LLSD sdres;
+ std::stringstream sstream(sdstring);
+ LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
+
+ S32 width = sdres[0];
+ S32 height = sdres[1];
+
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+ if (previewp && combobox->getCurrentIndex() >= 0)
+ {
+ S32 original_width = 0 , original_height = 0 ;
+ previewp->getSize(original_width, original_height) ;
+
+ if (width == 0 || height == 0)
+ {
+ // take resolution from current window size
+ lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl;
+ previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
+ }
+ else
+ {
+ // use the resolution from the selected pre-canned drop-down choice
+ lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl;
+ previewp->setSize(width, height);
+ }
+
+ checkAspectRatio(width);
+
+ previewp->getSize(width, height);
+
+ if(original_width != width || original_height != height)
+ {
+ previewp->setSize(width, height);
+
+ // hide old preview as the aspect ratio could be wrong
+ lldebugs << "updating thumbnail" << llendl;
+
+ previewp->updateSnapshot(FALSE, TRUE);
+ if(do_update)
+ {
+ lldebugs << "Will update controls" << llendl;
+ updateControls();
+ LLSocialPhotoPanel::onClickNewSnapshot();
+ }
+ }
+
+ }
+}
+
+void LLSocialPhotoPanel::checkAspectRatio(S32 index)
+{
+ LLSnapshotLivePreview *previewp = getPreviewView() ;
+
+ BOOL keep_aspect = FALSE;
+
+ if (0 == index) // current window size
+ {
+ keep_aspect = TRUE;
+ }
+ else // predefined resolution
+ {
+ keep_aspect = FALSE;
+ }
+
+ if (previewp)
+ {
+ previewp->mKeepAspectRatio = keep_aspect;
+ }
+}
+
+LLUICtrl* LLSocialPhotoPanel::getRefreshBtn()
+{
+ return mRefreshBtn;
+}
+
+////////////////////////
+//LLSocialCheckinPanel//
+////////////////////////
+
+LLSocialCheckinPanel::LLSocialCheckinPanel() :
+ mMapUrl(""),
+ mReloadingMapTexture(false)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendCheckin", boost::bind(&LLSocialCheckinPanel::onSend, this));
+}
+
+BOOL LLSocialCheckinPanel::postBuild()
+{
+ // Keep pointers to widgets so we don't traverse the UI hierarchy too often
+ mPostButton = getChild<LLUICtrl>("post_place_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_place_btn");
+ mMessageTextEditor = getChild<LLUICtrl>("place_caption");
+ mMapLoadingIndicator = getChild<LLUICtrl>("map_loading_indicator");
+ mMapPlaceholder = getChild<LLIconCtrl>("map_placeholder");
+ mMapDefault = getChild<LLIconCtrl>("map_default");
+ mMapCheckBox = getChild<LLCheckBoxCtrl>("add_place_view_cb");
+
+ return LLPanel::postBuild();
+}
+
+void LLSocialCheckinPanel::draw()
+{
+ bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing());
+ mPostButton->setEnabled(no_ongoing_connection);
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mMessageTextEditor->setEnabled(no_ongoing_connection);
+ mMapCheckBox->setEnabled(no_ongoing_connection);
+
+ std::string map_url = get_map_url();
+ // Did we change location?
+ if (map_url != mMapUrl)
+ {
+ mMapUrl = map_url;
+ // Load the map tile
+ mMapTexture = LLViewerTextureManager::getFetchedTextureFromUrl(mMapUrl, FTT_MAP_TILE, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ mMapTexture->setBoostLevel(LLGLTexture::BOOST_MAP);
+ mReloadingMapTexture = true;
+ // In the meantime, put the "loading" indicator on, hide the tile map and disable the checkbox
+ mMapLoadingIndicator->setVisible(true);
+ mMapPlaceholder->setVisible(false);
+ }
+ // Are we done loading the map tile?
+ if (mReloadingMapTexture && mMapTexture->isFullyLoaded())
+ {
+ // Don't do it again next time around
+ mReloadingMapTexture = false;
+ // Convert the map texture to the appropriate image object
+ LLPointer<LLUIImage> ui_image = new LLUIImage(mMapUrl, mMapTexture);
+ // Load the map widget with the correct map tile image
+ mMapPlaceholder->setImage(ui_image);
+ // Now hide the loading indicator, bring the tile in view and reenable the checkbox with its previous value
+ mMapLoadingIndicator->setVisible(false);
+ mMapPlaceholder->setVisible(true);
+ }
+ // Show the default icon if that's the checkbox value (the real one...)
+ // This will hide/show the loading indicator and/or tile underneath
+ mMapDefault->setVisible(!(mMapCheckBox->get()));
+
+ LLPanel::draw();
+}
+
+void LLSocialCheckinPanel::onSend()
+{
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialCheckinPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialCheckinPanel", boost::bind(&LLSocialCheckinPanel::onFacebookConnectStateChange, this, _1));
+
+ // Connect to Facebook if necessary and then post
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ sendCheckin();
+ }
+ else
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook(true);
+ }
+}
+
+bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLFacebookConnect::FB_CONNECTED:
+ sendCheckin();
+ break;
+
+ case LLFacebookConnect::FB_POSTED:
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialCheckinPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLSocialCheckinPanel::sendCheckin()
+{
+ // Get the location SLURL
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl);
+ std::string slurl_string = slurl.getSLURLString();
+
+ // Use a valid http:// URL if the scheme is secondlife://
+ LLURI slurl_uri(slurl_string);
+ if (slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME)
+ {
+ slurl_string = DEFAULT_CHECKIN_LOCATION_URL;
+ }
+
+ // Add query parameters so Google Analytics can track incoming clicks!
+ slurl_string += DEFAULT_CHECKIN_QUERY_PARAMETERS;
+
+ // Get the region name
+ std::string region_name = gAgent.getRegion()->getName();
+
+ // Get the region description
+ std::string description;
+ LLAgentUI::buildLocationString(description, LLAgentUI::LOCATION_FORMAT_NORMAL_COORDS, gAgent.getPositionAgent());
+
+ // Optionally add the region map view
+ bool add_map_view = mMapCheckBox->getValue().asBoolean();
+ std::string map_url = (add_map_view ? get_map_url() : DEFAULT_CHECKIN_ICON_URL);
+
+ // Get the caption
+ std::string caption = mMessageTextEditor->getValue().asString();
+
+ // Post to Facebook
+ LLFacebookConnect::instance().postCheckin(slurl_string, region_name, description, map_url, caption);
+}
+
+void LLSocialCheckinPanel::clearAndClose()
+{
+ mMessageTextEditor->setValue("");
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+///////////////////////////
+//LLSocialAccountPanel//////
+///////////////////////////
+
+LLSocialAccountPanel::LLSocialAccountPanel() :
+mAccountCaptionLabel(NULL),
+mAccountNameLabel(NULL),
+mPanelButtons(NULL),
+mConnectButton(NULL),
+mDisconnectButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLSocialAccountPanel::onConnect, this));
+ mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLSocialAccountPanel::onDisconnect, this));
+
+ setVisibleCallback(boost::bind(&LLSocialAccountPanel::onVisibilityChange, this, _2));
+}
+
+BOOL LLSocialAccountPanel::postBuild()
+{
+ mAccountCaptionLabel = getChild<LLTextBox>("account_caption_label");
+ mAccountNameLabel = getChild<LLTextBox>("account_name_label");
+ mPanelButtons = getChild<LLUICtrl>("panel_buttons");
+ mConnectButton = getChild<LLUICtrl>("connect_btn");
+ mDisconnectButton = getChild<LLUICtrl>("disconnect_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLSocialAccountPanel::draw()
+{
+ LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState();
+
+ //Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress
+ bool disconnecting = connection_state == LLFacebookConnect::FB_DISCONNECTING;
+ mDisconnectButton->setEnabled(!disconnecting);
+
+ //Disable the 'connect' button when a connection is in progress
+ bool connecting = connection_state == LLFacebookConnect::FB_CONNECTION_IN_PROGRESS;
+ mConnectButton->setEnabled(!connecting);
+
+ LLPanel::draw();
+}
+
+void LLSocialAccountPanel::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+
+ if(visible)
+ {
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialAccountPanel", boost::bind(&LLSocialAccountPanel::onFacebookConnectStateChange, this, _1));
+
+ LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLSocialAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectInfo").listen("LLSocialAccountPanel", boost::bind(&LLSocialAccountPanel::onFacebookConnectInfoChange, this));
+
+ //Connected
+ if(LLFacebookConnect::instance().isConnected())
+ {
+ showConnectedLayout();
+ }
+ //Check if connected (show disconnected layout in meantime)
+ else
+ {
+ showDisconnectedLayout();
+ }
+ if ((LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_NOT_CONNECTED) ||
+ (LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_CONNECTION_FAILED))
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook();
+ }
+ }
+ else
+ {
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLSocialAccountPanel");
+ }
+}
+
+bool LLSocialAccountPanel::onFacebookConnectStateChange(const LLSD& data)
+{
+ if(LLFacebookConnect::instance().isConnected())
+ {
+ //In process of disconnecting so leave the layout as is
+ if(data.get("enum").asInteger() != LLFacebookConnect::FB_DISCONNECTING)
+ {
+ showConnectedLayout();
+ }
+ }
+ else
+ {
+ showDisconnectedLayout();
+ }
+
+ return false;
+}
+
+bool LLSocialAccountPanel::onFacebookConnectInfoChange()
+{
+ LLSD info = LLFacebookConnect::instance().getInfo();
+ std::string clickable_name;
+
+ //Strings of format [http://www.somewebsite.com Click Me] become clickable text
+ if(info.has("link") && info.has("name"))
+ {
+ clickable_name = "[" + info["link"].asString() + " " + info["name"].asString() + "]";
+ }
+
+ mAccountNameLabel->setText(clickable_name);
+
+ return false;
+}
+
+void LLSocialAccountPanel::showConnectButton()
+{
+ if(!mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(TRUE);
+ mDisconnectButton->setVisible(FALSE);
+ }
+}
+
+void LLSocialAccountPanel::hideConnectButton()
+{
+ if(mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(FALSE);
+ mDisconnectButton->setVisible(TRUE);
+ }
+}
+
+void LLSocialAccountPanel::showDisconnectedLayout()
+{
+ mAccountCaptionLabel->setText(getString("facebook_disconnected"));
+ mAccountNameLabel->setText(std::string(""));
+ showConnectButton();
+}
+
+void LLSocialAccountPanel::showConnectedLayout()
+{
+ LLFacebookConnect::instance().loadFacebookInfo();
+
+ mAccountCaptionLabel->setText(getString("facebook_connected"));
+ hideConnectButton();
+}
+
+void LLSocialAccountPanel::onConnect()
+{
+ LLFacebookConnect::instance().checkConnectionToFacebook(true);
+
+ //Clear only the facebook browser cookies so that the facebook login screen appears
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com");
+}
+
+void LLSocialAccountPanel::onDisconnect()
+{
+ LLFacebookConnect::instance().disconnectFromFacebook();
+
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com");
+}
+
+////////////////////////
+//LLFloaterSocial///////
+////////////////////////
+
+LLFloaterSocial::LLFloaterSocial(const LLSD& key) : LLFloater(key),
+ mSocialPhotoPanel(NULL),
+ mStatusErrorText(NULL),
+ mStatusLoadingText(NULL),
+ mStatusLoadingIndicator(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterSocial::onCancel, this));
+}
+
+void LLFloaterSocial::onCancel()
+{
+ closeFloater();
+}
+
+BOOL LLFloaterSocial::postBuild()
+{
+ // Keep tab of the Photo Panel
+ mSocialPhotoPanel = static_cast<LLSocialPhotoPanel*>(getChild<LLUICtrl>("panel_social_photo"));
+ // Connection status widgets
+ mStatusErrorText = getChild<LLTextBox>("connection_error_text");
+ mStatusLoadingText = getChild<LLTextBox>("connection_loading_text");
+ mStatusLoadingIndicator = getChild<LLUICtrl>("connection_loading_indicator");
+ return LLFloater::postBuild();
+}
+
+// static
+void LLFloaterSocial::preUpdate()
+{
+ LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+ if (instance)
+ {
+ //Will set file size text to 'unknown'
+ instance->mSocialPhotoPanel->updateControls();
+ }
+}
+
+// static
+void LLFloaterSocial::postUpdate()
+{
+ LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+ if (instance)
+ {
+ //Will set the file size text
+ instance->mSocialPhotoPanel->updateControls();
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. after snapshot is taken
+ LLUICtrl * refresh_button = instance->mSocialPhotoPanel->getRefreshBtn();
+
+ if (!refresh_button->getVisible())
+ {
+ refresh_button->setVisible(true);
+ }
+
+ }
+}
+
+void LLFloaterSocial::draw()
+{
+ if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
+ {
+ mStatusErrorText->setVisible(false);
+ mStatusLoadingText->setVisible(false);
+ mStatusLoadingIndicator->setVisible(false);
+ LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState();
+ std::string status_text;
+
+ switch (connection_state)
+ {
+ case LLFacebookConnect::FB_NOT_CONNECTED:
+ // No status displayed when first opening the panel and no connection done
+ case LLFacebookConnect::FB_CONNECTED:
+ // When successfully connected, no message is displayed
+ case LLFacebookConnect::FB_POSTED:
+ // No success message to show since we actually close the floater after successful posting completion
+ break;
+ case LLFacebookConnect::FB_CONNECTION_IN_PROGRESS:
+ // Connection loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookConnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFacebookConnect::FB_POSTING:
+ // Posting indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookPosting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFacebookConnect::FB_CONNECTION_FAILED:
+ // Error connecting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookErrorConnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLFacebookConnect::FB_POST_FAILED:
+ // Error posting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookErrorPosting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLFacebookConnect::FB_DISCONNECTING:
+ // Disconnecting loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookDisconnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFacebookConnect::FB_DISCONNECT_FAILED:
+ // Error disconnecting from the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFacebookErrorDisconnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ }
+ }
+ LLFloater::draw();
+}
+
diff --git a/indra/newview/llfloatersocial.h b/indra/newview/llfloatersocial.h
new file mode 100644
index 0000000000..bbe07c9704
--- /dev/null
+++ b/indra/newview/llfloatersocial.h
@@ -0,0 +1,165 @@
+/**
+* @file llfloatersocial.h
+* @brief Header file for llfloatersocial
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* 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_LLFLOATERSOCIAL_H
+#define LL_LLFLOATERSOCIAL_H
+
+#include "llfloater.h"
+#include "lltextbox.h"
+#include "llviewertexture.h"
+
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLSnapshotLivePreview;
+
+class LLSocialStatusPanel : public LLPanel
+{
+public:
+ LLSocialStatusPanel();
+ BOOL postBuild();
+ void draw();
+ void onSend();
+ bool onFacebookConnectStateChange(const LLSD& data);
+
+ void sendStatus();
+ void clearAndClose();
+
+private:
+ LLUICtrl* mMessageTextEditor;
+ LLUICtrl* mPostButton;
+ LLUICtrl* mCancelButton;
+};
+
+class LLSocialPhotoPanel : public LLPanel
+{
+public:
+ LLSocialPhotoPanel();
+ ~LLSocialPhotoPanel();
+
+ BOOL postBuild();
+ void draw();
+
+ LLSnapshotLivePreview* getPreviewView();
+ void onVisibilityChange(const LLSD& new_visibility);
+ void onClickNewSnapshot();
+ void onSend();
+ bool onFacebookConnectStateChange(const LLSD& data);
+
+ void sendPhoto();
+ void clearAndClose();
+
+ void updateControls();
+ void updateResolution(BOOL do_update);
+ void checkAspectRatio(S32 index);
+ LLUICtrl* getRefreshBtn();
+
+private:
+ LLHandle<LLView> mPreviewHandle;
+
+ LLUICtrl * mSnapshotPanel;
+ LLUICtrl * mResolutionComboBox;
+ LLUICtrl * mRefreshBtn;
+ LLUICtrl * mWorkingLabel;
+ LLUICtrl * mThumbnailPlaceholder;
+ LLUICtrl * mCaptionTextBox;
+ LLUICtrl * mLocationCheckbox;
+ LLUICtrl * mPostButton;
+ LLUICtrl* mCancelButton;
+};
+
+class LLSocialCheckinPanel : public LLPanel
+{
+public:
+ LLSocialCheckinPanel();
+ BOOL postBuild();
+ void draw();
+ void onSend();
+ bool onFacebookConnectStateChange(const LLSD& data);
+
+ void sendCheckin();
+ void clearAndClose();
+
+private:
+ std::string mMapUrl;
+ LLPointer<LLViewerFetchedTexture> mMapTexture;
+ LLUICtrl* mPostButton;
+ LLUICtrl* mCancelButton;
+ LLUICtrl* mMessageTextEditor;
+ LLUICtrl* mMapLoadingIndicator;
+ LLIconCtrl* mMapPlaceholder;
+ LLIconCtrl* mMapDefault;
+ LLCheckBoxCtrl* mMapCheckBox;
+ bool mReloadingMapTexture;
+};
+
+class LLSocialAccountPanel : public LLPanel
+{
+public:
+ LLSocialAccountPanel();
+ BOOL postBuild();
+ void draw();
+
+private:
+ void onVisibilityChange(const LLSD& new_visibility);
+ bool onFacebookConnectStateChange(const LLSD& data);
+ bool onFacebookConnectInfoChange();
+ void onConnect();
+ void onUseAnotherAccount();
+ void onDisconnect();
+
+ void showConnectButton();
+ void hideConnectButton();
+ void showDisconnectedLayout();
+ void showConnectedLayout();
+
+ LLTextBox * mAccountCaptionLabel;
+ LLTextBox * mAccountNameLabel;
+ LLUICtrl * mPanelButtons;
+ LLUICtrl * mConnectButton;
+ LLUICtrl * mDisconnectButton;
+};
+
+
+class LLFloaterSocial : public LLFloater
+{
+public:
+ LLFloaterSocial(const LLSD& key);
+ BOOL postBuild();
+ void draw();
+ void onCancel();
+
+ static void preUpdate();
+ static void postUpdate();
+
+private:
+ LLSocialPhotoPanel* mSocialPhotoPanel;
+ LLTextBox* mStatusErrorText;
+ LLTextBox* mStatusLoadingText;
+ LLUICtrl* mStatusLoadingIndicator;
+};
+
+#endif // LL_LLFLOATERSOCIAL_H
+
diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp
index 3fe2518de6..9d703d2752 100755
--- a/indra/newview/llfloaterwebcontent.cpp
+++ b/indra/newview/llfloaterwebcontent.cpp
@@ -29,6 +29,7 @@
#include "llcombobox.h"
#include "lliconctrl.h"
#include "llfloaterreg.h"
+#include "llfacebookconnect.h"
#include "lllayoutstack.h"
#include "llpluginclassmedia.h"
#include "llprogressbar.h"
@@ -46,7 +47,8 @@ LLFloaterWebContent::_Params::_Params()
id("id"),
window_class("window_class", "web_content"),
show_chrome("show_chrome", true),
- allow_address_entry("allow_address_entry", true),
+ allow_address_entry("allow_address_entry", true),
+ allow_back_forward_navigation("allow_back_forward_navigation", true),
preferred_media_size("preferred_media_size"),
trusted_content("trusted_content", false),
show_page_title("show_page_title", true)
@@ -65,7 +67,11 @@ LLFloaterWebContent::LLFloaterWebContent( const Params& params )
mBtnReload(NULL),
mBtnStop(NULL),
mUUID(params.id()),
- mShowPageTitle(params.show_page_title)
+ mShowPageTitle(params.show_page_title),
+ mAllowNavigation(true),
+ mCurrentURL(""),
+ mDisplayURL(""),
+ mSecureURL(false)
{
mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this ));
mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this ));
@@ -97,7 +103,7 @@ BOOL LLFloaterWebContent::postBuild()
// cache image for secure browsing
mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag");
-
+
// initialize the URL history using the system URL History manager
initializeURLHistory();
@@ -243,6 +249,7 @@ void LLFloaterWebContent::open_media(const Params& p)
getChild<LLLayoutPanel>("status_bar")->setVisible(p.show_chrome);
getChild<LLLayoutPanel>("nav_controls")->setVisible(p.show_chrome);
bool address_entry_enabled = p.allow_address_entry && !p.trusted_content;
+ mAllowNavigation = p.allow_back_forward_navigation;
getChildView("address")->setEnabled(address_entry_enabled);
getChildView("popexternal")->setEnabled(address_entry_enabled);
@@ -287,6 +294,16 @@ void LLFloaterWebContent::onOpen(const LLSD& key)
//virtual
void LLFloaterWebContent::onClose(bool app_quitting)
{
+ // If we close the web browsing window showing the facebook login, we need to signal to this object that the connection will not happen
+ LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web");
+ if (fbc_web == this)
+ {
+ if (!LLFacebookConnect::instance().isConnected())
+ {
+ LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
+ }
+ }
+
LLViewerMedia::proxyWindowClosed(mUUID);
destroy();
}
@@ -295,8 +312,11 @@ void LLFloaterWebContent::onClose(bool app_quitting)
void LLFloaterWebContent::draw()
{
// this is asynchronous so we need to keep checking
- mBtnBack->setEnabled( mWebBrowser->canNavigateBack() );
- mBtnForward->setEnabled( mWebBrowser->canNavigateForward() );
+ mBtnBack->setEnabled( mWebBrowser->canNavigateBack() && mAllowNavigation);
+ mBtnForward->setEnabled( mWebBrowser->canNavigateForward() && mAllowNavigation);
+
+ // Show/hide the lock icon
+ mSecureLockIcon->setVisible(mSecureURL && !mAddressCombo->hasFocus());
LLFloater::draw();
}
@@ -342,19 +362,6 @@ void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
// we populate the status bar with URLs as they change so clear it now we're done
const std::string end_str = "";
mStatusBarText->setText( end_str );
-
- // decide if secure browsing icon should be displayed
- std::string prefix = std::string("https://");
- std::string test_prefix = mCurrentURL.substr(0, prefix.length());
- LLStringUtil::toLower(test_prefix);
- if(test_prefix == prefix)
- {
- mSecureLockIcon->setVisible(true);
- }
- else
- {
- mSecureLockIcon->setVisible(false);
- }
}
else if(event == MEDIA_EVENT_CLOSE_REQUEST)
{
@@ -397,15 +404,40 @@ void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
void LLFloaterWebContent::set_current_url(const std::string& url)
{
- mCurrentURL = url;
-
- // serialize url history into the system URL History manager
- LLURLHistory::removeURL("browser", mCurrentURL);
- LLURLHistory::addURL("browser", mCurrentURL);
-
- mAddressCombo->remove( mCurrentURL );
- mAddressCombo->add( mCurrentURL );
- mAddressCombo->selectByValue( mCurrentURL );
+ if (!url.empty())
+ {
+ if (!mCurrentURL.empty())
+ {
+ // Clean up the current browsing list to show true URL
+ mAddressCombo->remove(mDisplayURL);
+ mAddressCombo->add(mCurrentURL);
+ }
+
+ // Update current URL
+ mCurrentURL = url;
+ LLStringUtil::trim(mCurrentURL);
+
+ // Serialize url history into the system URL History manager
+ LLURLHistory::removeURL("browser", mCurrentURL);
+ LLURLHistory::addURL("browser", mCurrentURL);
+
+ // Check if this is a secure URL
+ static const std::string secure_prefix = std::string("https://");
+ std::string prefix = mCurrentURL.substr(0, secure_prefix.length());
+ LLStringUtil::toLower(prefix);
+ mSecureURL = (prefix == secure_prefix);
+
+ // Hack : we move the text a bit to make space for the lock icon in the secure URL case
+ mDisplayURL = (mSecureURL ? " " + mCurrentURL : mCurrentURL);
+
+ // Clean up browsing list (prevent dupes) and add/select the new URL to it
+ mAddressCombo->remove(mCurrentURL);
+ mAddressCombo->add(mDisplayURL);
+ mAddressCombo->selectByValue(mDisplayURL);
+
+ // Set the focus back to the web page. When setting the url, there's no point to leave the focus anywhere else.
+ mWebBrowser->setFocus(TRUE);
+ }
}
void LLFloaterWebContent::onClickForward()
@@ -449,6 +481,7 @@ void LLFloaterWebContent::onEnterAddress()
// make sure there is at least something there.
// (perhaps this test should be for minimum length of a URL)
std::string url = mAddressCombo->getValue().asString();
+ LLStringUtil::trim(url);
if ( url.length() > 0 )
{
mWebBrowser->navigateTo( url, "text/html");
@@ -460,6 +493,7 @@ void LLFloaterWebContent::onPopExternal()
// make sure there is at least something there.
// (perhaps this test should be for minimum length of a URL)
std::string url = mAddressCombo->getValue().asString();
+ LLStringUtil::trim(url);
if ( url.length() > 0 )
{
LLWeb::loadURLExternal( url );
diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h
index 86b5a5e00b..f22940cd07 100755
--- a/indra/newview/llfloaterwebcontent.h
+++ b/indra/newview/llfloaterwebcontent.h
@@ -55,6 +55,7 @@ public:
id;
Optional<bool> show_chrome,
allow_address_entry,
+ allow_back_forward_navigation,
trusted_content,
show_page_title;
Optional<LLRect> preferred_media_size;
@@ -106,9 +107,12 @@ protected:
LLView* mBtnReload;
LLView* mBtnStop;
- std::string mCurrentURL;
+ std::string mCurrentURL; // Current URL
+ std::string mDisplayURL; // URL being displayed in the address bar (can differ by trailing / leading space)
std::string mUUID;
bool mShowPageTitle;
+ bool mAllowNavigation;
+ bool mSecureURL; // true when the current url is prefixed "https://"
};
#endif // LL_LLFLOATERWEBCONTENT_H
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 2e53effcac..9ffbd1a675 100755
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -130,10 +130,10 @@ void process_dnd_im(const LLSD& notification)
fromID,
false,
false); //will need slight refactor to retrieve whether offline message or not (assume online for now)
- }
+ }
notify_of_message(data, true);
-}
+ }
@@ -155,22 +155,22 @@ static void on_avatar_name_cache_toast(const LLUUID& agent_id,
void notify_of_message(const LLSD& msg, bool is_dnd_msg)
{
- std::string user_preferences;
+ std::string user_preferences;
LLUUID participant_id = msg[is_dnd_msg ? "FROM_ID" : "from_id"].asUUID();
LLUUID session_id = msg[is_dnd_msg ? "SESSION_ID" : "session_id"].asUUID();
- LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
+ LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
- // do not show notification which goes from agent
- if (gAgent.getID() == participant_id)
- {
- return;
- }
+ // do not show notification which goes from agent
+ if (gAgent.getID() == participant_id)
+ {
+ return;
+ }
- // determine state of conversations floater
- enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status;
+ // determine state of conversations floater
+ enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status;
- LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id);
bool store_dnd_message = false; // flag storage of a dnd message
bool is_session_focused = session_floater->isTornOff() && session_floater->hasFocus();
@@ -179,23 +179,23 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg)
conversations_floater_status = CLOSED;
}
else if (!im_box->hasFocus() &&
- !(session_floater && LLFloater::isVisible(session_floater)
- && !session_floater->isMinimized() && session_floater->hasFocus()))
+ !(session_floater && LLFloater::isVisible(session_floater)
+ && !session_floater->isMinimized() && session_floater->hasFocus()))
{
conversations_floater_status = NOT_ON_TOP;
}
else if (im_box->getSelectedSession() != session_id)
{
conversations_floater_status = ON_TOP;
- }
+ }
else
{
conversations_floater_status = ON_TOP_AND_ITEM_IS_SELECTED;
}
- // determine user prefs for this session
- if (session_id.isNull())
- {
+ // determine user prefs for this session
+ if (session_id.isNull())
+ {
if (msg["source_type"].asInteger() == CHAT_SOURCE_OBJECT)
{
user_preferences = gSavedSettings.getString("NotificationObjectIMOptions");
@@ -206,50 +206,50 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg)
}
else
{
- user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions");
+ user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions");
if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNearbyChatIM") == TRUE))
{
make_ui_sound("UISndNewIncomingIMSession");
- }
+ }
}
}
- else if(session->isP2PSessionType())
- {
- if (LLAvatarTracker::instance().isBuddy(participant_id))
- {
- user_preferences = gSavedSettings.getString("NotificationFriendIMOptions");
+ else if(session->isP2PSessionType())
+ {
+ if (LLAvatarTracker::instance().isBuddy(participant_id))
+ {
+ user_preferences = gSavedSettings.getString("NotificationFriendIMOptions");
if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundFriendIM") == TRUE))
{
make_ui_sound("UISndNewIncomingIMSession");
}
- }
- else
- {
- user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions");
+ }
+ else
+ {
+ user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions");
if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNonFriendIM") == TRUE))
{
make_ui_sound("UISndNewIncomingIMSession");
- }
- }
+ }
+ }
}
- else if(session->isAdHocSessionType())
- {
- user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions");
+ else if(session->isAdHocSessionType())
+ {
+ user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions");
if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundConferenceIM") == TRUE))
{
make_ui_sound("UISndNewIncomingIMSession");
- }
+ }
}
- else if(session->isGroupSessionType())
- {
- user_preferences = gSavedSettings.getString("NotificationGroupChatOptions");
+ else if(session->isGroupSessionType())
+ {
+ user_preferences = gSavedSettings.getString("NotificationGroupChatOptions");
if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundGroupChatIM") == TRUE))
{
make_ui_sound("UISndNewIncomingIMSession");
}
- }
+ }
- // actions:
+ // actions:
// 0. nothing - exit
if (("noaction" == user_preferences ||
@@ -287,23 +287,23 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg)
}
}
}
- else
- {
+ else
+ {
store_dnd_message = true;
- }
+ }
- }
+ }
- // 2. Flash line item
- if ("openconversations" == user_preferences
- || ON_TOP == conversations_floater_status
- || ("toast" == user_preferences && ON_TOP != conversations_floater_status)
+ // 2. Flash line item
+ if ("openconversations" == user_preferences
+ || ON_TOP == conversations_floater_status
+ || ("toast" == user_preferences && ON_TOP != conversations_floater_status)
|| ("flash" == user_preferences && (CLOSED == conversations_floater_status
|| NOT_ON_TOP == conversations_floater_status))
|| is_dnd_msg)
- {
- if(!LLMuteList::getInstance()->isMuted(participant_id))
- {
+ {
+ if(!LLMuteList::getInstance()->isMuted(participant_id))
+ {
if(gAgent.isDoNotDisturb())
{
store_dnd_message = true;
@@ -318,43 +318,43 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg)
}
else
{
- im_box->flashConversationItemWidget(session_id, true);
- }
- }
+ im_box->flashConversationItemWidget(session_id, true);
+ }
+ }
}
}
- // 3. Flash FUI button
- if (("toast" == user_preferences || "flash" == user_preferences) &&
- (CLOSED == conversations_floater_status
+ // 3. Flash FUI button
+ if (("toast" == user_preferences || "flash" == user_preferences) &&
+ (CLOSED == conversations_floater_status
|| NOT_ON_TOP == conversations_floater_status)
&& !is_session_focused
&& !is_dnd_msg) //prevent flashing FUI button because the conversation floater will have already opened
{
if(!LLMuteList::getInstance()->isMuted(participant_id))
- {
+ {
if(!gAgent.isDoNotDisturb())
- {
+ {
gToolBarView->flashCommand(LLCommandId("chat"), true, im_box->isMinimized());
- }
+ }
else
{
store_dnd_message = true;
}
- }
+ }
}
- // 4. Toast
- if ((("toast" == user_preferences) &&
+ // 4. Toast
+ if ((("toast" == user_preferences) &&
(ON_TOP_AND_ITEM_IS_SELECTED != conversations_floater_status) &&
(!session_floater->isTornOff() || !LLFloater::isVisible(session_floater)))
- || !session_floater->isMessagePaneExpanded())
+ || !session_floater->isMessagePaneExpanded())
- {
- //Show IM toasts (upper right toasts)
- // Skip toasting for system messages and for nearby chat
- if(session_id.notNull() && participant_id.notNull())
- {
+ {
+ //Show IM toasts (upper right toasts)
+ // Skip toasting for system messages and for nearby chat
+ if(session_id.notNull() && participant_id.notNull())
+ {
if(!is_dnd_msg)
{
if(gAgent.isDoNotDisturb())
@@ -363,10 +363,10 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg)
}
else
{
- LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
- }
- }
- }
+ LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
+ }
+ }
+}
}
if (store_dnd_message)
{
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index e4fc469bb7..80ef506272 100755
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -4739,6 +4739,16 @@ void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string act
LLAvatarActions::offerTeleport(item->getCreatorUUID());
}
}
+ else if ("request_lure" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ LLAvatarActions::teleportRequest(item->getCreatorUUID());
+ }
+ }
+
else LLItemBridge::performAction(model, action);
}
@@ -4825,6 +4835,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
items.push_back(std::string("Send Instant Message Separator"));
items.push_back(std::string("Send Instant Message"));
items.push_back(std::string("Offer Teleport..."));
+ items.push_back(std::string("Request Teleport..."));
items.push_back(std::string("Conference Chat"));
if (!good_card)
@@ -4834,6 +4845,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
if (!good_card || !user_online)
{
disabled_items.push_back(std::string("Offer Teleport..."));
+ disabled_items.push_back(std::string("Request Teleport..."));
disabled_items.push_back(std::string("Conference Chat"));
}
}
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index dea90b9042..08b5eaedbb 100755
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -343,8 +343,11 @@ void LLNetMap::draw()
// Draw avatars
for (U32 i = 0; i < avatar_ids.size(); i++)
{
- pos_map = globalPosToView(positions[i]);
LLUUID uuid = avatar_ids[i];
+ // Skip self, we'll draw it later
+ if (uuid == gAgent.getID()) continue;
+
+ pos_map = globalPosToView(positions[i]);
bool show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(uuid) != NULL);
diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp
index 08c98e4f28..a3b15931c6 100755
--- a/indra/newview/llnotificationscripthandler.cpp
+++ b/indra/newview/llnotificationscripthandler.cpp
@@ -35,6 +35,9 @@
#include "llnotificationmanager.h"
#include "llnotifications.h"
#include "llscriptfloater.h"
+#include "llfacebookconnect.h"
+#include "llavatarname.h"
+#include "llavatarnamecache.h"
using namespace LLNotificationsUI;
@@ -87,7 +90,7 @@ bool LLScriptHandler::processNotification(const LLNotificationPtr& notification)
{
LLScriptFloaterManager::getInstance()->onAddNotification(notification->getID());
}
- else
+ else if (notification->canShowToast())
{
LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification);
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index d7c634d619..f551fc96ee 100755
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -28,6 +28,8 @@
// libs
#include "llavatarname.h"
+#include "llconversationview.h"
+#include "llfloaterimcontainer.h"
#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
#include "llmenubutton.h"
@@ -48,15 +50,19 @@
#include "llavataractions.h"
#include "llavatarlist.h"
#include "llavatarlistitem.h"
+#include "llavatarnamecache.h"
#include "llcallingcard.h" // for LLAvatarTracker
+#include "llcallbacklist.h"
+#include "llerror.h"
+#include "llfacebookconnect.h"
#include "llfloateravatarpicker.h"
-//#include "llfloaterminiinspector.h"
#include "llfriendcard.h"
#include "llgroupactions.h"
#include "llgrouplist.h"
#include "llinventoryobserver.h"
#include "llnetmap.h"
#include "llpanelpeoplemenus.h"
+#include "llparticipantlist.h"
#include "llsidetraypanelcontainer.h"
#include "llrecentpeople.h"
#include "llviewercontrol.h" // for gSavedSettings
@@ -64,6 +70,10 @@
#include "llvoiceclient.h"
#include "llworld.h"
#include "llspeakers.h"
+#include "llfloaterwebcontent.h"
+
+#include "llagentui.h"
+#include "llslurl.h"
#define FRIEND_LIST_UPDATE_TIMEOUT 0.5
#define NEARBY_LIST_UPDATE_INTERVAL 1
@@ -73,9 +83,9 @@ static const std::string FRIENDS_TAB_NAME = "friends_panel";
static const std::string GROUP_TAB_NAME = "groups_panel";
static const std::string RECENT_TAB_NAME = "recent_panel";
static const std::string BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars
-
static const std::string COLLAPSED_BY_USER = "collapsed_by_user";
+
extern S32 gMaxAgentGroups;
/** Comparator for comparing avatar items by last interaction date */
@@ -495,6 +505,7 @@ public:
LLPanelPeople::LLPanelPeople()
: LLPanel(),
+ mTryToConnectToFbc(true),
mTabContainer(NULL),
mOnlineFriendList(NULL),
mAllFriendList(NULL),
@@ -573,6 +584,7 @@ BOOL LLPanelPeople::postBuild()
getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
+ getChild<LLFilterEditor>("fbc_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
mTabContainer = getChild<LLTabContainer>("tabs");
mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2));
@@ -583,8 +595,11 @@ BOOL LLPanelPeople::postBuild()
// updater is active only if panel is visible to user.
friends_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFriendListUpdater, _2));
friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::removePicker, this));
+ friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::updateFacebookList, this, _2));
+
mOnlineFriendList = friends_tab->getChild<LLAvatarList>("avatars_online");
mAllFriendList = friends_tab->getChild<LLAvatarList>("avatars_all");
+ mSuggestedFriends = friends_tab->getChild<LLAvatarList>("suggested_friends");
mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online"));
mOnlineFriendList->setShowIcons("FriendsListShowIcons");
mOnlineFriendList->showPermissions("FriendsListShowPermissions");
@@ -617,6 +632,7 @@ BOOL LLPanelPeople::postBuild()
mRecentList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
+ mSuggestedFriends->setContextMenu(&LLPanelPeopleMenus::gSuggestedFriendsContextMenu);
setSortOrder(mRecentList, (ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"), false);
setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false);
@@ -695,7 +711,7 @@ void LLPanelPeople::updateFriendListHelpText()
// Seems sometimes all_friends can be empty because of issue with Inventory loading (clear cache, slow connection...)
// So, lets check all lists to avoid overlapping the text with online list. See EXT-6448.
- bool any_friend_exists = mAllFriendList->filterHasMatches() || mOnlineFriendList->filterHasMatches();
+ bool any_friend_exists = mAllFriendList->filterHasMatches() || mOnlineFriendList->filterHasMatches() || mSuggestedFriends->filterHasMatches();
no_friends_text->setVisible(!any_friend_exists);
if (no_friends_text->getVisible())
{
@@ -762,9 +778,40 @@ void LLPanelPeople::updateFriendList()
mAllFriendList->setDirty(true, !mAllFriendList->filterHasMatches());
//update trash and other buttons according to a selected item
updateButtons();
+ updateSuggestedFriendList();
showFriendsAccordionsIfNeeded();
}
+bool LLPanelPeople::updateSuggestedFriendList()
+{
+ const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ uuid_vec_t& suggested_friends = mSuggestedFriends->getIDs();
+ suggested_friends.clear();
+
+ //Add suggested friends
+ LLSD friends = LLFacebookConnect::instance().getContent();
+ for (LLSD::array_const_iterator i = friends.beginArray(); i != friends.endArray(); ++i)
+ {
+ LLUUID agent_id = (*i).asUUID();
+ bool second_life_buddy = agent_id.notNull() ? av_tracker.isBuddy(agent_id) : false;
+
+ if(!second_life_buddy)
+ {
+ //FB+SL but not SL friend
+ if (agent_id.notNull())
+ {
+ suggested_friends.push_back(agent_id);
+ }
+ }
+ }
+
+ //Force a refresh when there aren't any filter matches (prevent displaying content that shouldn't display)
+ mSuggestedFriends->setDirty(true, !mSuggestedFriends->filterHasMatches());
+ showFriendsAccordionsIfNeeded();
+
+ return false;
+}
+
void LLPanelPeople::updateNearbyList()
{
if (!mNearbyList)
@@ -788,6 +835,51 @@ void LLPanelPeople::updateRecentList()
mRecentList->setDirty();
}
+bool LLPanelPeople::onConnectedToFacebook(const LLSD& data)
+{
+ LLSD::Integer connection_state = data.get("enum").asInteger();
+
+ if (connection_state == LLFacebookConnect::FB_CONNECTED)
+ {
+ LLFacebookConnect::instance().loadFacebookFriends();
+ }
+ else if(connection_state == LLFacebookConnect::FB_NOT_CONNECTED)
+ {
+ updateSuggestedFriendList();
+ };
+
+ return false;
+}
+
+void LLPanelPeople::updateFacebookList(bool visible)
+{
+ if (visible)
+ {
+ LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLPanelPeople"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectContent").listen("LLPanelPeople", boost::bind(&LLPanelPeople::updateSuggestedFriendList, this));
+
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLPanelPeople"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLPanelPeople", boost::bind(&LLPanelPeople::onConnectedToFacebook, this, _1));
+
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ LLFacebookConnect::instance().loadFacebookFriends();
+ }
+ else if(mTryToConnectToFbc)
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook();
+ mTryToConnectToFbc = false;
+ }
+
+ updateSuggestedFriendList();
+ }
+ else
+ {
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLPanelPeople");
+ LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLPanelPeople");
+ }
+}
+
void LLPanelPeople::updateButtons()
{
std::string cur_tab = getActiveTabName();
@@ -993,23 +1085,25 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string)
{
// store accordion tabs opened/closed state before any manipulation with accordion tabs
if (!saved_filter.empty())
- {
- notifyChildren(LLSD().with("action","store_state"));
- }
+ {
+ notifyChildren(LLSD().with("action","store_state"));
+ }
mOnlineFriendList->setNameFilter(filter);
mAllFriendList->setNameFilter(filter);
+ mSuggestedFriends->setNameFilter(filter);
- setAccordionCollapsedByUser("tab_online", false);
- setAccordionCollapsedByUser("tab_all", false);
- showFriendsAccordionsIfNeeded();
+ setAccordionCollapsedByUser("tab_online", false);
+ setAccordionCollapsedByUser("tab_all", false);
+ setAccordionCollapsedByUser("tab_suggested_friends", false);
+ showFriendsAccordionsIfNeeded();
// restore accordion tabs state _after_ all manipulations
if(saved_filter.empty())
- {
- notifyChildren(LLSD().with("action","restore_state"));
- }
-}
+ {
+ notifyChildren(LLSD().with("action","restore_state"));
+ }
+ }
else if (cur_tab == GROUP_TAB_NAME)
{
mGroupList->setNameFilter(filter);
@@ -1229,7 +1323,7 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata)
mAllFriendList->showPermissions(show_permissions);
mOnlineFriendList->showPermissions(show_permissions);
}
-}
+ }
void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata)
{
@@ -1387,6 +1481,7 @@ void LLPanelPeople::showFriendsAccordionsIfNeeded()
// Expand and show accordions if needed, else - hide them
showAccordion("tab_online", mOnlineFriendList->filterHasMatches());
showAccordion("tab_all", mAllFriendList->filterHasMatches());
+ showAccordion("tab_suggested_friends", mSuggestedFriends->filterHasMatches());
// Rearrange accordions
LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("friends_accordion");
@@ -1450,4 +1545,5 @@ bool LLPanelPeople::isAccordionCollapsedByUser(const std::string& name)
return isAccordionCollapsedByUser(getChild<LLUICtrl>(name));
}
+
// EOF
diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h
index 4740964dee..c7141f36ee 100755
--- a/indra/newview/llpanelpeople.h
+++ b/indra/newview/llpanelpeople.h
@@ -22,7 +22,7 @@
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
- */
+ */
#ifndef LL_LLPANELPEOPLE_H
#define LL_LLPANELPEOPLE_H
@@ -30,6 +30,7 @@
#include <llpanel.h>
#include "llcallingcard.h" // for avatar tracker
+#include "llfloaterwebcontent.h"
#include "llvoiceclient.h"
class LLAvatarList;
@@ -55,6 +56,8 @@ public:
// when voice is available
/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+ bool mTryToConnectToFbc;
+
// internals
class Updater;
@@ -73,8 +76,10 @@ private:
// methods indirectly called by the updaters
void updateFriendListHelpText();
void updateFriendList();
+ bool updateSuggestedFriendList();
void updateNearbyList();
void updateRecentList();
+ void updateFacebookList(bool visible);
bool isItemsFreeOfFriends(const uuid_vec_t& uuids);
@@ -121,6 +126,8 @@ private:
void onFriendListRefreshComplete(LLUICtrl*ctrl, const LLSD& param);
+ bool onConnectedToFacebook(const LLSD& data);
+
void setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool collapsed);
void setAccordionCollapsedByUser(const std::string& name, bool collapsed);
bool isAccordionCollapsedByUser(LLUICtrl* acc_tab);
@@ -129,6 +136,7 @@ private:
LLTabContainer* mTabContainer;
LLAvatarList* mOnlineFriendList;
LLAvatarList* mAllFriendList;
+ LLAvatarList* mSuggestedFriends;
LLAvatarList* mNearbyList;
LLAvatarList* mRecentList;
LLGroupList* mGroupList;
@@ -140,6 +148,7 @@ private:
Updater* mFriendListUpdater;
Updater* mNearbyListUpdater;
Updater* mRecentListUpdater;
+ Updater* mFacebookListUpdater;
Updater* mButtonsUpdater;
LLHandle< LLFloater > mPicker;
};
diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp
index 49f7361c4a..6979ae06e0 100755
--- a/indra/newview/llpanelpeoplemenus.cpp
+++ b/indra/newview/llpanelpeoplemenus.cpp
@@ -47,6 +47,7 @@ namespace LLPanelPeopleMenus
PeopleContextMenu gPeopleContextMenu;
NearbyPeopleContextMenu gNearbyPeopleContextMenu;
+SuggestedFriendsContextMenu gSuggestedFriendsContextMenu;
//== PeopleContextMenu ===============================================================
@@ -74,6 +75,7 @@ LLContextMenu* PeopleContextMenu::createMenu()
registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, id));
registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, id));
registrar.add("Avatar.InviteToGroup", boost::bind(&LLAvatarActions::inviteToGroup, id));
+ registrar.add("Avatar.TeleportRequest", boost::bind(&PeopleContextMenu::requestTeleport, this));
registrar.add("Avatar.Calllog", boost::bind(&LLAvatarActions::viewChatHistory, id));
enable_registrar.add("Avatar.EnableItem", boost::bind(&PeopleContextMenu::enableContextMenuItem, this, _2));
@@ -125,6 +127,7 @@ void PeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
items.push_back(std::string("view_profile"));
items.push_back(std::string("im"));
items.push_back(std::string("offer_teleport"));
+ items.push_back(std::string("request_teleport"));
items.push_back(std::string("voice_call"));
items.push_back(std::string("chat_history"));
items.push_back(std::string("separator_chat_history"));
@@ -255,6 +258,13 @@ bool PeopleContextMenu::checkContextMenuItem(const LLSD& userdata)
return false;
}
+void PeopleContextMenu::requestTeleport()
+{
+ // boost::bind cannot recognize overloaded method LLAvatarActions::teleportRequest(),
+ // so we have to use a wrapper.
+ LLAvatarActions::teleportRequest(mUUIDs.front());
+}
+
void PeopleContextMenu::offerTeleport()
{
// boost::bind cannot recognize overloaded method LLAvatarActions::offerTeleport(),
@@ -284,6 +294,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"));
@@ -301,4 +312,36 @@ void NearbyPeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
hide_context_entries(menu, items, disabled_items);
}
+//== SuggestedFriendsContextMenu ===============================================================
+
+LLContextMenu* SuggestedFriendsContextMenu::createMenu()
+{
+ // set up the callbacks for all of the avatar menu items
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+ LLContextMenu* menu;
+
+ // Set up for one person selected menu
+ const LLUUID& id = mUUIDs.front();
+ registrar.add("Avatar.Profile", boost::bind(&LLAvatarActions::showProfile, id));
+ registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, id));
+
+ // create the context menu from the XUI
+ menu = createFromFile("menu_people_nearby.xml");
+ buildContextMenu(*menu, 0x0);
+
+ return menu;
+}
+
+void SuggestedFriendsContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
+{
+ menuentry_vec_t items;
+ menuentry_vec_t disabled_items;
+
+ items.push_back(std::string("view_profile"));
+ items.push_back(std::string("add_friend"));
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
} // namespace LLPanelPeopleMenus
diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h
index 0a1dcef303..945382ebc5 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();
};
/**
@@ -58,8 +59,21 @@ protected:
/*virtual*/ void buildContextMenu(class LLMenuGL& menu, U32 flags);
};
+/**
+ * Menu used in the suggested friends list.
+ */
+class SuggestedFriendsContextMenu : public PeopleContextMenu
+{
+public:
+ /*virtual*/ LLContextMenu * createMenu();
+
+protected:
+ /*virtual*/ void buildContextMenu(class LLMenuGL& menu, U32 flags);
+};
+
extern PeopleContextMenu gPeopleContextMenu;
extern NearbyPeopleContextMenu gNearbyPeopleContextMenu;
+extern SuggestedFriendsContextMenu gSuggestedFriendsContextMenu;
} // namespace LLPanelPeopleMenus
diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp
index 76d38f067d..9504f22a1d 100755
--- a/indra/newview/llpanelprimmediacontrols.cpp
+++ b/indra/newview/llpanelprimmediacontrols.cpp
@@ -94,7 +94,8 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() :
mZoomObjectFace(0),
mVolumeSliderVisible(0),
mWindowShade(NULL),
- mHideImmediately(false)
+ mHideImmediately(false),
+ mSecureURL(false)
{
mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this));
mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this));
@@ -345,7 +346,7 @@ void LLPanelPrimMediaControls::updateShape()
// Disable zoom if HUD
mZoomCtrl->setEnabled(!is_hud);
mUnzoomCtrl->setEnabled(!is_hud);
- mSecureLockIcon->setVisible(false);
+ mSecureURL = false;
mCurrentURL = media_impl->getCurrentMediaURL();
mBackCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateBack() && can_navigate);
@@ -382,7 +383,7 @@ void LLPanelPrimMediaControls::updateShape()
mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible());
mWhitelistIcon->setVisible(false);
- mSecureLockIcon->setVisible(false);
+ mSecureURL = false;
if (mMediaPanelScroll)
{
mMediaPanelScroll->setVisible(false);
@@ -416,7 +417,7 @@ void LLPanelPrimMediaControls::updateShape()
mMediaPlaySliderCtrl->setEnabled(true);
}
- // video vloume
+ // video volume
if(volume <= 0.0)
{
mMuteBtn->setToggleState(true);
@@ -492,10 +493,8 @@ void LLPanelPrimMediaControls::updateShape()
std::string prefix = std::string("https://");
std::string test_prefix = mCurrentURL.substr(0, prefix.length());
LLStringUtil::toLower(test_prefix);
- if(test_prefix == prefix)
- {
- mSecureLockIcon->setVisible(has_focus);
- }
+ mSecureURL = has_focus && (test_prefix == prefix);
+ mCurrentURL = (mSecureURL ? " " + mCurrentURL : mCurrentURL);
if(mCurrentURL!=mPreviousURL)
{
@@ -746,6 +745,9 @@ void LLPanelPrimMediaControls::draw()
clearFaceOnFade();
}
}
+
+ // Show/hide the lock icon for secure browsing
+ mSecureLockIcon->setVisible(mSecureURL && !mMediaAddress->hasFocus());
// Build rect for icon area in coord system of this panel
// Assumes layout_stack is a direct child of this panel
diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h
index eeb433e306..6d2eb3430e 100755
--- a/indra/newview/llpanelprimmediacontrols.h
+++ b/indra/newview/llpanelprimmediacontrols.h
@@ -191,6 +191,7 @@ private:
bool mUpdateSlider;
bool mClearFaceOnFade;
bool mHideImmediately;
+ bool mSecureURL;
LLMatrix4 mLastCameraMat;
EZoomLevel mCurrentZoom;
diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp
index c53760bca1..ee6893907e 100755
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h"
#include "llavatarnamecache.h"
+#include "llerror.h"
#include "llimview.h"
#include "llfloaterimcontainer.h"
#include "llparticipantlist.h"
@@ -401,6 +402,8 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)
adjustParticipant(avatar_id);
}
+static LLFastTimer::DeclareTimer FTM_FOLDERVIEW_TEST("add test avatar agents");
+
void LLParticipantList::adjustParticipant(const LLUUID& speaker_id)
{
LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id);
diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp
index 928d26646b..c4d5450e2b 100755
--- a/indra/newview/llsechandler_basic.cpp
+++ b/indra/newview/llsechandler_basic.cpp
@@ -207,6 +207,7 @@ LLSD _basic_constraints_ext(X509* cert)
}
}
+ BASIC_CONSTRAINTS_free( bs );
}
return result;
}
@@ -268,6 +269,8 @@ LLSD _ext_key_usage_ext(X509* cert)
ASN1_OBJECT_free(usage);
}
}
+
+ EXTENDED_KEY_USAGE_free( eku );
}
return result;
}
@@ -280,6 +283,8 @@ LLSD _subject_key_identifier_ext(X509 *cert)
if(skeyid)
{
result = cert_string_from_octet_string(skeyid);
+
+ ASN1_OCTET_STRING_free( skeyid );
}
return result;
}
@@ -300,6 +305,9 @@ LLSD _authority_key_identifier_ext(X509* cert)
{
result[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL] = cert_string_from_asn1_integer(akeyid->serial);
}
+
+
+ AUTHORITY_KEYID_free( akeyid );
}
// we ignore the issuer name in the authority key identifier, we check the issue name via
@@ -1049,6 +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);
+ X509_free( cert_x509 );
+ cert_x509 = NULL;
t_cert_cache::iterator cache_entry = mTrustedCertCache.find(sha1_hash);
if(cache_entry != mTrustedCertCache.end())
{
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
new file mode 100644
index 0000000000..7532ebfc57
--- /dev/null
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -0,0 +1,874 @@
+/**
+* @file llsnapshotlivepreview.cpp
+* @brief Implementation of llsnapshotlivepreview
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* 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 "llviewerprecompiledheaders.h"
+
+#include "llagent.h"
+#include "llagentcamera.h"
+#include "llagentui.h"
+#include "llcombobox.h"
+#include "lleconomy.h"
+#include "llfloaterperms.h"
+#include "llfloaterreg.h"
+#include "llfloatersocial.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llimagejpeg.h"
+#include "llimagepng.h"
+#include "lllandmarkactions.h"
+#include "lllocalcliprect.h"
+#include "llnotificationsutil.h"
+#include "llslurl.h"
+#include "llsnapshotlivepreview.h"
+#include "lltoolfocus.h"
+#include "llviewercontrol.h"
+#include "llviewermenufile.h" // upload_new_resource()
+#include "llviewerstats.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "llwebsharing.h"
+#include "llwindow.h"
+#include "llworld.h"
+
+const F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f;
+
+F32 SHINE_TIME = 0.5f;
+F32 SHINE_WIDTH = 0.6f;
+F32 SHINE_OPACITY = 0.3f;
+F32 FALL_TIME = 0.6f;
+S32 BORDER_WIDTH = 6;
+
+const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512
+
+std::set<LLSnapshotLivePreview*> LLSnapshotLivePreview::sList;
+
+LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Params& p)
+ : LLView(p),
+ mColor(1.f, 0.f, 0.f, 0.5f),
+ mCurImageIndex(0),
+ mPreviewImage(NULL),
+ mThumbnailImage(NULL) ,
+ mThumbnailWidth(0),
+ mThumbnailHeight(0),
+ mPreviewImageEncoded(NULL),
+ mFormattedImage(NULL),
+ mShineCountdown(0),
+ mFlashAlpha(0.f),
+ mNeedsFlash(TRUE),
+ mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")),
+ mDataSize(0),
+ mSnapshotType(SNAPSHOT_POSTCARD),
+ mSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))),
+ mSnapshotUpToDate(FALSE),
+ mCameraPos(LLViewerCamera::getInstance()->getOrigin()),
+ mCameraRot(LLViewerCamera::getInstance()->getQuaternion()),
+ mSnapshotActive(FALSE),
+ mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR)
+{
+ setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality"));
+ mSnapshotDelayTimer.setTimerExpirySec(0.0f);
+ mSnapshotDelayTimer.start();
+ // gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
+ sList.insert(this);
+ setFollowsAll();
+ mWidth[0] = gViewerWindow->getWindowWidthRaw();
+ mWidth[1] = gViewerWindow->getWindowWidthRaw();
+ mHeight[0] = gViewerWindow->getWindowHeightRaw();
+ mHeight[1] = gViewerWindow->getWindowHeightRaw();
+ mImageScaled[0] = FALSE;
+ mImageScaled[1] = FALSE;
+
+ mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
+ mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;
+ mThumbnailUpdateLock = FALSE ;
+ mThumbnailUpToDate = FALSE ;
+}
+
+LLSnapshotLivePreview::~LLSnapshotLivePreview()
+{
+ // delete images
+ mPreviewImage = NULL;
+ mPreviewImageEncoded = NULL;
+ mFormattedImage = NULL;
+
+ // gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
+ sList.erase(this);
+}
+
+void LLSnapshotLivePreview::setMaxImageSize(S32 size)
+{
+ if(size < MAX_SNAPSHOT_IMAGE_SIZE)
+ {
+ mMaxImageSize = size;
+ }
+ else
+ {
+ mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
+ }
+}
+
+LLViewerTexture* LLSnapshotLivePreview::getCurrentImage()
+{
+ return mViewerImage[mCurImageIndex];
+}
+
+F32 LLSnapshotLivePreview::getAspect()
+{
+ F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
+ F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
+
+ if (!mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ {
+ return image_aspect_ratio;
+ }
+ else
+ {
+ return window_aspect_ratio;
+ }
+}
+
+F32 LLSnapshotLivePreview::getImageAspect()
+{
+ if (!getCurrentImage())
+ {
+ return 0.f;
+ }
+
+ return getAspect() ;
+}
+
+void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
+{
+ // Invalidate current image.
+ lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl;
+ if (getSnapshotUpToDate())
+ {
+ S32 old_image_index = mCurImageIndex;
+ mCurImageIndex = (mCurImageIndex + 1) % 2;
+ setSize(mWidth[old_image_index], mHeight[old_image_index]);
+ mFallAnimTimer.start();
+ }
+ mSnapshotUpToDate = FALSE;
+
+ // Update snapshot source rect depending on whether we keep the aspect ratio.
+ LLRect& rect = mImageRect[mCurImageIndex];
+ rect.set(0, getRect().getHeight(), getRect().getWidth(), 0);
+
+ F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight());
+ F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight());
+
+ if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ {
+ if (image_aspect_ratio > window_aspect_ratio)
+ {
+ // trim off top and bottom
+ S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio);
+ rect.mBottom += (getRect().getHeight() - new_height) / 2;
+ rect.mTop -= (getRect().getHeight() - new_height) / 2;
+ }
+ else if (image_aspect_ratio < window_aspect_ratio)
+ {
+ // trim off left and right
+ S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio);
+ rect.mLeft += (getRect().getWidth() - new_width) / 2;
+ rect.mRight -= (getRect().getWidth() - new_width) / 2;
+ }
+ }
+
+ // Stop shining animation.
+ mShineAnimTimer.stop();
+
+ // Update snapshot if requested.
+ if (new_snapshot)
+ {
+ mSnapshotDelayTimer.start();
+ mSnapshotDelayTimer.setTimerExpirySec(delay);
+ LLFloaterSnapshot::preUpdate();
+ LLFloaterSocial::preUpdate();
+ }
+
+ // Update thumbnail if requested.
+ if(new_thumbnail)
+ {
+ mThumbnailUpToDate = FALSE ;
+ }
+}
+
+void LLSnapshotLivePreview::setSnapshotQuality(S32 quality)
+{
+ llclamp(quality, 0, 100);
+ if (quality != mSnapshotQuality)
+ {
+ mSnapshotQuality = quality;
+ gSavedSettings.setS32("SnapshotQuality", quality);
+ mSnapshotUpToDate = FALSE;
+ }
+}
+
+void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y)
+{
+ F32 line_width ;
+ glGetFloatv(GL_LINE_WIDTH, &line_width) ;
+ glLineWidth(2.0f * line_width) ;
+ LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ;
+ gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y,
+ mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, FALSE ) ;
+ glLineWidth(line_width) ;
+
+ //draw four alpha rectangles to cover areas outside of the snapshot image
+ if(!mKeepAspectRatio)
+ {
+ LLColor4 alpha_color(0.5f, 0.5f, 0.5f, 0.8f) ;
+ S32 dwl = 0, dwr = 0 ;
+ if(mThumbnailWidth > mPreviewRect.getWidth())
+ {
+ dwl = (mThumbnailWidth - mPreviewRect.getWidth()) >> 1 ;
+ dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ;
+
+ gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y,
+ mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ;
+ gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y,
+ mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ;
+ }
+
+ if(mThumbnailHeight > mPreviewRect.getHeight())
+ {
+ S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ;
+ gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y ,
+ mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, TRUE ) ;
+
+ dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ;
+ gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh,
+ mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, TRUE ) ;
+ }
+ }
+}
+
+//called when the frame is frozen.
+void LLSnapshotLivePreview::draw()
+{
+ if (getCurrentImage() &&
+ mPreviewImageEncoded.notNull() &&
+ getSnapshotUpToDate())
+ {
+ LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f);
+ gl_rect_2d(getRect(), bg_color);
+ const LLRect& rect = getImageRect();
+ LLRect shadow_rect = rect;
+ shadow_rect.stretch(BORDER_WIDTH);
+ gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10);
+
+ LLColor4 image_color(1.f, 1.f, 1.f, 1.f);
+ gGL.color4fv(image_color.mV);
+ gGL.getTexUnit(0)->bind(getCurrentImage());
+ // calculate UV scale
+ F32 uv_width = isImageScaled() ? 1.f : llmin((F32)getWidth() / (F32)getCurrentImage()->getWidth(), 1.f);
+ F32 uv_height = isImageScaled() ? 1.f : llmin((F32)getHeight() / (F32)getCurrentImage()->getHeight(), 1.f);
+ gGL.pushMatrix();
+ {
+ gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom, 0.f);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.texCoord2f(uv_width, uv_height);
+ gGL.vertex2i(rect.getWidth(), rect.getHeight() );
+
+ gGL.texCoord2f(0.f, uv_height);
+ gGL.vertex2i(0, rect.getHeight() );
+
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex2i(0, 0);
+
+ gGL.texCoord2f(uv_width, 0.f);
+ gGL.vertex2i(rect.getWidth(), 0);
+ }
+ gGL.end();
+ }
+ gGL.popMatrix();
+
+ gGL.color4f(1.f, 1.f, 1.f, mFlashAlpha);
+ gl_rect_2d(getRect());
+ if (mNeedsFlash)
+ {
+ if (mFlashAlpha < 1.f)
+ {
+ mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f));
+ }
+ else
+ {
+ mNeedsFlash = FALSE;
+ }
+ }
+ else
+ {
+ mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+
+ // Draw shining animation if appropriate.
+ if (mShineCountdown > 0)
+ {
+ mShineCountdown--;
+ if (mShineCountdown == 0)
+ {
+ mShineAnimTimer.start();
+ }
+ }
+ else if (mShineAnimTimer.getStarted())
+ {
+ lldebugs << "Drawing shining animation" << llendl;
+ F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME);
+
+ // draw "shine" effect
+ LLLocalClipRect clip(getLocalRect());
+ {
+ // draw diagonal stripe with gradient that passes over screen
+ S32 x1 = gViewerWindow->getWindowWidthScaled() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f)));
+ S32 x2 = x1 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH);
+ S32 x3 = x2 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH);
+ S32 y1 = 0;
+ S32 y2 = gViewerWindow->getWindowHeightScaled();
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.color4f(1.f, 1.f, 1.f, 0.f);
+ gGL.vertex2i(x1, y1);
+ gGL.vertex2i(x1 + gViewerWindow->getWindowWidthScaled(), y2);
+ gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY);
+ gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2);
+ gGL.vertex2i(x2, y1);
+
+ gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY);
+ gGL.vertex2i(x2, y1);
+ gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2);
+ gGL.color4f(1.f, 1.f, 1.f, 0.f);
+ gGL.vertex2i(x3 + gViewerWindow->getWindowWidthScaled(), y2);
+ gGL.vertex2i(x3, y1);
+ }
+ gGL.end();
+ }
+
+ // if we're at the end of the animation, stop
+ if (shine_interp >= 1.f)
+ {
+ mShineAnimTimer.stop();
+ }
+ }
+ }
+
+ // draw framing rectangle
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.color4f(1.f, 1.f, 1.f, 1.f);
+ const LLRect& outline_rect = getImageRect();
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mRight, outline_rect.mTop);
+ gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop);
+
+ gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom);
+ gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom);
+ gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+
+ gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop);
+ gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom);
+ gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+
+ gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom);
+ gGL.vertex2i(outline_rect.mRight, outline_rect.mTop);
+ gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ }
+ gGL.end();
+ }
+
+ // draw old image dropping away
+ if (mFallAnimTimer.getStarted())
+ {
+ S32 old_image_index = (mCurImageIndex + 1) % 2;
+ if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME)
+ {
+ lldebugs << "Drawing fall animation" << llendl;
+ F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME;
+ F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f);
+ LLColor4 image_color(1.f, 1.f, 1.f, alpha);
+ gGL.color4fv(image_color.mV);
+ gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]);
+ // calculate UV scale
+ // *FIX get this to work with old image
+ BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull();
+ F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f;
+ F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f;
+ gGL.pushMatrix();
+ {
+ LLRect& rect = mImageRect[old_image_index];
+ gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - llround(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f);
+ gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.texCoord2f(uv_width, uv_height);
+ gGL.vertex2i(rect.getWidth(), rect.getHeight() );
+
+ gGL.texCoord2f(0.f, uv_height);
+ gGL.vertex2i(0, rect.getHeight() );
+
+ gGL.texCoord2f(0.f, 0.f);
+ gGL.vertex2i(0, 0);
+
+ gGL.texCoord2f(uv_width, 0.f);
+ gGL.vertex2i(rect.getWidth(), 0);
+ }
+ gGL.end();
+ }
+ gGL.popMatrix();
+ }
+ }
+}
+
+/*virtual*/
+void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLRect old_rect = getRect();
+ LLView::reshape(width, height, called_from_parent);
+ if (old_rect.getWidth() != width || old_rect.getHeight() != height)
+ {
+ lldebugs << "window reshaped, updating thumbnail" << llendl;
+ updateSnapshot(FALSE, TRUE);
+ }
+}
+
+BOOL LLSnapshotLivePreview::setThumbnailImageSize()
+{
+ if(getWidth() < 10 || getHeight() < 10)
+ {
+ return FALSE ;
+ }
+ S32 window_width = gViewerWindow->getWindowWidthRaw() ;
+ S32 window_height = gViewerWindow->getWindowHeightRaw() ;
+
+ F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height);
+
+ // UI size for thumbnail
+ // *FIXME: the rect does not change, so maybe there's no need to recalculate max w/h.
+ const LLRect& thumbnail_rect = mThumbnailPlaceholderRect;
+ S32 max_width = thumbnail_rect.getWidth();
+ S32 max_height = thumbnail_rect.getHeight();
+
+ if (window_aspect_ratio > (F32)max_width / max_height)
+ {
+ // image too wide, shrink to width
+ mThumbnailWidth = max_width;
+ mThumbnailHeight = llround((F32)max_width / window_aspect_ratio);
+ }
+ else
+ {
+ // image too tall, shrink to height
+ mThumbnailHeight = max_height;
+ mThumbnailWidth = llround((F32)max_height * window_aspect_ratio);
+ }
+
+ if(mThumbnailWidth > window_width || mThumbnailHeight > window_height)
+ {
+ return FALSE ;//if the window is too small, ignore thumbnail updating.
+ }
+
+ S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ;
+ if(!mKeepAspectRatio)
+ {
+ F32 ratio_x = (F32)getWidth() / window_width ;
+ F32 ratio_y = (F32)getHeight() / window_height ;
+
+ //if(getWidth() > window_width ||
+ // getHeight() > window_height )
+ {
+ if(ratio_x > ratio_y)
+ {
+ top = (S32)(top * ratio_y / ratio_x) ;
+ }
+ else
+ {
+ right = (S32)(right * ratio_x / ratio_y) ;
+ }
+ }
+ //else
+ //{
+ // right = (S32)(right * ratio_x) ;
+ // top = (S32)(top * ratio_y) ;
+ //}
+ left = (S32)((mThumbnailWidth - right) * 0.5f) ;
+ bottom = (S32)((mThumbnailHeight - top) * 0.5f) ;
+ top += bottom ;
+ right += left ;
+ }
+ mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ;
+
+ return TRUE ;
+}
+
+void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
+{
+ if(mThumbnailUpdateLock) //in the process of updating
+ {
+ return ;
+ }
+ if(getThumbnailUpToDate() && !force_update)//already updated
+ {
+ return ;
+ }
+ if(getWidth() < 10 || getHeight() < 10)
+ {
+ return ;
+ }
+
+ ////lock updating
+ mThumbnailUpdateLock = TRUE ;
+
+ if(!setThumbnailImageSize())
+ {
+ mThumbnailUpdateLock = FALSE ;
+ mThumbnailUpToDate = TRUE ;
+ return ;
+ }
+
+ if(mThumbnailImage)
+ {
+ resetThumbnailImage() ;
+ }
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ if(!gViewerWindow->thumbnailSnapshot(raw,
+ mThumbnailWidth, mThumbnailHeight,
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE,
+ mSnapshotBufferType) )
+ {
+ raw = NULL ;
+ }
+
+ if(raw)
+ {
+ raw->expandToPowerOfTwo();
+ mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
+ mThumbnailUpToDate = TRUE ;
+ }
+
+ //unlock updating
+ mThumbnailUpdateLock = FALSE ;
+}
+
+
+// Called often. Checks whether it's time to grab a new snapshot and if so, does it.
+// Returns TRUE if new snapshot generated, FALSE otherwise.
+//static
+BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview;
+ if (previewp->getWidth() == 0 || previewp->getHeight() == 0)
+ {
+ llwarns << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << llendl;
+ return FALSE;
+ }
+
+ // If we're in freeze-frame mode and camera has moved, update snapshot.
+ LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin();
+ LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion();
+ if (gSavedSettings.getBOOL("FreezeTime") &&
+ (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))
+ {
+ previewp->mCameraPos = new_camera_pos;
+ previewp->mCameraRot = new_camera_rot;
+ // request a new snapshot whenever the camera moves, with a time delay
+ BOOL autosnap = gSavedSettings.getBOOL("AutoSnapshot");
+ lldebugs << "camera moved, updating thumbnail" << llendl;
+ previewp->updateSnapshot(
+ autosnap, // whether a new snapshot is needed or merely invalidate the existing one
+ FALSE, // or if 1st arg is false, whether to produce a new thumbnail image.
+ autosnap ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true.
+ }
+
+ // see if it's time yet to snap the shot and bomb out otherwise.
+ previewp->mSnapshotActive =
+ (previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired())
+ && !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active
+ if ( ! previewp->mSnapshotActive)
+ {
+ return FALSE;
+ }
+
+ // time to produce a snapshot
+ previewp->setThumbnailImageSize();
+
+ lldebugs << "producing snapshot" << llendl;
+ if (!previewp->mPreviewImage)
+ {
+ previewp->mPreviewImage = new LLImageRaw;
+ }
+
+ if (!previewp->mPreviewImageEncoded)
+ {
+ previewp->mPreviewImageEncoded = new LLImageRaw;
+ }
+
+ previewp->setVisible(FALSE);
+ previewp->setEnabled(FALSE);
+
+ previewp->getWindow()->incBusyCount();
+ previewp->setImageScaled(FALSE);
+
+ // grab the raw image and encode it into desired format
+ if(gViewerWindow->rawSnapshot(
+ previewp->mPreviewImage,
+ previewp->getWidth(),
+ previewp->getHeight(),
+ previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"),
+ previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE,
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE,
+ previewp->mSnapshotBufferType,
+ previewp->getMaxImageSize()))
+ {
+ previewp->mPreviewImageEncoded->resize(
+ previewp->mPreviewImage->getWidth(),
+ previewp->mPreviewImage->getHeight(),
+ previewp->mPreviewImage->getComponents());
+
+ if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE)
+ {
+ lldebugs << "Encoding new image of format J2C" << llendl;
+ LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(
+ previewp->mPreviewImage->getData(),
+ previewp->mPreviewImage->getWidth(),
+ previewp->mPreviewImage->getHeight(),
+ previewp->mPreviewImage->getComponents());
+
+ scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
+ previewp->setImageScaled(TRUE);
+ if (formatted->encode(scaled, 0.f))
+ {
+ previewp->mDataSize = formatted->getDataSize();
+ formatted->decode(previewp->mPreviewImageEncoded, 0);
+ }
+ }
+ else
+ {
+ // delete any existing image
+ previewp->mFormattedImage = NULL;
+ // now create the new one of the appropriate format.
+ LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat();
+ lldebugs << "Encoding new image of format " << format << llendl;
+
+ switch(format)
+ {
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
+ previewp->mFormattedImage = new LLImagePNG();
+ break;
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
+ previewp->mFormattedImage = new LLImageJPEG(previewp->mSnapshotQuality);
+ break;
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
+ previewp->mFormattedImage = new LLImageBMP();
+ break;
+ }
+ if (previewp->mFormattedImage->encode(previewp->mPreviewImage, 0))
+ {
+ previewp->mDataSize = previewp->mFormattedImage->getDataSize();
+ // special case BMP to copy instead of decode otherwise decode will crash.
+ if(format == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP)
+ {
+ previewp->mPreviewImageEncoded->copy(previewp->mPreviewImage);
+ }
+ else
+ {
+ previewp->mFormattedImage->decode(previewp->mPreviewImageEncoded, 0);
+ }
+ }
+ }
+
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(
+ previewp->mPreviewImageEncoded->getData(),
+ previewp->mPreviewImageEncoded->getWidth(),
+ previewp->mPreviewImageEncoded->getHeight(),
+ previewp->mPreviewImageEncoded->getComponents());
+
+ if(!scaled->isBufferInvalid())
+ {
+ // leave original image dimensions, just scale up texture buffer
+ if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024)
+ {
+ // go ahead and shrink image to appropriate power of 2 for display
+ scaled->biasedScaleToPowerOfTwo(1024);
+ previewp->setImageScaled(TRUE);
+ }
+ else
+ {
+ // expand image but keep original image data intact
+ scaled->expandToPowerOfTwo(1024, FALSE);
+ }
+
+ previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE);
+ LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex];
+ gGL.getTexUnit(0)->bind(curr_preview_image);
+ if (previewp->getSnapshotType() != SNAPSHOT_TEXTURE)
+ {
+ curr_preview_image->setFilteringOption(LLTexUnit::TFO_POINT);
+ }
+ else
+ {
+ curr_preview_image->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
+ }
+ curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP);
+
+ previewp->mSnapshotUpToDate = TRUE;
+ previewp->generateThumbnailImage(TRUE) ;
+
+ previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal();
+ previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
+ }
+ }
+ previewp->getWindow()->decBusyCount();
+ // only show fullscreen preview when in freeze frame mode
+ previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame"));
+ previewp->mSnapshotDelayTimer.stop();
+ previewp->mSnapshotActive = FALSE;
+
+ if(!previewp->getThumbnailUpToDate())
+ {
+ previewp->generateThumbnailImage() ;
+ }
+ lldebugs << "done creating snapshot" << llendl;
+ LLFloaterSnapshot::postUpdate();
+ LLFloaterSocial::postUpdate();
+
+ return TRUE;
+}
+
+void LLSnapshotLivePreview::setSize(S32 w, S32 h)
+{
+ lldebugs << "setSize(" << w << ", " << h << ")" << llendl;
+ setWidth(w);
+ setHeight(h);
+}
+
+void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
+{
+ w = getWidth();
+ h = getHeight();
+}
+
+void LLSnapshotLivePreview::saveTexture()
+{
+ lldebugs << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << llendl;
+ // gen a new uuid for this asset
+ LLTransactionID tid;
+ tid.generate();
+ LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(mPreviewImage->getData(),
+ mPreviewImage->getWidth(),
+ mPreviewImage->getHeight(),
+ mPreviewImage->getComponents());
+
+ scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
+ lldebugs << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << llendl;
+
+ if (formatted->encode(scaled, 0.0f))
+ {
+ LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE);
+ std::string pos_string;
+ LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL);
+ std::string who_took_it;
+ LLAgentUI::buildFullname(who_took_it);
+ LLAssetStorage::LLStoreAssetCallback callback = NULL;
+ S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
+ void *userdata = NULL;
+ upload_new_resource(tid, // tid
+ LLAssetType::AT_TEXTURE,
+ "Snapshot : " + pos_string,
+ "Taken by " + who_took_it + " at " + pos_string,
+ 0,
+ LLFolderType::FT_SNAPSHOT_CATEGORY,
+ LLInventoryType::IT_SNAPSHOT,
+ PERM_ALL, // Note: Snapshots to inventory is a special case of content upload
+ LLFloaterPerms::getGroupPerms(), // that is more permissive than other uploads
+ LLFloaterPerms::getEveryonePerms(),
+ "Snapshot : " + pos_string,
+ callback, expected_upload_cost, userdata);
+ gViewerWindow->playSnapshotAnimAndSound();
+ }
+ else
+ {
+ LLNotificationsUtil::add("ErrorEncodingSnapshot");
+ llwarns << "Error encoding snapshot" << llendl;
+ }
+
+ LLViewerStats::getInstance()->incStat(LLViewerStats::ST_SNAPSHOT_COUNT );
+
+ mDataSize = 0;
+}
+
+BOOL LLSnapshotLivePreview::saveLocal()
+{
+ BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage);
+
+ if(success)
+ {
+ gViewerWindow->playSnapshotAnimAndSound();
+ }
+ return success;
+}
+
+void LLSnapshotLivePreview::saveWeb()
+{
+ // *FIX: Will break if the window closes because of CloseSnapshotOnKeep!
+ // Needs to pass on ownership of the image.
+ LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get());
+ if(!jpg)
+ {
+ llwarns << "Formatted image not a JPEG" << llendl;
+ return;
+ }
+
+ LLSD metadata;
+ metadata["description"] = getChild<LLLineEditor>("description")->getText();
+
+ LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(gAgentCamera.getCameraPositionGlobal(),
+ boost::bind(&LLSnapshotLivePreview::regionNameCallback, this, jpg, metadata, _1, _2, _3, _4));
+
+ gViewerWindow->playSnapshotAnimAndSound();
+}
+
+void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z)
+{
+ metadata["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString();
+
+ LLWebSharing::instance().shareSnapshot(snapshot, metadata);
+}
diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h
new file mode 100644
index 0000000000..fe3d257b02
--- /dev/null
+++ b/indra/newview/llsnapshotlivepreview.h
@@ -0,0 +1,164 @@
+/**
+* @file llsnapshotlivepreview.h
+* @brief Header file for llsnapshotlivepreview
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* 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_LLSNAPSHOTLIVEPREVIEW_H
+#define LL_LLSNAPSHOTLIVEPREVIEW_H
+
+#include "llpanelsnapshot.h"
+#include "llviewerwindow.h"
+
+class LLImageJPEG;
+
+///----------------------------------------------------------------------------
+/// Class LLSnapshotLivePreview
+///----------------------------------------------------------------------------
+class LLSnapshotLivePreview : public LLView
+{
+ LOG_CLASS(LLSnapshotLivePreview);
+public:
+ enum ESnapshotType
+ {
+ SNAPSHOT_POSTCARD,
+ SNAPSHOT_TEXTURE,
+ SNAPSHOT_LOCAL,
+ SNAPSHOT_WEB
+ };
+
+
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Params()
+ {
+ name = "snapshot_live_preview";
+ mouse_opaque = false;
+ }
+ };
+
+
+ LLSnapshotLivePreview(const LLSnapshotLivePreview::Params& p);
+ ~LLSnapshotLivePreview();
+
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ void setSize(S32 w, S32 h);
+ void setWidth(S32 w) { mWidth[mCurImageIndex] = w; }
+ void setHeight(S32 h) { mHeight[mCurImageIndex] = h; }
+ void getSize(S32& w, S32& h) const;
+ S32 getWidth() const { return mWidth[mCurImageIndex]; }
+ S32 getHeight() const { return mHeight[mCurImageIndex]; }
+ S32 getDataSize() const { return mDataSize; }
+ void setMaxImageSize(S32 size) ;
+ S32 getMaxImageSize() {return mMaxImageSize ;}
+
+ ESnapshotType getSnapshotType() const { return mSnapshotType; }
+ LLFloaterSnapshot::ESnapshotFormat getSnapshotFormat() const { return mSnapshotFormat; }
+ BOOL getSnapshotUpToDate() const { return mSnapshotUpToDate; }
+ BOOL isSnapshotActive() { return mSnapshotActive; }
+ LLViewerTexture* getThumbnailImage() const { return mThumbnailImage ; }
+ S32 getThumbnailWidth() const { return mThumbnailWidth ; }
+ S32 getThumbnailHeight() const { return mThumbnailHeight ; }
+ BOOL getThumbnailLock() const { return mThumbnailUpdateLock ; }
+ BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;}
+ LLViewerTexture* getCurrentImage();
+ F32 getImageAspect();
+ F32 getAspect() ;
+ const LLRect& getImageRect() const { return mImageRect[mCurImageIndex]; }
+ BOOL isImageScaled() const { return mImageScaled[mCurImageIndex]; }
+ void setImageScaled(BOOL scaled) { mImageScaled[mCurImageIndex] = scaled; }
+ const LLVector3d& getPosTakenGlobal() const { return mPosTakenGlobal; }
+
+ void setSnapshotType(ESnapshotType type) { mSnapshotType = type; }
+ void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; }
+ void setSnapshotQuality(S32 quality);
+ void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
+ void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f);
+ void saveWeb();
+ void saveTexture();
+ BOOL saveLocal();
+
+ LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; }
+ LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; }
+
+ /// Sets size of preview thumbnail image and thhe surrounding rect.
+ void setThumbnailPlaceholderRect(const LLRect& rect) {mThumbnailPlaceholderRect = rect; }
+ BOOL setThumbnailImageSize() ;
+ void generateThumbnailImage(BOOL force_update = FALSE) ;
+ void resetThumbnailImage() { mThumbnailImage = NULL ; }
+ void drawPreviewRect(S32 offset_x, S32 offset_y) ;
+
+ // Returns TRUE when snapshot generated, FALSE otherwise.
+ static BOOL onIdle( void* snapshot_preview );
+
+ // callback for region name resolve
+ void regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z);
+
+private:
+ LLColor4 mColor;
+ LLPointer<LLViewerTexture> mViewerImage[2]; //used to represent the scene when the frame is frozen.
+ LLRect mImageRect[2];
+ S32 mWidth[2];
+ S32 mHeight[2];
+ BOOL mImageScaled[2];
+ S32 mMaxImageSize ;
+
+ //thumbnail image
+ LLPointer<LLViewerTexture> mThumbnailImage ;
+ S32 mThumbnailWidth ;
+ S32 mThumbnailHeight ;
+ LLRect mPreviewRect ;
+ BOOL mThumbnailUpdateLock ;
+ BOOL mThumbnailUpToDate ;
+ LLRect mThumbnailPlaceholderRect;
+
+ S32 mCurImageIndex;
+ LLPointer<LLImageRaw> mPreviewImage;
+ LLPointer<LLImageRaw> mPreviewImageEncoded;
+ LLPointer<LLImageFormatted> mFormattedImage;
+ LLFrameTimer mSnapshotDelayTimer;
+ S32 mShineCountdown;
+ LLFrameTimer mShineAnimTimer;
+ F32 mFlashAlpha;
+ BOOL mNeedsFlash;
+ LLVector3d mPosTakenGlobal;
+ S32 mSnapshotQuality;
+ S32 mDataSize;
+ ESnapshotType mSnapshotType;
+ LLFloaterSnapshot::ESnapshotFormat mSnapshotFormat;
+ BOOL mSnapshotUpToDate;
+ LLFrameTimer mFallAnimTimer;
+ LLVector3 mCameraPos;
+ LLQuaternion mCameraRot;
+ BOOL mSnapshotActive;
+ LLViewerWindow::ESnapshotType mSnapshotBufferType;
+
+public:
+ static std::set<LLSnapshotLivePreview*> sList;
+ BOOL mKeepAspectRatio ;
+};
+
+#endif // LL_LLSNAPSHOTLIVEPREVIEW_H
+
diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp
index 1a3add2bfb..626d69aca4 100755
--- a/indra/newview/llsyswellwindow.cpp
+++ b/indra/newview/llsyswellwindow.cpp
@@ -118,6 +118,11 @@ void LLSysWellWindow::removeItemByID(const LLUUID& id)
}
}
+ LLPanel * LLSysWellWindow::findItemByID(const LLUUID& id)
+{
+ return mMessageList->getItemByValue(id);
+}
+
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void LLSysWellWindow::initChannel()
diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h
index cc5c057d8b..71b41476f5 100755
--- a/indra/newview/llsyswellwindow.h
+++ b/indra/newview/llsyswellwindow.h
@@ -55,6 +55,7 @@ public:
// Operating with items
void removeItemByID(const LLUUID& id);
+ LLPanel * findItemByID(const LLUUID& id);
// Operating with outfit
virtual void setVisible(BOOL visible);
diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp
index 09ab31df36..bdbd8f1f83 100755
--- a/indra/newview/lltoastimpanel.cpp
+++ b/indra/newview/lltoastimpanel.cpp
@@ -93,7 +93,7 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif
if(!mIsGroupMsg)
{
- mAvatarName->setValue(p.from);
+ mAvatarName->setValue(p.from);
}
mTime->setValue(p.time);
mSessionID = p.session_id;
@@ -164,7 +164,7 @@ void LLToastIMPanel::spawnNameToolTip()
params.background_visible(false);
if(!mIsGroupMsg)
{
- params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE));
+ params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE));
}
else
{
diff --git a/indra/newview/llurlhistory.cpp b/indra/newview/llurlhistory.cpp
index dd17068be5..a80b9da13c 100755
--- a/indra/newview/llurlhistory.cpp
+++ b/indra/newview/llurlhistory.cpp
@@ -103,22 +103,29 @@ LLSD LLURLHistory::getURLHistory(const std::string& collection)
// static
void LLURLHistory::addURL(const std::string& collection, const std::string& url)
{
- if(! url.empty())
+ if(!url.empty())
{
- sHistorySD[collection].insert(0, url);
+ LLURI u(url);
+ std::string simplified_url = u.scheme() + "://" + u.authority() + u.path();
+ sHistorySD[collection].insert(0, simplified_url);
LLURLHistory::limitSize(collection);
}
}
// static
void LLURLHistory::removeURL(const std::string& collection, const std::string& url)
{
- for(int index = 0; index < sHistorySD[collection].size(); index++)
+ if(!url.empty())
{
- if(sHistorySD[collection].get(index).asString() == url)
- {
- sHistorySD[collection].erase(index);
- }
- }
+ LLURI u(url);
+ std::string simplified_url = u.scheme() + "://" + u.authority() + u.path();
+ for(int index = 0; index < sHistorySD[collection].size(); index++)
+ {
+ if(sHistorySD[collection].get(index).asString() == simplified_url)
+ {
+ sHistorySD[collection].erase(index);
+ }
+ }
+ }
}
// static
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index c6b28b9e5e..4ce049df03 100755
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -103,6 +103,7 @@
#include "llfloatersettingsdebug.h"
#include "llfloatersidepanelcontainer.h"
#include "llfloatersnapshot.h"
+#include "llfloatersocial.h"
#include "llfloatersounddevices.h"
#include "llfloaterspellchecksettings.h"
#include "llfloatertelehub.h"
@@ -303,6 +304,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("sell_land", "floater_sell_land.xml", &LLFloaterSellLand::buildFloater);
LLFloaterReg::add("settings_debug", "floater_settings_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSettingsDebug>);
LLFloaterReg::add("sound_devices", "floater_sound_devices.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundDevices>);
+ LLFloaterReg::add("social", "floater_social.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSocial>);
LLFloaterReg::add("stats", "floater_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
LLFloaterReg::add("start_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRunQueue>);
LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>);
@@ -311,7 +313,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("my_profile", "floater_my_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create);
LLFloaterReg::add("profile", "floater_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create);
LLFloaterReg::add("how_to", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
-
+ LLFloaterReg::add("fbc_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterUIPreviewUtil::registerFloater();
LLFloaterReg::add("upload_anim_bvh", "floater_animation_bvh_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBvhPreview>, "upload");
diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp
index 2fbb3bf868..aa9de076b8 100755
--- a/indra/newview/llviewerkeyboard.cpp
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -687,7 +687,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/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 2df028de69..13483790ed 100755
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -2000,7 +2000,12 @@ void LLViewerMediaImpl::loadURI()
"<>#%"
";/?:@&=",
false);
- llinfos << "Asking media source to load URI: " << uri << llendl;
+ {
+ // Do not log the query parts
+ LLURI u(uri);
+ std::string sanitized_uri = (u.query().empty() ? uri : u.scheme() + "://" + u.authority() + u.path());
+ llinfos << "Asking media source to load URI: " << sanitized_uri << llendl;
+ }
mMediaSource->loadURI( uri );
@@ -2567,7 +2572,12 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi
if(mPriority == LLPluginClassMedia::PRIORITY_UNLOADED)
{
// Helpful to have media urls in log file. Shouldn't be spammy.
- llinfos << "NOT LOADING media id= " << mTextureId << " url=" << url << " mime_type=" << mime_type << llendl;
+ {
+ // Do not log the query parts
+ LLURI u(url);
+ std::string sanitized_url = (u.query().empty() ? url : u.scheme() + "://" + u.authority() + u.path());
+ llinfos << "NOT LOADING media id= " << mTextureId << " url=" << sanitized_url << ", mime_type=" << mime_type << llendl;
+ }
// This impl should not be loaded at this time.
LL_DEBUGS("PluginPriority") << this << "Not loading (PRIORITY_UNLOADED)" << LL_ENDL;
@@ -2582,7 +2592,12 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi
void LLViewerMediaImpl::navigateInternal()
{
// Helpful to have media urls in log file. Shouldn't be spammy.
- llinfos << "media id= " << mTextureId << " url=" << mMediaURL << " mime_type=" << mMimeType << llendl;
+ {
+ // Do not log the query parts
+ LLURI u(mMediaURL);
+ std::string sanitized_url = (u.query().empty() ? mMediaURL : u.scheme() + "://" + u.authority() + u.path());
+ llinfos << "media id= " << mTextureId << " url=" << sanitized_url << ", mime_type=" << mMimeType << llendl;
+ }
if(mNavigateSuspended)
{
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 9468a2d542..ac2940fcfc 100755
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -40,11 +40,13 @@
#include "llinventorypanel.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
+#include "llviewereventrecorder.h"
// newview includes
#include "llagent.h"
#include "llagentaccess.h"
#include "llagentcamera.h"
+#include "llagentui.h"
#include "llagentwearables.h"
#include "llagentpilot.h"
#include "llcompilequeue.h"
@@ -52,6 +54,7 @@
#include "lldaycyclemanager.h"
#include "lldebugview.h"
#include "llenvmanager.h"
+#include "llfacebookconnect.h"
#include "llfilepicker.h"
#include "llfirstuse.h"
#include "llfloaterbuy.h"
@@ -1952,6 +1955,43 @@ class LLAdvancedDropPacket : public view_listener_t
};
+////////////////////
+// EVENT Recorder //
+///////////////////
+
+
+class LLAdvancedViewerEventRecorder : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ std::string command = userdata.asString();
+ if ("start playback" == command)
+ {
+ llinfos << "Event Playback starting" << llendl;
+ LLViewerEventRecorder::instance().playbackRecording();
+ llinfos << "Event Playback completed" << llendl;
+ }
+ else if ("stop playback" == command)
+ {
+ // Future
+ }
+ else if ("start recording" == command)
+ {
+ LLViewerEventRecorder::instance().setEventLoggingOn();
+ llinfos << "Event recording started" << llendl;
+ }
+ else if ("stop recording" == command)
+ {
+ LLViewerEventRecorder::instance().setEventLoggingOff();
+ llinfos << "Event recording stopped" << llendl;
+ }
+
+ return true;
+ }
+};
+
+
+
/////////////////
// AGENT PILOT //
@@ -8420,6 +8460,8 @@ void initialize_menus()
// Don't prepend MenuName.Foo because these can be used in any menu.
enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service));
+ enable.add("displayViewerEventRecorderMenuItems",boost::bind(&LLViewerEventRecorder::displayViewerEventRecorderMenuItems,&LLViewerEventRecorder::instance()));
+
view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts");
enable.add("Conversation.IsConversationLoggingAllowed", boost::bind(&LLFloaterIMContainer::isConversationLoggingAllowed));
@@ -8678,6 +8720,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 c38af1f990..ab9ea5618e 100755
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2220,7 +2220,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
@@ -2988,6 +2988,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
break;
case IM_LURE_USER:
+ case IM_TELEPORT_REQUEST:
{
if (is_muted)
{
@@ -3010,7 +3011,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);
@@ -3082,12 +3084,22 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
}
else
{
- LLNotification::Params params("TeleportOffered");
+ LLNotification::Params params;
+ if (IM_LURE_USER == dialog)
+ {
+ params.name = "TeleportOffered";
+ params.functor.name = "TeleportOffered";
+ }
+ else if (IM_TELEPORT_REQUEST == dialog)
+ {
+ params.name = "TeleportRequest";
+ params.functor.name = "TeleportRequest";
+ }
+
params.substitutions = args;
params.payload = payload;
LLPostponedNotification::add<LLPostponedOfferNotification>( params, from_id, false);
}
-
}
}
break;
@@ -6873,6 +6885,51 @@ void send_group_notice(const LLUUID& group_id,
bin_bucket_size);
}
+void send_lures(const LLSD& notification, const LLSD& response)
+{
+ std::string text = response["message"].asString();
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl);
+ text.append("\r\n").append(slurl.getSLURLString());
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_StartLure);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in.
+ msg->addStringFast(_PREHASH_Message, text);
+ for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray();
+ it != notification["payload"]["ids"].endArray();
+ ++it)
+ {
+ LLUUID target_id = it->asUUID();
+
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_TargetID, target_id);
+
+ // Record the offer.
+ {
+ std::string target_name;
+ gCacheName->getFullName(target_id, target_name); // for im log filenames
+ LLSD args;
+ args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();;
+
+ LLSD payload;
+
+ //*TODO please rewrite all keys to the same case, lower or upper
+ payload["from_id"] = target_id;
+ payload["SUPPRESS_TOAST"] = true;
+ LLNotificationsUtil::add("TeleportOfferSent", args, payload);
+
+ // Add the recepient to the recent people list.
+ LLRecentPeople::instance().add(target_id);
+ }
+ }
+ gAgent.sendReliableMessage();
+}
+
bool handle_lure_callback(const LLSD& notification, const LLSD& response)
{
static const unsigned OFFER_RECIPIENT_LIMIT = 250;
@@ -6886,50 +6943,12 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response)
LLNotificationsUtil::add("TooManyTeleportOffers", args);
return false;
}
-
- std::string text = response["message"].asString();
- LLSLURL slurl;
- LLAgentUI::buildSLURL(slurl);
- text.append("\r\n").append(slurl.getSLURLString());
+
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if(0 == option)
{
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_StartLure);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_Info);
- msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in.
- msg->addStringFast(_PREHASH_Message, text);
- for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray();
- it != notification["payload"]["ids"].endArray();
- ++it)
- {
- LLUUID target_id = it->asUUID();
-
- msg->nextBlockFast(_PREHASH_TargetData);
- msg->addUUIDFast(_PREHASH_TargetID, target_id);
-
- // Record the offer.
- {
- std::string target_name;
- gCacheName->getFullName(target_id, target_name); // for im log filenames
- LLSD args;
- args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();;
-
- LLSD payload;
-
- //*TODO please rewrite all keys to the same case, lower or upper
- payload["from_id"] = target_id;
- LLNotificationsUtil::add("TeleportOfferSent", args, payload);
-
- // Add the recepient to the recent people list.
- LLRecentPeople::instance().add(target_id);
- }
- }
- gAgent.sendReliableMessage();
+ send_lures(notification, response);
}
return false;
@@ -6969,6 +6988,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 66615657d8..b4e287c446 100755
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -954,15 +954,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
- LLViewerTextureAnim::updateClass();
- }
+ if (gAnimateTextures)
+ {
+ LLViewerTextureAnim::updateClass();
+ }
+ }
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 678f24fb3c..460f9084d9 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1595,6 +1595,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("EnvironmentSettings");
capabilityNames.append("EstateChangeInfo");
capabilityNames.append("EventQueueGet");
+ capabilityNames.append("FacebookConnect");
+ //capabilityNames.append("FacebookRedirect");
if (gSavedSettings.getBOOL("UseHTTPInventory"))
{
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index bf11bce3da..ff7642a773 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -198,6 +198,8 @@
#include "llagentui.h"
#include "llwearablelist.h"
+#include "llviewereventrecorder.h"
+
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llnotificationmanager.h"
@@ -954,27 +956,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) )
@@ -982,27 +975,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;
@@ -1373,7 +1383,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
}
@@ -2519,6 +2530,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;
}
@@ -2533,12 +2546,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;
}
}
@@ -2548,6 +2563,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
@@ -2562,11 +2578,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;
}
@@ -2606,18 +2624,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;
}
@@ -2625,6 +2652,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;
}
@@ -2673,7 +2702,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
// HACK: Numeric keypad <enter> on Mac is Unicode 3
// HACK: Control-M on Windows is Unicode 13
if ((uni_char == 13 && mask != MASK_CONTROL)
- || (uni_char == 3 && mask == MASK_NONE))
+ || (uni_char == 3 && mask == MASK_NONE) )
{
if (mask != MASK_ALT)
{
@@ -2696,14 +2725,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;
@@ -2712,8 +2734,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/llwebprofile.cpp b/indra/newview/llwebprofile.cpp
index 641f338f2c..69255af179 100755
--- a/indra/newview/llwebprofile.cpp
+++ b/indra/newview/llwebprofile.cpp
@@ -148,9 +148,6 @@ public:
LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << llendl;
LLWebProfile::reportImageUploadStatus(true);
}
-
-private:
- LLPointer<LLImageFormatted> mImagep;
};
@@ -172,7 +169,7 @@ public:
headers["Cookie"] = LLWebProfile::getAuthCookie();
const std::string& redir_url = content["location"];
LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << llendl;
- LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers);
+ LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder(), headers);
}
else
{
diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp
index 28f959eb71..a8e06511d7 100755
--- a/indra/newview/llwindowlistener.cpp
+++ b/indra/newview/llwindowlistener.cpp
@@ -265,7 +265,9 @@ void LLWindowListener::getPaths(LLSD const & request)
void LLWindowListener::keyDown(LLSD const & evt)
{
Response response(LLSD(), evt);
-
+ KEY key = getKEY(evt);
+ MASK mask = getMask(evt);
+
if (evt.has("path"))
{
std::string path(evt["path"]);
@@ -280,8 +282,6 @@ void LLWindowListener::keyDown(LLSD const & evt)
response.setResponse(target_view->getInfo());
gFocusMgr.setKeyboardFocus(target_view);
- KEY key = getKEY(evt);
- MASK mask = getMask(evt);
gViewerKeyboard.handleKey(key, mask, false);
if(key < 0x80) mWindow->handleUnicodeChar(key, mask);
}
@@ -294,7 +294,8 @@ void LLWindowListener::keyDown(LLSD const & evt)
}
else
{
- mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt));
+ gViewerKeyboard.handleKey(key, mask, false);
+ if(key < 0x80) mWindow->handleUnicodeChar(key, mask);
}
}
diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp
index 5fa380e0e3..bfae142812 100755
--- a/indra/newview/llworldmap.cpp
+++ b/indra/newview/llworldmap.cpp
@@ -522,6 +522,17 @@ bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID&
tooltip_fmt.setArg("[AREA]", llformat("%d", extra));
tooltip_fmt.setArg("[PRICE]", llformat("%d", extra2));
+
+ // Check for division by zero
+ if (extra != 0)
+ {
+ tooltip_fmt.setArg("[SQMPRICE]", llformat("%.1f", (F32)extra2 / (F32)extra));
+ }
+ else
+ {
+ tooltip_fmt.setArg("[SQMPRICE]", LLTrans::getString("Unknown"));
+ }
+
new_item.setTooltip(tooltip_fmt.getString());
if (type == MAP_ITEM_LAND_FOR_SALE)
diff --git a/indra/newview/skins/default/textures/icons/Facebook.png b/indra/newview/skins/default/textures/icons/Facebook.png
new file mode 100644
index 0000000000..8287d56f88
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Facebook.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/map_placeholder.png b/indra/newview/skins/default/textures/icons/map_placeholder.png
new file mode 100644
index 0000000000..31e457aa75
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/map_placeholder.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 54f60f4441..94c187e21a 100755
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -148,6 +148,7 @@ with the same filename but different name
<texture name="Command_Preferences_Icon" file_name="toolbar_icons/preferences.png" preload="true" />
<texture name="Command_Profile_Icon" file_name="toolbar_icons/profile.png" preload="true" />
<texture name="Command_Search_Icon" file_name="toolbar_icons/search.png" preload="true" />
+ <texture name="Command_Social_Icon" file_name="toolbar_icons/facebook.png" preload="true" />
<texture name="Command_Snapshot_Icon" file_name="toolbar_icons/snapshot.png" preload="true" />
<texture name="Command_Speak_Icon" file_name="toolbar_icons/speak.png" preload="true" />
<texture name="Command_View_Icon" file_name="toolbar_icons/view.png" preload="true" />
@@ -199,6 +200,8 @@ with the same filename but different name
<texture name="ExternalBrowser_Off" file_name="icons/ExternalBrowser_Off.png" preload="false" />
<texture name="Edit_Wrench" file_name="icons/Edit_Wrench.png" preload="false" />
+ <texture name="Facebook_Icon" file_name="icons/Facebook.png" preload="false" />
+
<texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" />
<texture name="Favorite_Star_Off" file_name="navbar/Favorite_Star_Off.png" preload="false" />
<texture name="Favorite_Star_Press" file_name="navbar/Favorite_Star_Press.png" preload="false" />
@@ -323,6 +326,8 @@ with the same filename but different name
<texture name="Locked_Icon" file_name="icons/Locked_Icon.png" preload="false" />
+ <texture name="Map_Placeholder_Icon" file_name="icons/map_placeholder.png" preload="true" />
+
<texture name="MarketplaceBtn_Off" file_name="widgets/MarketplaceBtn_Off.png" preload="true" scale.left="30" scale.top="19" scale.right="35" scale.bottom="4" />
<texture name="MarketplaceBtn_Selected" file_name="widgets/MarketplaceBtn_Selected.png" preload="true" scale.left="30" scale.top="19" scale.right="35" scale.bottom="4" />
@@ -565,6 +570,7 @@ with the same filename but different name
<texture name="Snapshot_Email" file_name="snapshot_email.png" preload="false" />
<texture name="Snapshot_Inventory" file_name="toolbar_icons/inventory.png" preload="false" />
<texture name="Snapshot_Profile" file_name="toolbar_icons/profile.png" preload="false" />
+ <texture name="Snapshot_Facebook" file_name="toolbar_icons/facebook.png" preload="false" />
<texture name="startup_logo" file_name="windows/startup_logo.png" preload="true" />
diff --git a/indra/newview/skins/default/textures/toolbar_icons/facebook.png b/indra/newview/skins/default/textures/toolbar_icons/facebook.png
new file mode 100644
index 0000000000..b960b834dc
--- /dev/null
+++ b/indra/newview/skins/default/textures/toolbar_icons/facebook.png
Binary files differ
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index c8c20424e1..4268b95370 100755
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -1274,7 +1274,7 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden.
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
</string>
<string name="MarketplaceURL_CreateStore">
- http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4
+ http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3
</string>
<string name="MarketplaceURL_Dashboard">
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard
diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml
index 793a6e6fa1..a660e812cc 100755
--- a/indra/newview/skins/default/xui/en/floater_about_land.xml
+++ b/indra/newview/skins/default/xui/en/floater_about_land.xml
@@ -1365,7 +1365,7 @@ Only large parcels can be listed in search.
<combo_box.item
label="Any Category"
name="item0"
- value="any" />
+ value="none" />
<combo_box.item
label="Linden Location"
name="item1"
diff --git a/indra/newview/skins/default/xui/en/floater_fbc_web.xml b/indra/newview/skins/default/xui/en/floater_fbc_web.xml
new file mode 100644
index 0000000000..0d35e22a19
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_fbc_web.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater name="floater_fbc_web"
+ help_topic="fbc_web"
+ width="780"
+ height="775"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="false"
+ filename="floater_web_content.xml"/>
diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml
index 49d64767cc..853c209bca 100755
--- a/indra/newview/skins/default/xui/en/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml
@@ -21,7 +21,11 @@
Sending Email
</string>
<string
- name="profile_progress_str">
+ name="facebook_progress_str">
+ Posting to Facebook
+ </string>
+ <string
+ name="profile_progress_str">
Posting
</string>
<string
@@ -33,7 +37,11 @@
Saving to Computer
</string>
<string
- name="profile_succeeded_str">
+ name="facebook_succeeded_str">
+ Image uploaded
+ </string>
+ <string
+ name="profile_succeeded_str">
Image uploaded
</string>
<string
@@ -49,7 +57,11 @@
Saved to Computer!
</string>
<string
- name="profile_failed_str">
+ name="facebook_failed_str">
+ Failed to upload image to your Facebook timeline.
+ </string>
+ <string
+ name="profile_failed_str">
Failed to upload image to your Profile Feed.
</string>
<string
diff --git a/indra/newview/skins/default/xui/en/floater_social.xml b/indra/newview/skins/default/xui/en/floater_social.xml
new file mode 100644
index 0000000000..b7ff374d5f
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_social.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<floater
+ positioning="cascading"
+ can_close="true"
+ can_resize="false"
+ help_topic="floater_social"
+ layout="topleft"
+ name="floater_social"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="POST TO FACEBOOK"
+ height="482"
+ width="304">
+ <panel
+ height="482"
+ width="304"
+ visible="true"
+ name="background"
+ follows="all"
+ top="0"
+ left="0">
+ <tab_container
+ name="tabs"
+ tab_group="1"
+ tab_min_width="70"
+ tab_height="30"
+ tab_position="top"
+ top="7"
+ height="437"
+ halign="center">
+ <panel
+ filename="panel_social_status.xml"
+ class="llsocialstatuspanel"
+ follows="all"
+ label="STATUS"
+ name="panel_social_status"/>
+ <panel
+ filename="panel_social_photo.xml"
+ class="llsocialphotopanel"
+ follows="all"
+ label="PHOTO"
+ name="panel_social_photo"/>
+ <panel
+ filename="panel_social_place.xml"
+ class="llsocialcheckinpanel"
+ follows="all"
+ label="CHECK IN"
+ name="panel_social_place"/>
+ <panel
+ filename="panel_social_account.xml"
+ class="llsocialaccountpanel"
+ follows="all"
+ label="ACCOUNT"
+ name="panel_social_account"/>
+ </tab_container>
+ <panel
+ name="connection_status_panel"
+ follows="left|bottom|right"
+ height="24">
+ <text
+ name="connection_error_text"
+ type="string"
+ follows="left|bottom|right"
+ top="5"
+ left="9"
+ width="250"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ text_color="DrYellow"
+ font="SansSerif">
+ Error
+ </text>
+ <loading_indicator
+ follows="left|bottom|right"
+ height="24"
+ width="24"
+ name="connection_loading_indicator"
+ top="2"
+ left="9"
+ visible="true"/>
+ <text
+ name="connection_loading_text"
+ type="string"
+ follows="left|bottom|right"
+ top="5"
+ left_pad="5"
+ width="250"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ text_color="EmphasisColor"
+ font="SansSerif">
+ Loading...
+ </text>
+ </panel>
+ </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_web_content.xml b/indra/newview/skins/default/xui/en/floater_web_content.xml
index cea10adca8..a80440e844 100755
--- a/indra/newview/skins/default/xui/en/floater_web_content.xml
+++ b/indra/newview/skins/default/xui/en/floater_web_content.xml
@@ -125,10 +125,10 @@
<icon
name="media_secure_lock_flag"
height="16"
- follows="top|right"
+ follows="top|left"
image_name="Lock2"
layout="topleft"
- left_delta="620"
+ left_delta="2"
top_delta="2"
visible="false"
tool_tip="Secured Browsing"
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
@@ -53,6 +53,13 @@
<on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/>
</menu_item_call>
<menu_item_call
+ label="Request teleport"
+ layout="topleft"
+ name="request_teleport">
+ <on_click function="Avatar.DoToSelected" parameter="request_teleport"/>
+ <on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/>
+ </menu_item_call>
+ <menu_item_call
label="Voice call"
layout="topleft"
name="voice_call">
diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml
index 8796b87955..a1a3afbf68 100755
--- a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml
@@ -56,6 +56,16 @@
function="Calllog.Enable"
parameter="can_offer_teleport"/>
</menu_item_call>
+ <menu_item_call
+ label="Request Teleport"
+ name="request_teleport">
+ <on_click
+ function="Calllog.Action"
+ parameter="request_teleport"/>
+ <on_enable
+ function="Calllog.Enable"
+ parameter="can_offer_teleport"/>
+ </menu_item_call>
<menu_item_separator />
<menu_item_call
label="Add Friend"
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index 13dc0b941a..512205ba43 100755
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -591,6 +591,14 @@
parameter="lure" />
</menu_item_call>
<menu_item_call
+ label="Request Teleport..."
+ layout="topleft"
+ name="Request Teleport...">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="request_lure" />
+ </menu_item_call>
+ <menu_item_call
label="Start Conference Chat"
layout="topleft"
name="Conference Chat">
diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml
index 3abb5f7bc8..f12226ebeb 100755
--- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml
+++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml
@@ -29,6 +29,15 @@
parameter="can_offer_teleport"/>
</menu_item_call>
<menu_item_call
+ label="Request Teleport"
+ name="request_teleport">
+ <menu_item_call.on_click
+ function="Avatar.TeleportRequest"/>
+ <menu_item_call.on_enable
+ function="Avatar.EnableItem"
+ parameter="can_offer_teleport"/>
+ </menu_item_call>
+ <menu_item_call
label="Voice call"
layout="topleft"
name="voice_call">
@@ -134,5 +143,4 @@
function="Avatar.EnableItem"
parameter="can_block" />
</menu_item_check>
- <menu_item_separator />
</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 2d65052def..d635b8ee93 100755
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -16,6 +16,14 @@
parameter="agent" />
</menu_item_call>
<menu_item_call
+ label="Post to Facebook..."
+ name="PostToFacebook">
+ <menu_item_call.on_click
+ function="Floater.Toggle"
+ parameter="social"/>
+ </menu_item_call>
+ <menu_item_separator/>
+ <menu_item_call
label="Appearance..."
name="ChangeOutfit">
<menu_item_call.on_click
@@ -189,7 +197,7 @@
name="Preferences"
shortcut="control|P">
<menu_item_call.on_click
- function="Floater.Show"
+ function="Floater.Toggle"
parameter="preferences" />
</menu_item_call>
<menu_item_call
@@ -2947,6 +2955,34 @@
label="Recorder"
name="Recorder"
tear_off="true">
+ <menu_item_call visible="false"
+ label="Start event recording"
+ name="Start event recording">
+ <menu_item_call.on_visible
+ function="displayViewerEventRecorderMenuItems" />
+ <menu_item_call.on_click
+ function="Advanced.EventRecorder"
+ parameter="start recording" />
+ </menu_item_call>
+ <menu_item_call visible="false"
+ label="Stop event recording"
+ name="Stop event recording">
+ <menu_item_call.on_visible
+ function="displayViewerEventRecorderMenuItems" />
+ <menu_item_call.on_click
+ function="Advanced.EventRecorder"
+ parameter="stop recording" />
+ </menu_item_call>
+ <menu_item_call visible="false"
+ label="Playback event recording"
+ name="Playback event recording">
+ <menu_item_call.on_visible
+ function="displayViewerEventRecorderMenuItems" />
+ <menu_item_call.on_click
+ function="Advanced.EventRecorder"
+ parameter="start playback" />
+ </menu_item_call>
+
<menu_item_call
label="Start Playback"
name="Start Playback">
@@ -3049,6 +3085,13 @@
parameter="http://google.com"/>
</menu_item_call>
<menu_item_call
+ label="FB Connect Test"
+ name="FB Connect Test">
+ <menu_item_call.on_click
+ function="Advanced.WebContentTest"
+ parameter="https://cryptic-ridge-1632.herokuapp.com/"/>
+ </menu_item_call>
+ <menu_item_call
label="Dump SelectMgr"
name="Dump SelectMgr">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 69b7fe5a75..e7c89db069 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -4071,6 +4071,27 @@ Join me in [REGION]
<notification
icon="alertmodal.tga"
+ name="TeleportRequestPrompt"
+ type="alertmodal">
+Request a teleport to [NAME] with the following message
+ <tag>confirm</tag>
+ <form name="form">
+ <input name="message" type="text">
+
+ </input>
+ <button
+ default="true"
+ index="0"
+ name="OK"
+ text="OK"/>
+ <button
+ index="1"
+ name="Cancel"
+ text="Cancel"/>
+ </form>
+ </notification>
+ <notification
+ icon="alertmodal.tga"
name="TooManyTeleportOffers"
type="alertmodal">
You attempted to make [OFFERS] teleport offers
@@ -6017,6 +6038,13 @@ Please select at least one type of content to search (General, Moderate, or Adul
[MESSAGE]
</notification>
+ <notification
+ icon="notify.tga"
+ name="FacebookConnect"
+ type="notifytip">
+[MESSAGE]
+ </notification>
+
<notification
icon="notify.tga"
name="PaymentReceived"
@@ -6637,7 +6665,7 @@ Your object named &lt;nolink&gt;[OBJECTFROMNAME]&lt;/nolink&gt; has given you th
sound="UISndNewIncomingIMSession">
[NAME_SLURL] has offered to teleport you to their location:
-“[MESSAGE]”
+"[MESSAGE]”
&lt;icon&gt;[MATURITY_ICON]&lt;/icon&gt; - [MATURITY_STR]
<tag>confirm</tag>
<form name="form">
@@ -6702,6 +6730,27 @@ However, this region contains content accessible to adults only.
Teleport offer sent to [TO_NAME]
</notification>
+ <notification
+ icon="notify.tga"
+ name="TeleportRequest"
+ log_to_im="true"
+ type="offer">
+[NAME_SLURL] is requesting to be teleported to your location.
+[MESSAGE]
+
+Offer a teleport?
+ <tag>confirm</tag>
+ <form name="form">
+ <button
+ index="0"
+ name="Yes"
+ text="Yes"/>
+ <button
+ index="1"
+ name="No"
+ text="No"/>
+ </form>
+ </notification>
<notification
icon="notify.tga"
@@ -6750,7 +6799,6 @@ However, this region contains content accessible to adults only.
icon="notify.tga"
name="FriendshipOffered"
log_to_im="true"
- show_toast="false"
type="notify">
<tag>friendship</tag>
You have offered friendship to [TO_NAME]
@@ -6800,7 +6848,6 @@ However, this region contains content accessible to adults only.
icon="notify.tga"
name="FriendshipAcceptedByMe"
log_to_im="true"
- show_toast="false"
type="notify">
<tag>friendship</tag>
Friendship offer accepted.
@@ -6810,7 +6857,6 @@ Friendship offer accepted.
icon="notify.tga"
name="FriendshipDeclinedByMe"
log_to_im="true"
- show_toast="false"
type="notify">
<tag>friendship</tag>
Friendship offer declined.
diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml
index ed274d0233..dc0e4a5947 100755
--- a/indra/newview/skins/default/xui/en/panel_people.xml
+++ b/indra/newview/skins/default/xui/en/panel_people.xml
@@ -365,6 +365,23 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M
top="0"
width="307" />
</accordion_tab>
+ <accordion_tab
+ layout="topleft"
+ height="173"
+ name="tab_suggested_friends"
+ title="People you may want to friend">
+ <avatar_list
+ ignore_online_status="true"
+ allow_select="true"
+ follows="all"
+ height="173"
+ layout="topleft"
+ left="0"
+ name="suggested_friends"
+ show_permissions_granted="true"
+ top="0"
+ width="307" />
+ </accordion_tab>
</accordion>
<text
follows="all"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_move.xml b/indra/newview/skins/default/xui/en/panel_preferences_move.xml
index d9067b41c7..8794e3bf95 100755
--- a/indra/newview/skins/default/xui/en/panel_preferences_move.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_move.xml
@@ -129,6 +129,16 @@
name="tap_tap_hold_to_run"
width="237"
top_pad="0"/>
+ <check_box
+ control_name="AutomaticFly"
+ follows="left|top"
+ height="20"
+ label="Hold jump or crouch key to start or stop flying"
+ layout="topleft"
+ left_delta="0"
+ name="automatic_fly"
+ width="237"
+ top_pad="0"/>
<text
follows="left|top"
type="string"
@@ -260,4 +270,4 @@
function="Floater.Show"
parameter="pref_joystick" />
</button>
-</panel> \ No newline at end of file
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
index 198ccd6e2f..8f90521bb2 100755
--- a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
+++ b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml
@@ -315,7 +315,18 @@
<line_editor.commit_callback
function="MediaCtrl.CommitURL"/>
</line_editor>
- <layout_stack
+ <icon
+ name="media_secure_lock_flag"
+ height="16"
+ follows="top|left"
+ image_name="Lock2"
+ layout="topleft"
+ left_delta="2"
+ top_delta="2"
+ visible="false"
+ tool_tip="Secured Browsing"
+ width="16" />
+ <layout_stack
name="media_address_url_icons"
animate="false"
follows="top|right"
@@ -340,19 +351,6 @@
tool_tip="White List enabled"
width="16" />
</layout_panel>
- <layout_panel
- layout="topleft"
- width="16"
- mouse_opaque="false"
- auto_resize="false">
- <icon
- name="media_secure_lock_flag"
- height="16"
- image_name="Lock2"
- layout="topleft"
- tool_tip="Secured Browsing"
- width="16" />
- </layout_panel>
</layout_stack>
</layout_panel>
<layout_panel
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
index d2f29ade44..61c8c971c2 100755
--- a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
@@ -16,11 +16,11 @@
imgoverlay_label_space="10"
label="Post to My Profile Feed"
layout="topleft"
- left="10"
+ left_delta="0"
name="save_to_profile_btn"
pad_left="10"
right="-10"
- top="5">
+ top_pad="10">
<button.commit_callback
function="Snapshot.SaveToProfile" />
</button>
diff --git a/indra/newview/skins/default/xui/en/panel_social_account.xml b/indra/newview/skins/default/xui/en/panel_social_account.xml
new file mode 100644
index 0000000000..d7235396fe
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_social_account.xml
@@ -0,0 +1,75 @@
+<panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_social_account">
+ <string
+ name="facebook_connected"
+ value="You are connected to Facebook as:" />
+ <string
+ name="facebook_disconnected"
+ value="Not connected to Facebook" />
+ <text
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="account_caption_label"
+ top="21"
+ type="string">
+ Not connected to Facebook.
+ </text>
+ <text
+ layout="topleft"
+ top_pad="2"
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="account_name_label"
+ parse_urls="true"
+ type="string"/>
+ <panel
+ layout="topleft"
+ name="panel_buttons"
+ height="345"
+ left="9">
+ <button
+ layout="topleft"
+ follows="left|top"
+ top_pad="9"
+ visible="true"
+ height="23"
+ label="Connect..."
+ name="connect_btn"
+ width="210">
+ <commit_callback function="SocialSharing.Connect"/>
+ </button>
+
+ <button
+ layout="topleft"
+ follows="left|top"
+ top_delta="0"
+ height="23"
+ label="Disconnect"
+ name="disconnect_btn"
+ width="210"
+ visible="false">
+ <commit_callback function="SocialSharing.Disconnect"/>
+ </button>
+ <text
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="0"
+ name="account_learn_more_label"
+ top_pad="20"
+ type="string">
+ [http://community.secondlife.com/t5/English-Knowledge-Base/Second-Life-Share/ta-p/2149711 Learn about posting to Facebook]
+ </text>
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_social_photo.xml b/indra/newview/skins/default/xui/en/panel_social_photo.xml
new file mode 100644
index 0000000000..a55613b52a
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_social_photo.xml
@@ -0,0 +1,152 @@
+ <panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_social_photo">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="392"
+ follows="all"
+ orientation="vertical"
+ name="stack_photo"
+ top="8">
+ <layout_panel
+ name="snapshot_panel"
+ height="367">
+ <combo_box
+ control_name="SocialPhotoResolution"
+ follows="left|top"
+ top="6"
+ left="9"
+ name="resolution_combobox"
+ tool_tip="Image resolution"
+ height="21"
+ width="135">
+ <combo_box.item
+ label="Current Window"
+ name="CurrentWindow"
+ value="[i0,i0]" />
+ <combo_box.item
+ label="640x480"
+ name="640x480"
+ value="[i640,i480]" />
+ <combo_box.item
+ label="800x600"
+ name="800x600"
+ value="[i800,i600]" />
+ <combo_box.item
+ label="1024x768"
+ name="1024x768"
+ value="[i1024,i768]" />
+ </combo_box>
+ <text
+ follows="left|top"
+ font="SansSerifSmall"
+ height="14"
+ left="208"
+ length="1"
+ halign="right"
+ name="file_size_label"
+ top="9"
+ type="string"
+ width="50">
+ [SIZE] KB
+ </text>
+ <panel
+ height="150"
+ width="250"
+ visible="true"
+ name="thumbnail_placeholder"
+ top="33"
+ follows="left|top"
+ left="9">
+ </panel>
+ <button
+ follows="left|top"
+ height="23"
+ label="Refresh"
+ left="9"
+ top_pad="5"
+ name="new_snapshot_btn"
+ tool_tip="Click to refresh"
+ visible="true"
+ width="100" >
+ <button.commit_callback
+ function="SocialSharing.RefreshPhoto" />
+ </button>
+ <text
+ follows="left|top"
+ font="SansSerif"
+ text_color="EmphasisColor"
+ height="14"
+ top_pad="-19"
+ left_pad="-20"
+ length="1"
+ halign="center"
+ name="working_lbl"
+ translate="false"
+ type="string"
+ visible="true"
+ width="150">
+ Refreshing...
+ </text>
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="caption_label"
+ top_pad="20"
+ type="string">
+ Comment (optional):
+ </text>
+ <text_editor
+ follows="left|top"
+ height="87"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="photo_caption"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <check_box
+ follows="left|top"
+ initial_value="true"
+ label="Include location in posting"
+ name="add_location_cb"
+ left="9"
+ height="16"
+ top_pad="8"/>
+ </layout_panel>
+ <layout_panel
+ name="photo_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Post"
+ name="post_photo_btn"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.SendPhoto" />
+ </button>
+ <button
+ follows="left|top"
+ height="23"
+ label="Cancel"
+ name="cancel_photo_btn"
+ left_pad="15"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.Cancel" />
+ </button>
+ </layout_panel>
+ </layout_stack>
+ </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_social_place.xml b/indra/newview/skins/default/xui/en/panel_social_place.xml
new file mode 100644
index 0000000000..13e94f6998
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_social_place.xml
@@ -0,0 +1,132 @@
+ <panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_social_place">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="392"
+ follows="all"
+ orientation="vertical"
+ name="stack_place"
+ top="8">
+ <layout_panel
+ name="place_detail_panel"
+ height="181">
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="place_caption_label"
+ top="13"
+ type="string">
+ Say something about where you are:
+ </text>
+ <text_editor
+ follows="top|left"
+ height="150"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="place_caption"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ </layout_panel>
+ <layout_panel
+ name="place_map_panel"
+ height="186">
+ <panel
+ follows="left|top"
+ height="128"
+ width="128"
+ background_visible="true"
+ bg_opaque_color="Black"
+ bg_alpha_color="Black"
+ top="20"
+ left="9"
+ visible="true"
+ name="map_border">
+ </panel>
+ <loading_indicator
+ follows="left|top"
+ height="24"
+ width="24"
+ name="map_loading_indicator"
+ top="77"
+ left="61"
+ visible="true"/>
+ <icon
+ follows="left|top"
+ height="128"
+ width="128"
+ image_name="Map_Placeholder_Icon"
+ layout="topleft"
+ top="20"
+ left="9"
+ visible="true"
+ name="map_placeholder">
+ </icon>
+ <icon
+ follows="left|top"
+ height="128"
+ width="128"
+ image_name="Map_Placeholder_Icon"
+ layout="topleft"
+ top="20"
+ left="9"
+ visible="true"
+ name="map_default">
+ </icon>
+ <check_box
+ follows="left|top"
+ initial_value="false"
+ top_delta="8"
+ width="8"
+ label=""
+ name="add_place_view_cb"
+ left_pad="5"/>
+ <text
+ follows="left|top"
+ font="SansSerif"
+ height="32"
+ width="130"
+ word_wrap="true"
+ left_pad="12"
+ top_delta="-8"
+ type="string">
+ Include overhead view of location
+ </text>
+ </layout_panel>
+ <layout_panel
+ name="place_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Post"
+ name="post_place_btn"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.SendCheckin" />
+ </button>
+ <button
+ follows="left|top"
+ height="23"
+ label="Cancel"
+ name="cancel_place_btn"
+ left_pad="15"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.Cancel" />
+ </button>
+ </layout_panel>
+ </layout_stack>
+ </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_social_status.xml b/indra/newview/skins/default/xui/en/panel_social_status.xml
new file mode 100644
index 0000000000..54cfa3f524
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_social_status.xml
@@ -0,0 +1,67 @@
+ <panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_social_status">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="392"
+ follows="all"
+ orientation="vertical"
+ name="stack_status"
+ top="8">
+ <layout_panel
+ name="status_detail_panel"
+ height="367">
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="status_caption_label"
+ top="13"
+ type="string">
+ What's on your mind?
+ </text>
+ <text_editor
+ follows="left|top"
+ height="150"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="status_message"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ </layout_panel>
+ <layout_panel
+ name="status_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Post"
+ name="post_status_btn"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.SendStatus" />
+ </button>
+ <button
+ follows="left|top"
+ height="23"
+ label="Cancel"
+ name="cancel_status_btn"
+ left_pad="15"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.Cancel" />
+ </button>
+ </layout_panel>
+ </layout_stack>
+ </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml
index dd2a0c6627..064ece6e4b 100755
--- a/indra/newview/skins/default/xui/en/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml
@@ -113,7 +113,7 @@
image_pressed="Pause_Press"
image_pressed_selected="Play_Press"
is_toggle="true"
- left_pad="15"
+ left_pad="5"
top="1"
name="media_toggle_btn"
tool_tip="Start/Stop All Media (Music, Video, Web pages)"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 1c46cec479..8ac95beddb 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -148,6 +148,14 @@ Please try logging in again in a minute.</string>
<string name="SentToInvalidRegion">You were sent to an invalid region.</string>
<string name="TestingDisconnect">Testing viewer disconnect</string>
+ <!-- Facebook Connect and, eventually, other Social Network -->
+ <string name="SocialFacebookConnecting">Connecting to Facebook...</string>
+ <string name="SocialFacebookPosting">Posting...</string>
+ <string name="SocialFacebookDisconnecting">Disconnecting from Facebook...</string>
+ <string name="SocialFacebookErrorConnecting">Problem connecting to Facebook</string>
+ <string name="SocialFacebookErrorPosting">Problem posting to Facebook</string>
+ <string name="SocialFacebookErrorDisconnecting">Problem disconnecting from Facebook</string>
+
<!-- Tooltip -->
<string name="TooltipPerson">Person</string><!-- Object under mouse pointer is an avatar -->
<string name="TooltipNoName">(no name)</string> <!-- No name on an object -->
@@ -379,7 +387,7 @@ Please try logging in again in a minute.</string>
<!-- world map -->
<string name="texture_loading">Loading...</string>
<string name="worldmap_offline">Offline</string>
- <string name="worldmap_item_tooltip_format">[AREA] m² L$[PRICE]</string>
+ <string name="worldmap_item_tooltip_format">[AREA] m² L$[PRICE] ([SQMPRICE] L$/m²)</string>
<string name="worldmap_results_none_found">None found.</string>
<!-- animations uploading status codes -->
@@ -2185,7 +2193,7 @@ For AI Character: Get the closest navigable point to the point provided.
<string name="InventoryNoTexture">You do not have a copy of this texture in your inventory</string>
<string name="InventoryInboxNoItems">Your Marketplace purchases will appear here. You may then drag them into your inventory to use them.</string>
<string name="MarketplaceURL">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/</string>
- <string name="MarketplaceURL_CreateStore">http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4</string>
+ <string name="MarketplaceURL_CreateStore">http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3</string>
<string name="MarketplaceURL_Dashboard">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard</string>
<string name="MarketplaceURL_Imports">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports</string>
<string name="MarketplaceURL_LearnMore">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more</string>
@@ -2464,7 +2472,11 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale
all estates that you manage for [OWNER]
</string>
<string name="RegionInfoAllowedResidents">Allowed Residents: ([ALLOWEDAGENTS], max [MAXACCESS])</string>
- <string name="RegionInfoAllowedGroups">Allowed groups: ([ALLOWEDGROUPS], max [MAXACCESS])</string>
+ <string name="RegionInfoAllowedGroups">Allowed Groups: ([ALLOWEDGROUPS], max [MAXACCESS])</string>
+ <string name="RegionInfoEstateManagers">Estate Managers: ([ESTATEMANAGERS], max [MAXMANAGERS])</string>
+ <string name="RegionInfoBannedResidents">Banned Residents: ([BANNEDAGENTS], max [MAXBANNED])</string>
+ <string name="RegionInfoListTypeAllowedAgents">Allowed Residents</string>
+ <string name="RegionInfoListTypeBannedAgents">Banned Residents</string>
<!-- script limits floater -->
<string name="ScriptLimitsParcelScriptMemory">Parcel Script Memory</string>
@@ -3427,6 +3439,9 @@ If you continue to receive this message, contact the [SUPPORT_SITE].
Drag items from inventory here
</string>
+ <string name="facebook_post_success">
+ You posted to Facebook.
+ </string>
<string name="no_session_message">
(IM Session Doesn't Exist)
@@ -3859,6 +3874,7 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Profile_Label">Profile</string>
<string name="Command_Search_Label">Search</string>
<string name="Command_Snapshot_Label">Snapshot</string>
+ <string name="Command_Social_Label">Facebook</string>
<string name="Command_Speak_Label">Speak</string>
<string name="Command_View_Label">Camera controls</string>
<string name="Command_Voice_Label">Voice settings</string>
@@ -3886,6 +3902,7 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Profile_Tooltip">Edit or view your profile</string>
<string name="Command_Search_Tooltip">Find places, events, people</string>
<string name="Command_Snapshot_Tooltip">Take a picture</string>
+ <string name="Command_Social_Tooltip">Post to Facebook</string>
<string name="Command_Speak_Tooltip">Speak with people nearby using your microphone</string>
<string name="Command_View_Tooltip">Changing camera angle</string>
<string name="Command_Voice_Tooltip">Volume controls for calls and people near you in world</string>
diff --git a/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml
new file mode 100644
index 0000000000..af5aec2c34
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<person_tab_view
+ folder_arrow_image="Folder_Arrow"
+ folder_indentation="5"
+ item_height="24"
+ item_top_pad="3"
+ mouse_opaque="true"
+ follows="left|top|right"
+ text_pad="6"
+ text_pad_left="4"
+ text_pad_right="4"
+ arrow_size="10"
+ max_folder_item_overlap="2"/>
diff --git a/indra/newview/skins/default/xui/en/widgets/person_view.xml b/indra/newview/skins/default/xui/en/widgets/person_view.xml
new file mode 100644
index 0000000000..46c1b7ff75
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/person_view.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<person_view
+ folder_arrow_image="Folder_Arrow"
+ folder_indentation="5"
+ item_height="24"
+ item_top_pad="3"
+ mouse_opaque="true"
+ follows="left|top|right"
+ icon_pad="4"
+ icon_width="20"
+ text_pad="6"
+ text_pad_left="4"
+ text_pad_right="4"
+ arrow_size="10"
+ max_folder_item_overlap="2">
+ <facebook_icon
+ follows="left"
+ height="14"
+ image_name="Facebook_Icon"
+ left="5"
+ bottom="6"
+ name="facebook_icon"
+ tool_tip="Facebook User"
+ visible="false"
+ width="14" />
+ <avatar_icon
+ follows="left"
+ layout="topleft"
+ height="20"
+ default_icon_name="Generic_Person"
+ left="5"
+ top="2"
+ visible="false"
+ width="20" />
+ <last_interaction_time_textbox
+ layout="topleft"
+ follows="right"
+ font="SansSerifSmall"
+ height="15"
+ left_pad="5"
+ right="-164"
+ name="last_interaction_time_textbox"
+ text_color="LtGray_50"
+ value="0s"
+ visible="false"
+ width="35" />
+ <permission_edit_theirs_icon
+ layout="topleft"
+ height="16"
+ follows="right"
+ image_name="Permission_Edit_Objects_Theirs"
+ left_pad="3"
+ right="-129"
+ name="permission_edit_theirs_icon"
+ tool_tip="You can edit this friend&apos;s objects"
+ top="4"
+ visible="false"
+ width="16" />
+ <permission_edit_mine_icon
+ layout="topleft"
+ height="16"
+ follows="right"
+ image_name="Permission_Edit_Objects_Mine"
+ left_pad="3"
+ right="-110"
+ name="permission_edit_mine_icon"
+ tool_tip="This friend can edit, delete or take your objects"
+ top="4"
+ visible="false"
+ width="16" />
+ <permission_map_icon
+ height="16"
+ follows="right"
+ image_name="Permission_Visible_Map"
+ left_pad="3"
+ tool_tip="This friend can locate you on the map"
+ right="-91"
+ name="permission_map_icon"
+ visible="false"
+ width="16" />
+ <permission_online_icon
+ height="16"
+ follows="right"
+ image_name="Permission_Visible_Online"
+ left_pad="3"
+ right="-72"
+ name="permission_online_icon"
+ tool_tip="This friend can see when you&apos;re online"
+ visible="false"
+ width="16" />
+ <info_btn
+ follows="right"
+ height="16"
+ image_pressed="Info_Press"
+ image_unselected="Info_Over"
+ left_pad="3"
+ right="-53"
+ name="info_btn"
+ tool_tip="More info"
+ tab_stop="false"
+ visible="false"
+ width="16" />
+ <profile_btn
+ layout="topleft"
+ follows="right"
+ height="20"
+ image_overlay="Web_Profile_Off"
+ left_pad="5"
+ right="-28"
+ name="profile_btn"
+ tab_stop="false"
+ tool_tip="View profile"
+ top="2"
+ visible="false"
+ width="20" />
+ <output_monitor
+ auto_update="true"
+ follows="right"
+ draw_border="false"
+ height="16"
+ right="-3"
+ mouse_opaque="true"
+ name="speaking_indicator"
+ visible="false"
+ width="20" />
+ </person_view>
+
diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml
index 4686d1ed3a..484511a08b 100755
--- a/indra/newview/skins/default/xui/es/strings.xml
+++ b/indra/newview/skins/default/xui/es/strings.xml
@@ -1256,7 +1256,7 @@ Intenta iniciar sesión de nuevo en unos instantes.
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
</string>
<string name="MarketplaceURL_CreateStore">
- http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4
+ http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3
</string>
<string name="MarketplaceURL_Dashboard">
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard
diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml
index 346fa62351..78d846ff4f 100755
--- a/indra/newview/skins/default/xui/fr/strings.xml
+++ b/indra/newview/skins/default/xui/fr/strings.xml
@@ -1274,7 +1274,7 @@ Veuillez réessayer de vous connecter dans une minute.
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
</string>
<string name="MarketplaceURL_CreateStore">
- http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4
+ http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3
</string>
<string name="MarketplaceURL_Dashboard">
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard
diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml
index 160df911d3..60ed2b0929 100755
--- a/indra/newview/skins/default/xui/it/strings.xml
+++ b/indra/newview/skins/default/xui/it/strings.xml
@@ -1265,7 +1265,7 @@ Prova ad accedere nuovamente tra un minuto.
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
</string>
<string name="MarketplaceURL_CreateStore">
- http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4
+ http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3
</string>
<string name="MarketplaceURL_Dashboard">
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard
diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml
index ab2fd461ab..a0f45e5a55 100755
--- a/indra/newview/skins/default/xui/ja/strings.xml
+++ b/indra/newview/skins/default/xui/ja/strings.xml
@@ -1274,7 +1274,7 @@ support@secondlife.com にお問い合わせください。
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
</string>
<string name="MarketplaceURL_CreateStore">
- http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4
+ http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3
</string>
<string name="MarketplaceURL_Dashboard">
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard
diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml
index adc3ee14e0..2eb4c0a02e 100755
--- a/indra/newview/skins/default/xui/pt/strings.xml
+++ b/indra/newview/skins/default/xui/pt/strings.xml
@@ -1220,7 +1220,7 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
</string>
<string name="MarketplaceURL_CreateStore">
- http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4
+ http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3
</string>
<string name="MarketplaceURL_Dashboard">
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard
diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml
index 49ce33ec96..0f71edcee0 100755
--- a/indra/newview/skins/default/xui/ru/strings.xml
+++ b/indra/newview/skins/default/xui/ru/strings.xml
@@ -1271,7 +1271,7 @@ support@secondlife.com.
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
</string>
<string name="MarketplaceURL_CreateStore">
- http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4
+ http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3
</string>
<string name="MarketplaceURL_Dashboard">
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard
diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml
index 5a809ea783..c4b1530b2b 100755
--- a/indra/newview/skins/default/xui/tr/strings.xml
+++ b/indra/newview/skins/default/xui/tr/strings.xml
@@ -1271,7 +1271,7 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin.
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
</string>
<string name="MarketplaceURL_CreateStore">
- http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4
+ http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.3
</string>
<string name="MarketplaceURL_Dashboard">
https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard
diff --git a/indra/newview/tests/lltranslate_test.cpp b/indra/newview/tests/lltranslate_test.cpp
index fd9527d631..8ce56326d8 100755
--- a/indra/newview/tests/lltranslate_test.cpp
+++ b/indra/newview/tests/lltranslate_test.cpp
@@ -308,8 +308,8 @@ void LLCurl::Responder::errorWithContent(U32, std::string const&, LLSD const&) {
void LLCurl::Responder::result(LLSD const&) {}
LLCurl::Responder::~Responder() {}
-void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32) {}
-void LLHTTPClient::get(const std::string&, LLPointer<LLCurl::Responder>, const LLSD&, const F32) {}
+void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32, bool) {}
+void LLHTTPClient::get(const std::string&, LLPointer<LLCurl::Responder>, const LLSD&, const F32, bool) {}
LLBufferStream::LLBufferStream(const LLChannelDescriptors& channels, LLBufferArray* buffer)
: std::iostream(&mStreamBuf), mStreamBuf(channels, buffer) {}
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 19863dd845..894e368427 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -104,26 +104,37 @@ class ViewerManifest(LLManifest):
self.path("dictionaries")
self.end_prefix(pkgdir)
- # CHOP-955: If we have "sourceid" in the build process
- # environment, generate it into settings_install.xml.
- try:
- sourceid = os.environ["sourceid"]
- except KeyError:
- # no sourceid, no settings_install.xml file
- pass
- else:
- if sourceid:
- # Single-entry subset of the LLSD content of settings.xml
- content = dict(sourceid=dict(Comment='Identify referring agency to Linden web servers',
- Persist=1,
- Type='String',
- Value=sourceid))
- # put_in_file(src=) need not be an actual pathname; it
- # only needs to be non-empty
- settings_install = self.put_in_file(llsd.format_pretty_xml(content),
- "settings_install.xml",
- src="environment")
- print "Put sourceid '%s' in %s" % (sourceid, settings_install)
+ # CHOP-955: If we have "sourceid" or "viewer_channel" in the
+ # build process environment, generate it into
+ # settings_install.xml.
+ settings_template = dict(
+ sourceid=dict(Comment='Identify referring agency to Linden web servers',
+ Persist=1,
+ Type='String',
+ Value=''),
+ CmdLineChannel=dict(Comment='Command line specified channel name',
+ Persist=0,
+ Type='String',
+ Value=''))
+ settings_install = {}
+ for key, setting in (("sourceid", "sourceid"),
+ ("channel", "CmdLineChannel")):
+ if key in self.args:
+ # only set if value is non-empty
+ if self.args[key]:
+ # copy corresponding setting from settings_template
+ settings_install[setting] = settings_template[setting].copy()
+ # then fill in Value
+ settings_install[setting]["Value"] = self.args[key]
+ print "Put %s '%s' in settings_install.xml" % (setting, self.args[key])
+
+ # did we actually copy anything into settings_install dict?
+ if settings_install:
+ # put_in_file(src=) need not be an actual pathname; it
+ # only needs to be non-empty
+ self.put_in_file(llsd.format_pretty_xml(settings_install),
+ "settings_install.xml",
+ src="environment")
self.end_prefix("app_settings")
@@ -611,6 +622,9 @@ class WindowsManifest(ViewerManifest):
installer_file = self.args['installer_name']
else:
installer_file = installer_file % substitution_strings
+ if len(self.args['package_id']) > 0:
+ installer_file = installer_file.replace(self.args['package_id'], "")
+ installer_file = installer_file.replace(".exe", self.args['package_id'] + ".exe")
substitution_strings['installer_file'] = installer_file
tempfile = "secondlife_setup_tmp.nsi"
@@ -838,7 +852,9 @@ class DarwinManifest(ViewerManifest):
volname="Second Life Installer" # DO NOT CHANGE without understanding comment above
- if self.default_channel():
+ if len(self.args['package_id']) > 0:
+ imagename = imagename + self.args['package_id']
+ elif self.default_channel():
if not self.default_grid():
# beta case
imagename = imagename + '_' + self.args['grid'].upper()
@@ -851,7 +867,7 @@ class DarwinManifest(ViewerManifest):
# make sure we don't have stale files laying about
self.remove(sparsename, finalname)
- self.run_command('hdiutil create %(sparse)r -volname %(vol)r -fs HFS+ -type SPARSE -megabytes 700 -layout SPUD' % {
+ self.run_command('hdiutil create %(sparse)r -volname %(vol)r -fs HFS+ -type SPARSE -megabytes 1000 -layout SPUD' % {
'sparse':sparsename,
'vol':volname})
@@ -926,6 +942,7 @@ class DarwinManifest(ViewerManifest):
print "Converting temp disk image to final disk image"
self.run_command('hdiutil convert %(sparse)r -format UDZO -imagekey zlib-level=9 -o %(final)r' % {'sparse':sparsename, 'final':finalname})
+ self.run_command('hdiutil internet-enable -yes %(final)r' % {'final':finalname})
# get rid of the temp file
self.package_file = finalname
self.remove(sparsename)
@@ -998,6 +1015,7 @@ class LinuxManifest(ViewerManifest):
installer_name += '_' + self.args['grid'].upper()
else:
installer_name += '_' + self.channel_oneword().upper()
+ installer_name = installer_name + self.args['package_id']
self.strip_binaries()
diff --git a/indra/test_apps/llfbconnecttest/CMakeLists.txt b/indra/test_apps/llfbconnecttest/CMakeLists.txt
new file mode 100644
index 0000000000..f56329a010
--- /dev/null
+++ b/indra/test_apps/llfbconnecttest/CMakeLists.txt
@@ -0,0 +1,304 @@
+# -*- cmake -*-
+project(llfbconnecttest)
+
+include(00-Common)
+include(FindOpenGL)
+include(LLCommon)
+include(LLPlugin)
+include(Linking)
+include(LLSharedLibs)
+include(PluginAPI)
+include(LLImage)
+include(LLMath)
+include(LLMessage)
+include(LLRender)
+include(LLWindow)
+include(Glut)
+include(Glui)
+
+include_directories(
+ ${LLPLUGIN_INCLUDE_DIRS}
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLIMAGE_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLMESSAGE_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
+ ${LLWINDOW_INCLUDE_DIRS}
+)
+
+if (DARWIN)
+ include(CMakeFindFrameworks)
+ find_library(COREFOUNDATION_LIBRARY CoreFoundation)
+endif (DARWIN)
+
+### llfbconnecttest
+
+set(llfbconnecttest_SOURCE_FILES
+ llfbconnecttest.cpp
+ llfbconnecttest.h
+ bookmarks.txt
+ )
+
+add_executable(llfbconnecttest
+ WIN32
+ MACOSX_BUNDLE
+ ${llfbconnecttest_SOURCE_FILES}
+)
+
+set_target_properties(llfbconnecttest
+ PROPERTIES
+ WIN32_EXECUTABLE
+ FALSE
+)
+
+target_link_libraries(llfbconnecttest
+ ${GLUT_LIBRARY}
+ ${GLUI_LIBRARY}
+ ${OPENGL_LIBRARIES}
+ ${LLPLUGIN_LIBRARIES}
+ ${LLMESSAGE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ ${PLUGIN_API_WINDOWS_LIBRARIES}
+)
+
+if (DARWIN)
+ # The testbed needs to use a couple of CoreFoundation calls now, to deal with being a bundled app.
+ target_link_libraries(llfbconnecttest
+ ${COREFOUNDATION_LIBRARY}
+ )
+endif (DARWIN)
+
+add_dependencies(llfbconnecttest
+ stage_third_party_libs
+ SLPlugin
+ media_plugin_webkit
+ ${LLPLUGIN_LIBRARIES}
+ ${LLMESSAGE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+)
+
+# turn off weird GLUI pragma
+add_definitions(-DGLUI_NO_LIB_PRAGMA)
+
+if (DARWIN OR LINUX)
+ # glui.h contains code that triggers the "overloaded-virtual" warning in gcc.
+ set_source_files_properties(llfbconnecttest.cpp PROPERTIES COMPILE_FLAGS "-Wno-overloaded-virtual")
+endif (DARWIN OR LINUX)
+
+# Gather build products of the various dependencies into the build directory for the testbed.
+
+if (DARWIN)
+ # path inside the app bundle where we'll need to copy plugins and other related files
+ set(PLUGINS_DESTINATION_DIR
+ ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llfbconnecttest.app/Contents/Resources
+ )
+
+ # create the Contents/Resources directory
+ add_custom_command(
+ TARGET llfbconnecttest POST_BUILD
+ COMMAND ${CMAKE_COMMAND}
+ ARGS
+ -E
+ make_directory
+ ${PLUGINS_DESTINATION_DIR}
+ COMMENT "Creating Resources directory in app bundle."
+ )
+else (DARWIN)
+ set(PLUGINS_DESTINATION_DIR
+ ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/
+ )
+endif (DARWIN)
+
+get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION)
+add_custom_command(TARGET llfbconnecttest POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_SLPLUGIN} ${PLUGINS_DESTINATION_DIR}
+ DEPENDS ${BUILT_SLPLUGIN}
+)
+
+get_target_property(BUILT_LLCOMMON llcommon LOCATION)
+add_custom_command(TARGET llfbconnecttest POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_LLCOMMON} ${PLUGINS_DESTINATION_DIR}
+ DEPENDS ${BUILT_LLCOMMON}
+)
+
+
+if (DARWIN OR WINDOWS)
+ get_target_property(BUILT_WEBKIT_PLUGIN media_plugin_webkit LOCATION)
+ add_custom_command(TARGET llfbconnecttest POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_WEBKIT_PLUGIN} ${PLUGINS_DESTINATION_DIR}
+ DEPENDS ${BUILT_WEBKIT_PLUGIN}
+ )
+
+ # copy over bookmarks file if llfbconnecttest gets built
+ get_target_property(BUILT_LLFBCONNECTTEST llfbconnecttest LOCATION)
+ add_custom_command(TARGET llfbconnecttest POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${CMAKE_CURRENT_BINARY_DIR}/
+ DEPENDS ${BUILT_LLFBCONNECTTEST}
+ )
+ # also copy it to the same place as SLPlugin, which is what the mac wants...
+ add_custom_command(TARGET llfbconnecttest POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${PLUGINS_DESTINATION_DIR}
+ DEPENDS ${BUILT_LLFBCONNECTTEST}
+ )
+endif (DARWIN OR WINDOWS)
+
+if (DARWIN)
+ add_custom_command(TARGET llfbconnecttest POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib ${PLUGINS_DESTINATION_DIR}
+ DEPENDS ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib
+ )
+endif (DARWIN)
+
+if(WINDOWS)
+ #********************
+ # Plugin test library deploy
+ #
+ # Debug config runtime files required for the FB connect test
+ set(fbconnecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}")
+ set(fbconnecttest_debug_files
+ libeay32.dll
+ libglib-2.0-0.dll
+ libgmodule-2.0-0.dll
+ libgobject-2.0-0.dll
+ libgthread-2.0-0.dll
+ qtcored4.dll
+ qtguid4.dll
+ qtnetworkd4.dll
+ qtopengld4.dll
+ qtwebkitd4.dll
+ ssleay32.dll
+ )
+ copy_if_different(
+ ${fbconnecttest_debug_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/Debug"
+ out_targets
+ ${fbconnecttest_debug_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ # Debug config runtime files required for the FB connect test (Qt image format plugins)
+ set(fbconecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}/imageformats")
+ set(fbconecttest_debug_files
+ qgifd4.dll
+ qicod4.dll
+ qjpegd4.dll
+ qmngd4.dll
+ qsvgd4.dll
+ qtiffd4.dll
+ )
+ copy_if_different(
+ ${fbconecttest_debug_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/Debug/imageformats"
+ out_targets
+ ${fbconecttest_debug_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ # Debug config runtime files required for the FB connect test (Qt codec plugins)
+ set(fbconnecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}/codecs")
+ set(fbconnecttest_debug_files
+ qcncodecsd4.dll
+ qjpcodecsd4.dll
+ qkrcodecsd4.dll
+ qtwcodecsd4.dll
+ )
+ copy_if_different(
+ ${fbconnecttest_debug_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/Debug/codecs"
+ out_targets
+ ${fbconnecttest_debug_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ # Release & ReleaseDebInfo config runtime files required for the FB connect test
+ set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
+ set(fbconnecttest_release_files
+ libeay32.dll
+ libglib-2.0-0.dll
+ libgmodule-2.0-0.dll
+ libgobject-2.0-0.dll
+ libgthread-2.0-0.dll
+ qtcore4.dll
+ qtgui4.dll
+ qtnetwork4.dll
+ qtopengl4.dll
+ qtwebkit4.dll
+ qtxmlpatterns4.dll
+ ssleay32.dll
+ )
+ copy_if_different(
+ ${fbconnecttest_release_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/Release"
+ out_targets
+ ${fbconnecttest_release_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ copy_if_different(
+ ${fbconnecttest_release_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo"
+ out_targets
+ ${fbconnecttest_release_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ # Release & ReleaseDebInfo config runtime files required for the FB connect test (Qt image format plugins)
+ set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}/imageformats")
+ set(fbconnecttest_release_files
+ qgif4.dll
+ qico4.dll
+ qjpeg4.dll
+ qmng4.dll
+ qsvg4.dll
+ qtiff4.dll
+ )
+ copy_if_different(
+ ${fbconnecttest_release_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/Release/imageformats"
+ out_targets
+ ${fbconnecttest_release_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ copy_if_different(
+ ${fbconnecttest_release_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/imageformats"
+ out_targets
+ ${fbconnecttest_release_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ # Release & ReleaseDebInfo config runtime files required for the FB connect test (Qt codec plugins)
+ set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}/codecs")
+ set(fbconnecttest_release_files
+ qcncodecs4.dll
+ qjpcodecs4.dll
+ qkrcodecs4.dll
+ qtwcodecs4.dll
+ )
+ copy_if_different(
+ ${fbconnecttest_release_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/Release/codecs"
+ out_targets
+ ${fbconnecttest_release_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ copy_if_different(
+ ${fbconnecttest_release_src_dir}
+ "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/codecs"
+ out_targets
+ ${fbconnecttest_release_files}
+ )
+ set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets})
+
+ add_custom_target(copy_fbconnecttest_libs ALL
+ DEPENDS
+ ${fbconnect_test_targets}
+ )
+
+ add_dependencies(llfbconnecttest copy_fbconnecttest_libs)
+
+endif(WINDOWS)
+
+ll_deploy_sharedlibs_command(llfbconnecttest)
diff --git a/indra/test_apps/llfbconnecttest/bookmarks.txt b/indra/test_apps/llfbconnecttest/bookmarks.txt
new file mode 100644
index 0000000000..3995627ea9
--- /dev/null
+++ b/indra/test_apps/llfbconnecttest/bookmarks.txt
@@ -0,0 +1,4 @@
+# format is description, url (don't put ',' chars in description :)
+# if no ',' found, whole line is used for both description and url
+Google Home Page,http://www.google.com
+Facebook Home Page,http://www.facebook.com
diff --git a/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp b/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp
new file mode 100644
index 0000000000..483a15c468
--- /dev/null
+++ b/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp
@@ -0,0 +1,2394 @@
+/**
+ * @file LLFBConnectTest.cpp
+ * @brief Facebook Connect Test App
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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 "indra_constants.h"
+
+#include "llapr.h"
+#include "llerrorcontrol.h"
+
+#include <math.h>
+#include <iomanip>
+#include <sstream>
+#include <ctime>
+
+#include "llfbconnecttest.h"
+
+#if __APPLE__
+ #include <GLUT/glut.h>
+ #include <CoreFoundation/CoreFoundation.h>
+#else
+ #define FREEGLUT_STATIC
+ #include "GL/freeglut.h"
+ #define GLUI_FREEGLUT
+#endif
+
+#if LL_WINDOWS
+#pragma warning(disable: 4263)
+#pragma warning(disable: 4264)
+#endif
+#include "glui.h"
+
+
+LLFBConnectTest* gApplication = 0;
+static void gluiCallbackWrapper( int control_id );
+
+////////////////////////////////////////////////////////////////////////////////
+//
+static bool isTexture( GLuint texture )
+{
+ bool result = false;
+
+ // glIsTexture will sometimes return false for real textures... do this instead.
+ if(texture != 0)
+ result = true;
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+mediaPanel::mediaPanel()
+{
+ mMediaTextureHandle = 0;
+ mPickTextureHandle = 0;
+ mMediaSource = NULL;
+ mPickTexturePixels = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+mediaPanel::~mediaPanel()
+{
+ // delete OpenGL texture handles
+ if ( isTexture( mPickTextureHandle ) )
+ {
+ std::cerr << "remMediaPanel: deleting pick texture " << mPickTextureHandle << std::endl;
+ glDeleteTextures( 1, &mPickTextureHandle );
+ mPickTextureHandle = 0;
+ }
+
+ if ( isTexture( mMediaTextureHandle ) )
+ {
+ std::cerr << "remMediaPanel: deleting media texture " << mMediaTextureHandle << std::endl;
+ glDeleteTextures( 1, &mMediaTextureHandle );
+ mMediaTextureHandle = 0;
+ }
+
+ if(mPickTexturePixels)
+ {
+ delete mPickTexturePixels;
+ }
+
+ if(mMediaSource)
+ {
+ delete mMediaSource;
+ }
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+LLFBConnectTest::LLFBConnectTest( int app_window, int window_width, int window_height ) :
+ mVersionMajor( 2 ),
+ mVersionMinor( 0 ),
+ mVersionPatch( 0 ),
+ mMaxPanels( 25 ),
+ mViewportAspect( 0 ),
+ mAppWindow( app_window ),
+ mCurMouseX( 0 ),
+ mCurMouseY( 0 ),
+ mFuzzyMedia( true ),
+ mSelectedPanel( 0 ),
+ mDistanceCameraToSelectedGeometry( 0.0f ),
+ //mMediaBrowserControlEnableCookies( 0 ),
+ mMediaBrowserControlBackButton( 0 ),
+ mMediaBrowserControlForwardButton( 0 ),
+ //mMediaTimeControlVolume( 100 ),
+ //mMediaTimeControlSeekSeconds( 0 ),
+ //mGluiMediaTimeControlWindowFlag( true ),
+ mGluiMediaBrowserControlWindowFlag( true ),
+ mMediaBrowserControlBackButtonFlag( true ),
+ mMediaBrowserControlForwardButtonFlag( true ),
+ mHomeWebUrl( "https://cryptic-ridge-1632.herokuapp.com/" )
+{
+ // debugging spam
+ std::cout << std::endl << " GLUT version: " << "3.7.6" << std::endl; // no way to get real version from GLUT
+ std::cout << std::endl << " GLUI version: " << GLUI_Master.get_version() << std::endl;
+ std::cout << std::endl << "Media Plugin Test version: " << mVersionMajor << "." << mVersionMinor << "." << mVersionPatch << std::endl;
+
+ // bookmark title
+ mBookmarks.push_back( std::pair< std::string, std::string >( "--- Bookmarks ---", "" ) );
+
+ // insert hardcoded URLs here as required for testing
+ //mBookmarks.push_back( std::pair< std::string, std::string >( "description", "url" ) );
+
+ // read bookmarks from file.
+ // note: uses command in ./CmakeLists.txt which copies bookmmarks file from source directory
+ // to app directory (WITHOUT build configuration dir) (this is cwd in Windows within MSVC)
+ // For example, test_apps\llplugintest and not test_apps\llplugintest\Release
+ // This may need to be changed for Mac/Linux builds.
+ // See https://jira.lindenlab.com/browse/DEV-31350 for large list of media URLs from AGNI
+ const std::string bookmarks_filename( "bookmarks.txt" );
+ std::ifstream file_handle( bookmarks_filename.c_str() );
+ if ( file_handle.is_open() )
+ {
+ std::cout << "Reading bookmarks for test" << std::endl;
+ while( ! file_handle.eof() )
+ {
+ std::string line;
+ std::getline( file_handle, line );
+ if ( file_handle.eof() )
+ break;
+
+ if ( line.substr( 0, 1 ) != "#" )
+ {
+ size_t comma_pos = line.find_first_of( ',' );
+ if ( comma_pos != std::string::npos )
+ {
+ std::string description = line.substr( 0, comma_pos );
+ std::string url = line.substr( comma_pos + 1 );
+ mBookmarks.push_back( std::pair< std::string, std::string >( description, url ) );
+ }
+ else
+ {
+ mBookmarks.push_back( std::pair< std::string, std::string >( line, line ) );
+ };
+ };
+ };
+ std::cout << "Read " << mBookmarks.size() << " bookmarks" << std::endl;
+ }
+ else
+ {
+ std::cout << "Unable to read bookmarks from file: " << bookmarks_filename << std::endl;
+ };
+
+ // initialize linden lab APR module
+ ll_init_apr();
+
+ // Set up llerror logging
+ {
+ LLError::initForApplication(".");
+ LLError::setDefaultLevel(LLError::LEVEL_INFO);
+ //LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG);
+ }
+
+ // lots of randomness in this app
+ srand( ( unsigned int )time( 0 ) );
+
+ // build GUI
+ makeChrome();
+
+ // OpenGL initialilzation
+ glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
+ glClearDepth( 1.0f );
+ glEnable( GL_DEPTH_TEST );
+ glEnable( GL_COLOR_MATERIAL );
+ glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
+ glDepthFunc( GL_LEQUAL );
+ glEnable( GL_TEXTURE_2D );
+ glDisable( GL_BLEND );
+ glColor3f( 1.0f, 1.0f, 1.0f );
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+ glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
+
+ // start with a sane view
+ resetView();
+
+ // initial media panel
+ const int num_initial_panels = 1;
+ for( int i = 0; i < num_initial_panels; ++i )
+ {
+ //addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second );
+ addMediaPanel( mHomeWebUrl );
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+LLFBConnectTest::~LLFBConnectTest()
+{
+ // delete all media panels
+ for( int i = 0; i < (int)mMediaPanels.size(); ++i )
+ {
+ remMediaPanel( mMediaPanels[ i ] );
+ };
+
+ // Stop the plugin read thread if it's running.
+ LLPluginProcessParent::setUseReadThread(false);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::reshape( int width, int height )
+{
+ // update viewport (the active window inside the chrome)
+ int viewport_x, viewport_y;
+ int viewport_height, viewport_width;
+ GLUI_Master.get_viewport_area( &viewport_x, &viewport_y, &viewport_width, &viewport_height );
+ mViewportAspect = (float)( viewport_width ) / (float)( viewport_height );
+ glViewport( viewport_x, viewport_y, viewport_width, viewport_height );
+
+ // save these as we'll need them later
+ mWindowWidth = width;
+ mWindowHeight = height;
+
+ // adjust size of URL bar so it doesn't get clipped
+ mUrlEdit->set_w( mWindowWidth - 360 );
+
+ // GLUI requires this
+ if ( glutGetWindow() != mAppWindow )
+ glutSetWindow( mAppWindow );
+
+ // trigger re-display
+ glutPostRedisplay();
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::bindTexture(GLuint texture, GLint row_length, GLint alignment)
+{
+ glEnable( GL_TEXTURE_2D );
+
+ glBindTexture( GL_TEXTURE_2D, texture );
+ glPixelStorei( GL_UNPACK_ROW_LENGTH, row_length );
+ glPixelStorei( GL_UNPACK_ALIGNMENT, alignment );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool LLFBConnectTest::checkGLError(const char *name)
+{
+ bool result = false;
+ GLenum error = glGetError();
+
+ if(error != GL_NO_ERROR)
+ {
+ // For some reason, glGenTextures is returning GL_INVALID_VALUE...
+ std::cout << name << " ERROR 0x" << std::hex << error << std::dec << std::endl;
+ result = true;
+ }
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+GLfloat LLFBConnectTest::distanceToCamera( GLfloat point_x, GLfloat point_y, GLfloat point_z )
+{
+ GLdouble camera_pos_x = 0.0f;
+ GLdouble camera_pos_y = 0.0f;
+ GLdouble camera_pos_z = 0.0f;
+
+ GLdouble modelMatrix[16];
+ GLdouble projMatrix[16];
+ GLint viewport[4];
+
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
+ glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ gluUnProject(
+ (viewport[2]-viewport[0])/2 , (viewport[3]-viewport[1])/2,
+ 0.0,
+ modelMatrix, projMatrix, viewport,
+ &camera_pos_x, &camera_pos_y, &camera_pos_z );
+
+ GLfloat distance =
+ sqrt( ( camera_pos_x - point_x ) * ( camera_pos_x - point_x ) +
+ ( camera_pos_y - point_y ) * ( camera_pos_y - point_y ) +
+ ( camera_pos_z - point_z ) * ( camera_pos_z - point_z ) );
+
+ return distance;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::drawGeometry( int panel, bool selected )
+{
+ // texture coordinates for each panel
+ GLfloat non_opengl_texture_coords[ 8 ] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f };
+ GLfloat opengl_texture_coords[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f };
+
+ GLfloat *texture_coords = mMediaPanels[ panel ]->mAppTextureCoordsOpenGL?opengl_texture_coords:non_opengl_texture_coords;
+
+ // base coordinates for each panel
+ GLfloat base_vertex_pos[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f };
+
+ // calculate posiitons
+ const int num_panels = (int)mMediaPanels.size();
+ const int num_rows = (int)sqrt( (float)num_panels );
+ const int num_cols = num_panels / num_rows;
+ const int panel_x = ( panel / num_rows );
+ const int panel_y = ( panel % num_rows );
+
+ // default spacing is small - make it larger if checkbox set - for testing positional audio
+ float spacing = 0.1f;
+ //if ( mLargePanelSpacing )
+ // spacing = 2.0f;
+
+ const GLfloat offset_x = num_cols * ( 1.0 + spacing ) / 2;
+ const GLfloat offset_y = num_rows * ( 1.0 + spacing ) / 2;
+
+ // Adjust for media aspect ratios
+ {
+ float aspect = 1.0f;
+
+ if(mMediaPanels[ panel ]->mMediaHeight != 0)
+ {
+ aspect = (float)mMediaPanels[ panel ]->mMediaWidth / (float)mMediaPanels[ panel ]->mMediaHeight;
+ }
+
+ if(aspect > 1.0f)
+ {
+ // media is wider than it is high -- adjust the top and bottom in
+ for( int corner = 0; corner < 4; ++corner )
+ {
+ float temp = base_vertex_pos[corner * 2 + 1];
+
+ if(temp < 0.5f)
+ temp += 0.5 - (0.5f / aspect);
+ else
+ temp -= 0.5 - (0.5f / aspect);
+
+ base_vertex_pos[corner * 2 + 1] = temp;
+ }
+ }
+ else if(aspect < 1.0f)
+ {
+ // media is higher than it is wide -- adjust the left and right sides in
+ for( int corner = 0; corner < 4; ++corner )
+ {
+ float temp = base_vertex_pos[corner * 2];
+
+ if(temp < 0.5f)
+ temp += 0.5f - (0.5f * aspect);
+ else
+ temp -= 0.5f - (0.5f * aspect);
+
+ base_vertex_pos[corner * 2] = temp;
+ }
+ }
+ }
+
+ glBegin( GL_QUADS );
+ for( int corner = 0; corner < 4; ++corner )
+ {
+ glTexCoord2f( texture_coords[ corner * 2 ], texture_coords[ corner * 2 + 1 ] );
+ GLfloat x = base_vertex_pos[ corner * 2 ] + panel_x * ( 1.0 + spacing ) - offset_x + spacing / 2.0f;
+ GLfloat y = base_vertex_pos[ corner * 2 + 1 ] + panel_y * ( 1.0 + spacing ) - offset_y + spacing / 2.0f;
+
+ glVertex3f( x, y, 0.0f );
+ };
+ glEnd();
+
+ // calculate distance to this panel if it's selected
+ if ( selected )
+ {
+ GLfloat point_x = base_vertex_pos[ 0 ] + panel_x * ( 1.0 + spacing ) - offset_x + spacing / 2.0f;
+ GLfloat point_y = base_vertex_pos[ 0 + 1 ] + panel_y * ( 1.0 + spacing ) - offset_y + spacing / 2.0f;
+ GLfloat point_z = 0.0f;
+ mDistanceCameraToSelectedGeometry = distanceToCamera( point_x, point_y, point_z );
+ };
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::startPanelHighlight( float red, float green, float blue, float line_width )
+{
+ glPushAttrib( GL_ALL_ATTRIB_BITS );
+ glEnable( GL_POLYGON_OFFSET_FILL );
+ glPolygonOffset( -2.5f, -2.5f );
+ glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+ glLineWidth( line_width );
+ glColor3f( red, green, blue );
+ glDisable( GL_TEXTURE_2D );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::endPanelHighlight()
+{
+ glPopAttrib();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::draw( int draw_type )
+{
+ for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel )
+ {
+ // drawing pick texture
+ if ( draw_type == DrawTypePickTexture )
+ {
+ // only bother with pick if we have something to render
+ // Actually, we need to pick even if we're not ready to render.
+ // Otherwise you can't select and remove a panel which has gone bad.
+ //if ( mMediaPanels[ panel ]->mReadyToRender )
+ {
+ glMatrixMode( GL_TEXTURE );
+ glPushMatrix();
+
+ // pick texture is a power of 2 so no need to scale
+ glLoadIdentity();
+
+ // bind to media texture
+ glLoadIdentity();
+ bindTexture( mMediaPanels[ panel ]->mPickTextureHandle );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+
+ // draw geometry using pick texture
+ drawGeometry( panel, false );
+
+ glMatrixMode( GL_TEXTURE );
+ glPopMatrix();
+ };
+ }
+ else
+ if ( draw_type == DrawTypeMediaTexture )
+ {
+ bool texture_valid = false;
+ bool plugin_exited = false;
+
+ if(mMediaPanels[ panel ]->mMediaSource)
+ {
+ texture_valid = mMediaPanels[ panel ]->mMediaSource->textureValid();
+ plugin_exited = mMediaPanels[ panel ]->mMediaSource->isPluginExited();
+ }
+
+ // save texture matrix (changes for each panel)
+ glMatrixMode( GL_TEXTURE );
+ glPushMatrix();
+
+ // only process texture if the media is ready to draw
+ // (we still want to draw the geometry)
+ if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid )
+ {
+ // bind to media texture
+ bindTexture( mMediaPanels[ panel ]->mMediaTextureHandle );
+
+ if ( mFuzzyMedia )
+ {
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ }
+ else
+ {
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ }
+
+ // scale to fit panel
+ glScalef( mMediaPanels[ panel ]->mTextureScaleX,
+ mMediaPanels[ panel ]->mTextureScaleY,
+ 1.0f );
+ };
+
+ float intensity = plugin_exited?0.25f:1.0f;
+
+ // highlight the selected panel
+ if ( mSelectedPanel && ( mMediaPanels[ panel ]->mId == mSelectedPanel->mId ) )
+ {
+ startPanelHighlight( intensity, intensity, 0.0f, 5.0f );
+ drawGeometry( panel, true );
+ endPanelHighlight();
+ }
+ else
+ // this panel not able to render yet since it
+ // doesn't have enough information
+ if ( !mMediaPanels[ panel ]->mReadyToRender )
+ {
+ startPanelHighlight( intensity, 0.0f, 0.0f, 2.0f );
+ drawGeometry( panel, false );
+ endPanelHighlight();
+ }
+ else
+ // just display a border around the media
+ {
+ startPanelHighlight( 0.0f, intensity, 0.0f, 2.0f );
+ drawGeometry( panel, false );
+ endPanelHighlight();
+ };
+
+ if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid )
+ {
+ // draw visual geometry
+ drawGeometry( panel, false );
+ }
+
+ // restore texture matrix (changes for each panel)
+ glMatrixMode( GL_TEXTURE );
+ glPopMatrix();
+ };
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::display()
+{
+ // GLUI requires this
+ if ( glutGetWindow() != mAppWindow )
+ glutSetWindow( mAppWindow );
+
+ // start with a clean slate
+ glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+
+ // set up OpenGL view
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ glFrustum( -mViewportAspect * 0.04f, mViewportAspect * 0.04f, -0.04f, 0.04f, 0.1f, 50.0f );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ glTranslatef( 0.0, 0.0, 0.0f );
+ glTranslatef( mViewPos[ 0 ], mViewPos[ 1 ], -mViewPos[ 2 ] );
+ glMultMatrixf( mViewRotation );
+
+ // draw pick texture
+ draw( DrawTypePickTexture );
+
+ // read colors and get coordinate values
+ glReadPixels( mCurMouseX, mCurMouseY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, mPixelReadColor );
+
+ // clear the pick render (otherwise it may depth-fight with the textures rendered later)
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+
+ // draw visible geometry
+ draw( DrawTypeMediaTexture );
+
+ glutSwapBuffers();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::idle()
+{
+// checkGLError("LLFBConnectTest::idle");
+
+ // GLUI requires this
+ if ( glutGetWindow() != mAppWindow )
+ glutSetWindow( mAppWindow );
+
+ // random creation/destruction of panels enabled?
+/*
+ const time_t panel_timeout_time = 5;
+ if ( mRandomPanelCount )
+ {
+ // time for a change
+ static time_t last_panel_time = 0;
+ if ( time( NULL ) - last_panel_time > panel_timeout_time )
+ {
+ if ( rand() % 2 == 0 )
+ {
+ if ( mMediaPanels.size() < 16 )
+ {
+ std::cout << "Randomly adding new panel" << std::endl;
+ addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second );
+ };
+ }
+ else
+ {
+ if ( mMediaPanels.size() > 0 )
+ {
+ std::cout << "Deleting selected panel" << std::endl;
+ remMediaPanel( mSelectedPanel );
+ };
+ };
+ time( &last_panel_time );
+ };
+ };
+
+ // random selection of bookmarks enabled?
+ const time_t bookmark_timeout_time = 5;
+ if ( mRandomBookmarks )
+ {
+ // time for a change
+ static time_t last_bookmark_time = 0;
+ if ( time( NULL ) - last_bookmark_time > bookmark_timeout_time )
+ {
+ // go to a different random bookmark on each panel
+ for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel )
+ {
+ std::string uri = mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second;
+
+ std::cout << "Random: navigating to : " << uri << std::endl;
+
+ std::string mime_type = mimeTypeFromUrl( uri );
+
+ if ( mime_type != mMediaPanels[ panel ]->mMimeType )
+ {
+ replaceMediaPanel( mMediaPanels[ panel ], uri );
+ }
+ else
+ {
+ mMediaPanels[ panel ]->mMediaSource->loadURI( uri );
+ mMediaPanels[ panel ]->mMediaSource->start();
+ };
+ };
+
+ time( &last_bookmark_time );
+ };
+ };
+*/
+ // update UI
+ if ( mSelectedPanel )
+ {
+ // set volume based on slider if we have time media
+ //if ( mGluiMediaTimeControlWindowFlag )
+ //{
+ // mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f );
+ //};
+
+ // NOTE: it is absurd that we need cache the state of GLUI controls
+ // but enabling/disabling controls drags framerate from 500+
+ // down to 15. Not a problem for plugin system - only this test
+ // enable/disable time based UI controls based on type of plugin
+ if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() )
+ {
+ /*
+ if ( ! mGluiMediaTimeControlWindowFlag )
+ {
+ mGluiMediaTimeControlWindow->enable();
+ mGluiMediaTimeControlWindowFlag = true;
+ };
+ */
+ }
+ else
+ {
+ /*
+ if ( mGluiMediaTimeControlWindowFlag )
+ {
+ mGluiMediaTimeControlWindow->disable();
+ mGluiMediaTimeControlWindowFlag = false;
+ };
+ */
+ };
+
+ // enable/disable browser based UI controls based on type of plugin
+ if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() )
+ {
+ if ( ! mGluiMediaBrowserControlWindowFlag )
+ {
+ mGluiMediaBrowserControlWindow->enable();
+ mGluiMediaBrowserControlWindowFlag = true;
+ };
+ }
+ else
+ {
+ if ( mGluiMediaBrowserControlWindowFlag )
+ {
+ mGluiMediaBrowserControlWindow->disable();
+ mGluiMediaBrowserControlWindowFlag = false;
+ };
+ };
+
+ // enable/disable browser back button depending on browser history
+ if ( mSelectedPanel->mMediaSource->getHistoryBackAvailable() )
+ {
+ if ( ! mMediaBrowserControlBackButtonFlag )
+ {
+ mMediaBrowserControlBackButton->enable();
+ mMediaBrowserControlBackButtonFlag = true;
+ };
+ }
+ else
+ {
+ if ( mMediaBrowserControlBackButtonFlag )
+ {
+ mMediaBrowserControlBackButton->disable();
+ mMediaBrowserControlBackButtonFlag = false;
+ };
+ };
+
+ // enable/disable browser forward button depending on browser history
+ if ( mSelectedPanel->mMediaSource->getHistoryForwardAvailable() )
+ {
+ if ( ! mMediaBrowserControlForwardButtonFlag )
+ {
+ mMediaBrowserControlForwardButton->enable();
+ mMediaBrowserControlForwardButtonFlag = true;
+ };
+ }
+ else
+ {
+ if ( mMediaBrowserControlForwardButtonFlag )
+ {
+ mMediaBrowserControlForwardButton->disable();
+ mMediaBrowserControlForwardButtonFlag = false;
+ };
+ };
+
+ // NOTE: This is *very* slow and not worth optimising
+ updateStatusBar();
+ };
+
+ // update all the panels
+ for( int panel_index = 0; panel_index < (int)mMediaPanels.size(); ++panel_index )
+ {
+ mediaPanel *panel = mMediaPanels[ panel_index ];
+
+ // call plugins idle function so it can potentially update itself
+ panel->mMediaSource->idle();
+
+ // update each media panel
+ updateMediaPanel( panel );
+
+ LLRect dirty_rect;
+ if ( ! panel->mMediaSource->textureValid() )
+ {
+ //std::cout << "texture invalid, skipping update..." << std::endl;
+ }
+ else
+ if ( panel &&
+ ( panel->mMediaWidth != panel->mMediaSource->getWidth() ||
+ panel->mMediaHeight != panel->mMediaSource->getHeight() ) )
+ {
+ //std::cout << "Resize in progress, skipping update..." << std::endl;
+ }
+ else
+ if ( panel->mMediaSource->getDirty( &dirty_rect ) )
+ {
+ const unsigned char* pixels = panel->mMediaSource->getBitsData();
+ if ( pixels && isTexture(panel->mMediaTextureHandle))
+ {
+ int x_offset = dirty_rect.mLeft;
+ int y_offset = dirty_rect.mBottom;
+ int width = dirty_rect.mRight - dirty_rect.mLeft;
+ int height = dirty_rect.mTop - dirty_rect.mBottom;
+
+ if((dirty_rect.mRight <= panel->mTextureWidth) && (dirty_rect.mTop <= panel->mTextureHeight))
+ {
+ // Offset the pixels pointer properly
+ pixels += ( y_offset * panel->mMediaSource->getTextureDepth() * panel->mMediaSource->getBitsWidth() );
+ pixels += ( x_offset * panel->mMediaSource->getTextureDepth() );
+
+ // set up texture
+ bindTexture( panel->mMediaTextureHandle, panel->mMediaSource->getBitsWidth() );
+ if ( mFuzzyMedia )
+ {
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ }
+ else
+ {
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ };
+
+ checkGLError("glTexParameteri");
+
+ if(panel->mMediaSource->getTextureFormatSwapBytes())
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ checkGLError("glPixelStorei");
+ }
+
+ // draw portion that changes into texture
+ glTexSubImage2D( GL_TEXTURE_2D, 0,
+ x_offset,
+ y_offset,
+ width,
+ height,
+ panel->mMediaSource->getTextureFormatPrimary(),
+ panel->mMediaSource->getTextureFormatType(),
+ pixels );
+
+ if(checkGLError("glTexSubImage2D"))
+ {
+ std::cerr << " panel ID=" << panel->mId << std::endl;
+ std::cerr << " texture size = " << panel->mTextureWidth << " x " << panel->mTextureHeight << std::endl;
+ std::cerr << " media size = " << panel->mMediaWidth << " x " << panel->mMediaHeight << std::endl;
+ std::cerr << " dirty rect = " << dirty_rect.mLeft << ", " << dirty_rect.mBottom << ", " << dirty_rect.mRight << ", " << dirty_rect.mTop << std::endl;
+ std::cerr << " texture width = " << panel->mMediaSource->getBitsWidth() << std::endl;
+ std::cerr << " format primary = 0x" << std::hex << panel->mMediaSource->getTextureFormatPrimary() << std::dec << std::endl;
+ std::cerr << " format type = 0x" << std::hex << panel->mMediaSource->getTextureFormatType() << std::dec << std::endl;
+ std::cerr << " pixels = " << (void*)pixels << std::endl;
+ }
+
+ if(panel->mMediaSource->getTextureFormatSwapBytes())
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ checkGLError("glPixelStorei");
+ }
+
+ panel->mMediaSource->resetDirty();
+
+ panel->mReadyToRender = true;
+ }
+ else
+ {
+ std::cerr << "dirty rect is outside current media size, skipping update" << std::endl;
+ }
+ };
+ };
+ };
+
+ // GLUI requires this
+ if ( glutGetWindow() != mAppWindow )
+ glutSetWindow( mAppWindow );
+
+ // trigger re-display
+ glutPostRedisplay();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::windowPosToTexturePos( int window_x, int window_y,
+ int& media_x, int& media_y,
+ int& id )
+{
+ if ( ! mSelectedPanel )
+ {
+ media_x = 0;
+ media_y = 0;
+ id = 0;
+ return;
+ };
+
+ // record cursor poisiton for a readback next frame
+ mCurMouseX = window_x;
+ // OpenGL app == coordinate system this way
+ // NOTE: unrelated to settings in plugin - this
+ // is just for this app
+ mCurMouseY = mWindowHeight - window_y;
+
+ // extract x (0..1023, y (0..1023) and id (0..15) from RGB components
+ unsigned long pixel_read_color_bits = ( mPixelReadColor[ 0 ] << 16 ) | ( mPixelReadColor[ 1 ] << 8 ) | mPixelReadColor[ 2 ];
+ int texture_x = pixel_read_color_bits & 0x3ff;
+ int texture_y = ( pixel_read_color_bits >> 10 ) & 0x3ff;
+ id = ( pixel_read_color_bits >> 20 ) & 0x0f;
+
+ // scale to size of media (1024 because we use 10 bits for X and Y from 24)
+ media_x = (int)( ( (float)mSelectedPanel->mMediaWidth * (float)texture_x ) / 1024.0f );
+ media_y = (int)( ( (float)mSelectedPanel->mMediaHeight * (float)texture_y ) / 1024.0f );
+
+ // we assume the plugin uses an inverted coordinate scheme like OpenGL
+ // if not, the plugin code inverts the Y coordinate for us - we don't need to
+ media_y = mSelectedPanel->mMediaHeight - media_y;
+
+ if ( media_x > 0 && media_y > 0 )
+ {
+ //std::cout << " mouse coords: " << mCurMouseX << " x " << mCurMouseY << " and id = " << id << std::endl;
+ //std::cout << "raw texture coords: " << texture_x << " x " << texture_y << " and id = " << id << std::endl;
+ //std::cout << " media coords: " << media_x << " x " << media_y << " and id = " << id << std::endl;
+ //std::cout << std::endl;
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::selectPanelById( int id )
+{
+ for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel )
+ {
+ if ( mMediaPanels[ panel ]->mId == id )
+ {
+ selectPanel(mMediaPanels[ panel ]);
+ return;
+ };
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::selectPanel( mediaPanel* panel )
+{
+ if( mSelectedPanel == panel )
+ return;
+
+ // turn off volume before we delete it
+ if( mSelectedPanel && mSelectedPanel->mMediaSource )
+ {
+ mSelectedPanel->mMediaSource->setVolume( 0.0f );
+ mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_LOW );
+ };
+
+ mSelectedPanel = panel;
+
+ if( mSelectedPanel && mSelectedPanel->mMediaSource )
+ {
+ //mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f );
+ mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_NORMAL );
+
+ if(!mSelectedPanel->mStartUrl.empty())
+ {
+ mUrlEdit->set_text(const_cast<char*>(mSelectedPanel->mStartUrl.c_str()) );
+ }
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+mediaPanel* LLFBConnectTest::findMediaPanel( LLPluginClassMedia* source )
+{
+ mediaPanel *result = NULL;
+
+ for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel )
+ {
+ if ( mMediaPanels[ panel ]->mMediaSource == source )
+ {
+ result = mMediaPanels[ panel ];
+ }
+ }
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+mediaPanel* LLFBConnectTest::findMediaPanel( const std::string &target_name )
+{
+ mediaPanel *result = NULL;
+
+ for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel )
+ {
+ if ( mMediaPanels[ panel ]->mTarget == target_name )
+ {
+ result = mMediaPanels[ panel ];
+ }
+ }
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::navigateToNewURI( std::string uri )
+{
+ if ( uri.length() )
+ {
+ std::string mime_type = mimeTypeFromUrl( uri );
+
+ if ( !mSelectedPanel->mMediaSource->isPluginExited() && (mime_type == mSelectedPanel->mMimeType) )
+ {
+ std::cout << "MIME type is the same" << std::endl;
+ mSelectedPanel->mMediaSource->loadURI( uri );
+ mSelectedPanel->mMediaSource->start();
+ mBookmarkList->do_selection( 0 );
+ }
+ else
+ {
+ std::cout << "MIME type changed or plugin had exited" << std::endl;
+ replaceMediaPanel( mSelectedPanel, uri );
+ mBookmarkList->do_selection( 0 );
+ }
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::initUrlHistory( std::string uris )
+{
+ if ( uris.length() > 0 )
+ {
+ std::cout << "init URL : " << uris << std::endl;
+ LLSD historySD;
+
+ char *cstr, *p;
+ cstr = new char[uris.size()+1];
+ strcpy(cstr, uris.c_str());
+ const char *DELIMS = " ,;";
+ p = strtok(cstr, DELIMS);
+ while (p != NULL) {
+ historySD.insert(0, p);
+ p = strtok(NULL, DELIMS);
+ }
+ mSelectedPanel->mMediaSource->initializeUrlHistory(historySD);
+ delete[] cstr;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::gluiCallback( int control_id )
+{
+ if ( control_id == mIdBookmarks )
+ {
+ std::string uri = mBookmarks[ mSelBookmark ].second;
+
+ navigateToNewURI( uri );
+ }
+ else
+ if ( control_id == mIdUrlEdit)
+ {
+ std::string uri = mUrlEdit->get_text();
+
+ navigateToNewURI( uri );
+ }
+/*
+ else
+ if ( control_id == mIdUrlInitHistoryEdit )
+ {
+ std::string uri = mUrlInitHistoryEdit->get_text();
+
+ initUrlHistory( uri );
+ }
+ else
+ if ( control_id == mIdControlAddPanel )
+ {
+ addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second );
+ }
+ else
+ if ( control_id == mIdControlRemPanel )
+ {
+ remMediaPanel( mSelectedPanel );
+ }
+ else
+ if ( control_id == mIdDisableTimeout )
+ {
+ // Set the "disable timeout" flag for all active plugins.
+ for( int i = 0; i < (int)mMediaPanels.size(); ++i )
+ {
+ mMediaPanels[ i ]->mMediaSource->setDisableTimeout(mDisableTimeout);
+ }
+ }
+ else
+ if ( control_id == mIdUsePluginReadThread )
+ {
+ LLPluginProcessParent::setUseReadThread(mUsePluginReadThread);
+ }
+ else
+ if ( control_id == mIdControlCrashPlugin )
+ {
+ // send message to plugin and ask it to crash
+ // (switch out for ReleaseCandidate version :) )
+ if(mSelectedPanel && mSelectedPanel->mMediaSource)
+ {
+ mSelectedPanel->mMediaSource->crashPlugin();
+ }
+ }
+ else
+ if ( control_id == mIdControlHangPlugin )
+ {
+ // send message to plugin and ask it to hang
+ // (switch out for ReleaseCandidate version :) )
+ if(mSelectedPanel && mSelectedPanel->mMediaSource)
+ {
+ mSelectedPanel->mMediaSource->hangPlugin();
+ }
+ }
+ else
+*/
+ if ( control_id == mIdControlExitApp )
+ {
+ // text for exiting plugin system cleanly
+ delete this; // clean up
+ exit( 0 );
+ }
+/*
+ else
+ if ( control_id == mIdMediaTimeControlPlay )
+ {
+ if ( mSelectedPanel )
+ {
+ mSelectedPanel->mMediaSource->setLoop( false );
+ mSelectedPanel->mMediaSource->start();
+ };
+ }
+ else
+ if ( control_id == mIdMediaTimeControlLoop )
+ {
+ if ( mSelectedPanel )
+ {
+ mSelectedPanel->mMediaSource->setLoop( true );
+ mSelectedPanel->mMediaSource->start();
+ };
+ }
+ else
+ if ( control_id == mIdMediaTimeControlPause )
+ {
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->pause();
+ }
+ else
+ if ( control_id == mIdMediaTimeControlStop )
+ {
+ if ( mSelectedPanel )
+ {
+ mSelectedPanel->mMediaSource->stop();
+ };
+ }
+ else
+ if ( control_id == mIdMediaTimeControlSeek )
+ {
+ if ( mSelectedPanel )
+ {
+ // get value from spinner
+ float seconds_to_seek = mMediaTimeControlSeekSeconds;
+ mSelectedPanel->mMediaSource->seek( seconds_to_seek );
+ mSelectedPanel->mMediaSource->start();
+ };
+ }
+ else
+ if ( control_id == mIdMediaTimeControlRewind )
+ {
+ if ( mSelectedPanel )
+ {
+ mSelectedPanel->mMediaSource->setLoop( false );
+ mSelectedPanel->mMediaSource->start(-2.0f);
+ };
+ }
+ else
+ if ( control_id == mIdMediaTimeControlFastForward )
+ {
+ if ( mSelectedPanel )
+ {
+ mSelectedPanel->mMediaSource->setLoop( false );
+ mSelectedPanel->mMediaSource->start(2.0f);
+ };
+ }
+ else
+*/
+ if ( control_id == mIdMediaBrowserControlBack )
+ {
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->browse_back();
+ }
+ else
+ if ( control_id == mIdMediaBrowserControlStop )
+ {
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->browse_stop();
+ }
+ else
+ if ( control_id == mIdMediaBrowserControlForward )
+ {
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->browse_forward();
+ }
+ else
+ if ( control_id == mIdMediaBrowserControlHome )
+ {
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->loadURI( mHomeWebUrl );
+ }
+ else
+ if ( control_id == mIdMediaBrowserControlReload )
+ {
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->browse_reload( true );
+ }
+/*
+ else
+ if ( control_id == mIdMediaBrowserControlClearCache )
+ {
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->clear_cache();
+ }
+ else
+ if ( control_id == mIdMediaBrowserControlClearCookies )
+ {
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->clear_cookies();
+ }
+ else
+ if ( control_id == mIdMediaBrowserControlEnableCookies )
+ {
+ if ( mSelectedPanel )
+ {
+ if ( mMediaBrowserControlEnableCookies )
+ {
+ mSelectedPanel->mMediaSource->enable_cookies( true );
+ }
+ else
+ {
+ mSelectedPanel->mMediaSource->enable_cookies( false );
+ }
+ };
+ };
+*/
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::keyboard( int key )
+{
+ //if ( key == 'a' || key == 'A' )
+ // addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second );
+ //else
+ //if ( key == 'r' || key == 'R' )
+ // remMediaPanel( mSelectedPanel );
+ //else
+ //if ( key == 'd' || key == 'D' )
+ // dumpPanelInfo();
+ //else
+ if ( key == 27 )
+ {
+ std::cout << "Application finished - exiting..." << std::endl;
+ delete this;
+ exit( 0 );
+ };
+
+ mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_DOWN, key, 0 , LLSD());
+ mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_UP, key, 0, LLSD());
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::mouseButton( int button, int state, int x, int y )
+{
+ if ( button == GLUT_LEFT_BUTTON )
+ {
+ if ( state == GLUT_DOWN )
+ {
+ int media_x, media_y, id;
+ windowPosToTexturePos( x, y, media_x, media_y, id );
+
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_DOWN, 0, media_x, media_y, 0 );
+ }
+ else
+ if ( state == GLUT_UP )
+ {
+ int media_x, media_y, id;
+ windowPosToTexturePos( x, y, media_x, media_y, id );
+
+ // only select a panel if we're on a panel
+ // (HACK: strictly speaking this rules out clicking on
+ // the origin of a panel but that's very unlikely)
+ if ( media_x > 0 && media_y > 0 )
+ {
+ selectPanelById( id );
+
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_UP, 0, media_x, media_y, 0 );
+ };
+ };
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::mousePassive( int x, int y )
+{
+ int media_x, media_y, id;
+ windowPosToTexturePos( x, y, media_x, media_y, id );
+
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, 0, media_x, media_y, 0 );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::mouseMove( int x, int y )
+{
+ int media_x, media_y, id;
+ windowPosToTexturePos( x, y, media_x, media_y, id );
+
+ if ( mSelectedPanel )
+ mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, 0, media_x, media_y, 0 );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::makeChrome()
+{
+ // IDs used by GLUI
+ int start_id = 0x1000;
+
+ // right side window - geometry manipulators
+#if __APPLE__
+ // the Apple GLUT implementation doesn't seem to set the graphic offset of subwindows correctly when they overlap in certain ways.
+ // Use a separate controls window in this case.
+ // GLUI window at right containing manipulation controls and other buttons
+ int x = glutGet(GLUT_WINDOW_X) + glutGet(GLUT_WINDOW_WIDTH) + 4;
+ int y = glutGet(GLUT_WINDOW_Y);
+ GLUI* right_glui_window = GLUI_Master.create_glui( "", 0, x, y );
+#else
+ GLUI* right_glui_window = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_RIGHT );
+#endif
+ mViewRotationCtrl = right_glui_window->add_rotation( "Rotation", mViewRotation );
+ mViewTranslationCtrl = right_glui_window->add_translation( "Translate", GLUI_TRANSLATION_XY, mViewPos );
+ mViewTranslationCtrl->set_speed( 0.01f );
+ mViewScaleCtrl = right_glui_window->add_translation( "Scale", GLUI_TRANSLATION_Z, &mViewPos[ 2 ] );
+ mViewScaleCtrl->set_speed( 0.05f );
+ right_glui_window->set_main_gfx_window( mAppWindow );
+
+ // right side window - app controls
+ /*
+ mIdControlAddPanel = start_id++;
+ right_glui_window->add_statictext( "" );
+ right_glui_window->add_separator();
+ right_glui_window->add_statictext( "" );
+ right_glui_window->add_button( "Add panel", mIdControlAddPanel, gluiCallbackWrapper );
+ right_glui_window->add_statictext( "" );
+ mIdControlRemPanel = start_id++;
+ right_glui_window->add_button( "Rem panel", mIdControlRemPanel, gluiCallbackWrapper );
+ right_glui_window->add_statictext( "" );
+ right_glui_window->add_separator();
+ right_glui_window->add_statictext( "" );
+ mIdControlCrashPlugin = start_id++;
+ right_glui_window->add_button( "Crash plugin", mIdControlCrashPlugin, gluiCallbackWrapper );
+ mIdControlHangPlugin = start_id++;
+ right_glui_window->add_button( "Hang plugin", mIdControlHangPlugin, gluiCallbackWrapper );
+ */
+ right_glui_window->add_statictext( "" );
+ right_glui_window->add_separator();
+ right_glui_window->add_statictext( "" );
+ mIdControlExitApp = start_id++;
+ right_glui_window->add_button( "Exit app", mIdControlExitApp, gluiCallbackWrapper );
+
+ //// top window - holds bookmark UI
+ mIdBookmarks = start_id++;
+ mSelBookmark = 0;
+ GLUI* glui_window_top = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP );
+ mBookmarkList = glui_window_top->add_listbox( "", &mSelBookmark, mIdBookmarks, gluiCallbackWrapper );
+ // only add the first 50 bookmarks - list can be very long sometimes (30,000+)
+ // when testing list of media URLs from AGNI for example
+ for( unsigned int each = 0; each < mBookmarks.size() && each < 50; ++each )
+ mBookmarkList->add_item( each, const_cast< char* >( mBookmarks[ each ].first.c_str() ) );
+ glui_window_top->set_main_gfx_window( mAppWindow );
+
+ glui_window_top->add_column( false );
+ mIdUrlEdit = start_id++;
+ mUrlEdit = glui_window_top->add_edittext( "Url:", GLUI_EDITTEXT_TEXT, 0, mIdUrlEdit, gluiCallbackWrapper );
+ mUrlEdit->set_w( 600 );
+ //GLUI* glui_window_top2 = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP );
+ //mIdUrlInitHistoryEdit = start_id++;
+ //mUrlInitHistoryEdit = glui_window_top2->add_edittext( "Init History (separate by commas or semicolons):",
+ // GLUI_EDITTEXT_TEXT, 0, mIdUrlInitHistoryEdit, gluiCallbackWrapper );
+ //mUrlInitHistoryEdit->set_w( 800 );
+
+ // top window - media controls for "time" media types (e.g. movies)
+/*
+ mGluiMediaTimeControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP );
+ mGluiMediaTimeControlWindow->set_main_gfx_window( mAppWindow );
+ mIdMediaTimeControlPlay = start_id++;
+ mGluiMediaTimeControlWindow->add_button( "PLAY", mIdMediaTimeControlPlay, gluiCallbackWrapper );
+ mGluiMediaTimeControlWindow->add_column( false );
+ mIdMediaTimeControlLoop = start_id++;
+ mGluiMediaTimeControlWindow->add_button( "LOOP", mIdMediaTimeControlLoop, gluiCallbackWrapper );
+ mGluiMediaTimeControlWindow->add_column( false );
+ mIdMediaTimeControlPause = start_id++;
+ mGluiMediaTimeControlWindow->add_button( "PAUSE", mIdMediaTimeControlPause, gluiCallbackWrapper );
+ mGluiMediaTimeControlWindow->add_column( false );
+
+ GLUI_Button *button;
+ mIdMediaTimeControlRewind = start_id++;
+ button = mGluiMediaTimeControlWindow->add_button( "<<", mIdMediaTimeControlRewind, gluiCallbackWrapper );
+ button->set_w(30);
+ mGluiMediaTimeControlWindow->add_column( false );
+ mIdMediaTimeControlFastForward = start_id++;
+ button = mGluiMediaTimeControlWindow->add_button( ">>", mIdMediaTimeControlFastForward, gluiCallbackWrapper );
+ button->set_w(30);
+
+ mGluiMediaTimeControlWindow->add_column( true );
+
+ mIdMediaTimeControlStop = start_id++;
+ mGluiMediaTimeControlWindow->add_button( "STOP", mIdMediaTimeControlStop, gluiCallbackWrapper );
+ mGluiMediaTimeControlWindow->add_column( false );
+ mIdMediaTimeControlVolume = start_id++;
+ GLUI_Spinner* spinner = mGluiMediaTimeControlWindow->add_spinner( "Volume", 2, &mMediaTimeControlVolume, mIdMediaTimeControlVolume, gluiCallbackWrapper);
+ spinner->set_float_limits( 0, 100 );
+ mGluiMediaTimeControlWindow->add_column( true );
+ mIdMediaTimeControlSeekSeconds = start_id++;
+ spinner = mGluiMediaTimeControlWindow->add_spinner( "", 2, &mMediaTimeControlSeekSeconds, mIdMediaTimeControlSeekSeconds, gluiCallbackWrapper);
+ spinner->set_float_limits( 0, 200 );
+ spinner->set_w( 32 );
+ spinner->set_speed( 0.025f );
+ mGluiMediaTimeControlWindow->add_column( false );
+ mIdMediaTimeControlSeek = start_id++;
+ mGluiMediaTimeControlWindow->add_button( "SEEK", mIdMediaTimeControlSeek, gluiCallbackWrapper );
+ mGluiMediaTimeControlWindow->add_column( false );
+*/
+
+ // top window - media controls for "browser" media types (e.g. web browser)
+ mGluiMediaBrowserControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP );
+ mGluiMediaBrowserControlWindow->set_main_gfx_window( mAppWindow );
+ mIdMediaBrowserControlBack = start_id++;
+ mMediaBrowserControlBackButton = mGluiMediaBrowserControlWindow->add_button( "BACK", mIdMediaBrowserControlBack, gluiCallbackWrapper );
+ mGluiMediaBrowserControlWindow->add_column( false );
+ mIdMediaBrowserControlStop = start_id++;
+ mGluiMediaBrowserControlWindow->add_button( "STOP", mIdMediaBrowserControlStop, gluiCallbackWrapper );
+ mGluiMediaBrowserControlWindow->add_column( false );
+ mIdMediaBrowserControlForward = start_id++;
+ mMediaBrowserControlForwardButton = mGluiMediaBrowserControlWindow->add_button( "FORWARD", mIdMediaBrowserControlForward, gluiCallbackWrapper );
+ mGluiMediaBrowserControlWindow->add_column( false );
+ mIdMediaBrowserControlHome = start_id++;
+ mGluiMediaBrowserControlWindow->add_button( "HOME", mIdMediaBrowserControlHome, gluiCallbackWrapper );
+ mGluiMediaBrowserControlWindow->add_column( false );
+ mIdMediaBrowserControlReload = start_id++;
+ mGluiMediaBrowserControlWindow->add_button( "RELOAD", mIdMediaBrowserControlReload, gluiCallbackWrapper );
+ mGluiMediaBrowserControlWindow->add_column( false );
+ /*
+ mIdMediaBrowserControlClearCache = start_id++;
+ mGluiMediaBrowserControlWindow->add_button( "CLEAR CACHE", mIdMediaBrowserControlClearCache, gluiCallbackWrapper );
+ mGluiMediaBrowserControlWindow->add_column( false );
+ mIdMediaBrowserControlClearCookies = start_id++;
+ mGluiMediaBrowserControlWindow->add_button( "CLEAR COOKIES", mIdMediaBrowserControlClearCookies, gluiCallbackWrapper );
+ mGluiMediaBrowserControlWindow->add_column( false );
+ mIdMediaBrowserControlEnableCookies = start_id++;
+ mMediaBrowserControlEnableCookies = 0;
+ mGluiMediaBrowserControlWindow->add_checkbox( "Enable Cookies", &mMediaBrowserControlEnableCookies, mIdMediaBrowserControlEnableCookies, gluiCallbackWrapper );
+
+ // top window - misc controls
+ GLUI* glui_window_misc_control = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP );
+ mIdRandomPanelCount = start_id++;
+ mRandomPanelCount = 0;
+ glui_window_misc_control->add_checkbox( "Randomize panel count", &mRandomPanelCount, mIdRandomPanelCount, gluiCallbackWrapper );
+ glui_window_misc_control->set_main_gfx_window( mAppWindow );
+ glui_window_misc_control->add_column( true );
+ mIdRandomBookmarks = start_id++;
+ mRandomBookmarks = 0;
+ glui_window_misc_control->add_checkbox( "Randomize bookmarks", &mRandomBookmarks, mIdRandomBookmarks, gluiCallbackWrapper );
+ glui_window_misc_control->set_main_gfx_window( mAppWindow );
+ glui_window_misc_control->add_column( true );
+
+ mIdDisableTimeout = start_id++;
+ mDisableTimeout = 0;
+ glui_window_misc_control->add_checkbox( "Disable plugin timeout", &mDisableTimeout, mIdDisableTimeout, gluiCallbackWrapper );
+ glui_window_misc_control->set_main_gfx_window( mAppWindow );
+ glui_window_misc_control->add_column( true );
+
+ mIdUsePluginReadThread = start_id++;
+ mUsePluginReadThread = 0;
+ glui_window_misc_control->add_checkbox( "Use plugin read thread", &mUsePluginReadThread, mIdUsePluginReadThread, gluiCallbackWrapper );
+ glui_window_misc_control->set_main_gfx_window( mAppWindow );
+ glui_window_misc_control->add_column( true );
+
+ mIdLargePanelSpacing = start_id++;
+ mLargePanelSpacing = 0;
+ glui_window_misc_control->add_checkbox( "Large Panel Spacing", &mLargePanelSpacing, mIdLargePanelSpacing, gluiCallbackWrapper );
+ glui_window_misc_control->set_main_gfx_window( mAppWindow );
+ glui_window_misc_control->add_column( true );
+*/
+ // bottom window - status
+ mBottomGLUIWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_BOTTOM );
+ mStatusText = mBottomGLUIWindow->add_statictext( "" );
+ mBottomGLUIWindow->set_main_gfx_window( mAppWindow );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::resetView()
+{
+ mViewRotationCtrl->reset();
+
+ mViewScaleCtrl->set_x( 0.0f );
+ mViewScaleCtrl->set_y( 0.0f );
+ mViewScaleCtrl->set_z( 1.3f );
+
+ mViewTranslationCtrl->set_x( 0.0f );
+ mViewTranslationCtrl->set_y( 0.0f );
+ mViewTranslationCtrl->set_z( 0.0f );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels )
+{
+ int pick_texture_width = 1024;
+ int pick_texture_height = 1024;
+ int pick_texture_depth = 3;
+ unsigned char* ptr = new unsigned char[ pick_texture_width * pick_texture_height * pick_texture_depth ];
+ for( int y = 0; y < pick_texture_height; ++y )
+ {
+ for( int x = 0; x < pick_texture_width * pick_texture_depth ; x += pick_texture_depth )
+ {
+ unsigned long bits = 0L;
+ bits |= ( id << 20 ) | ( y << 10 ) | ( x / 3 );
+ unsigned char r_component = ( bits >> 16 ) & 0xff;
+ unsigned char g_component = ( bits >> 8 ) & 0xff;
+ unsigned char b_component = bits & 0xff;
+
+ ptr[ y * pick_texture_width * pick_texture_depth + x + 0 ] = r_component;
+ ptr[ y * pick_texture_width * pick_texture_depth + x + 1 ] = g_component;
+ ptr[ y * pick_texture_width * pick_texture_depth + x + 2 ] = b_component;
+ };
+ };
+
+ glGenTextures( 1, texture_handle );
+
+ checkGLError("glGenTextures");
+ std::cout << "glGenTextures returned " << *texture_handle << std::endl;
+
+ bindTexture( *texture_handle );
+ glTexImage2D( GL_TEXTURE_2D, 0,
+ GL_RGB,
+ pick_texture_width, pick_texture_height,
+ 0, GL_RGB, GL_UNSIGNED_BYTE, ptr );
+
+ *texture_pixels = ptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+std::string LLFBConnectTest::mimeTypeFromUrl( std::string& url )
+{
+ // default to web
+ std::string mime_type = "text/html";
+
+ // we may need a more advanced MIME type accessor later :-)
+ if ( url.find( ".mov" ) != std::string::npos ) // Movies
+ mime_type = "video/quicktime";
+ else
+ if ( url.find( ".txt" ) != std::string::npos ) // Apple Text descriptors
+ mime_type = "video/quicktime";
+ else
+ if ( url.find( ".mp3" ) != std::string::npos ) // Apple Text descriptors
+ mime_type = "video/quicktime";
+ else
+ if ( url.find( "example://" ) != std::string::npos ) // Example plugin
+ mime_type = "example/example";
+
+ return mime_type;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+std::string LLFBConnectTest::pluginNameFromMimeType( std::string& mime_type )
+{
+#if LL_DARWIN
+ std::string plugin_name( "media_plugin_null.dylib" );
+ if ( mime_type == "video/quicktime" )
+ plugin_name = "media_plugin_quicktime.dylib";
+ else
+ if ( mime_type == "text/html" )
+ plugin_name = "media_plugin_webkit.dylib";
+
+#elif LL_WINDOWS
+ std::string plugin_name( "media_plugin_null.dll" );
+
+ if ( mime_type == "video/quicktime" )
+ plugin_name = "media_plugin_quicktime.dll";
+ else
+ if ( mime_type == "text/html" )
+ plugin_name = "media_plugin_webkit.dll";
+ else
+ if ( mime_type == "example/example" )
+ plugin_name = "media_plugin_example.dll";
+
+#elif LL_LINUX
+ std::string plugin_name( "libmedia_plugin_null.so" );
+
+ if ( mime_type == "video/quicktime" )
+ plugin_name = "libmedia_plugin_quicktime.so";
+ else
+ if ( mime_type == "text/html" )
+ plugin_name = "libmedia_plugin_webkit.so";
+#endif
+ return plugin_name;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+mediaPanel* LLFBConnectTest::addMediaPanel( std::string url )
+{
+ // Get the plugin filename using the URL
+ std::string mime_type = mimeTypeFromUrl( url );
+ std::string plugin_name = pluginNameFromMimeType( mime_type );
+
+ // create a random size for the new media
+ int media_width;
+ int media_height;
+ getRandomMediaSize( media_width, media_height, mime_type );
+ media_width = 1024;
+ media_height = 1536;
+
+ // make a new plugin
+ LLPluginClassMedia* media_source = new LLPluginClassMedia(this);
+
+ // enable cookies so the FB login works
+ media_source->enable_cookies(true);
+
+ // tell the plugin what size we asked for
+ media_source->setSize( media_width, media_height );
+
+ // Use the launcher start and initialize the plugin
+#if LL_DARWIN || LL_LINUX
+ std::string launcher_name( "SLPlugin" );
+#elif LL_WINDOWS
+ std::string launcher_name( "SLPlugin.exe" );
+#endif
+
+ // for this test app, use the cwd as the user data path (ugh).
+#if LL_WINDOWS
+ std::string user_data_path = ".\\";
+#else
+ char cwd[ FILENAME_MAX ];
+ if (NULL == getcwd( cwd, FILENAME_MAX - 1 ))
+ {
+ std::cerr << "Couldn't get cwd - probably too long - failing to init." << std::endl;
+ return NULL;
+ }
+ std::string user_data_path = std::string( cwd ) + "/";
+#endif
+ media_source->setUserDataPath(user_data_path);
+ media_source->init( launcher_name, user_data_path, plugin_name, false );
+ //media_source->setDisableTimeout(mDisableTimeout);
+
+ // make a new panel and save parameters
+ mediaPanel* panel = new mediaPanel;
+ panel->mMediaSource = media_source;
+ panel->mStartUrl = url;
+ panel->mMimeType = mime_type;
+ panel->mMediaWidth = media_width;
+ panel->mMediaHeight = media_height;
+ panel->mTextureWidth = 0;
+ panel->mTextureHeight = 0;
+ panel->mTextureScaleX = 0;
+ panel->mTextureScaleY = 0;
+ panel->mMediaTextureHandle = 0;
+ panel->mPickTextureHandle = 0;
+ panel->mAppTextureCoordsOpenGL = false; // really need an 'undefined' state here too
+ panel->mReadyToRender = false;
+
+ // look through current media panels to find an unused index number
+ bool id_exists = true;
+ for( int nid = 0; nid < mMaxPanels; ++nid )
+ {
+ // does this id exist already?
+ id_exists = false;
+ for( int pid = 0; pid < (int)mMediaPanels.size(); ++pid )
+ {
+ if ( nid == mMediaPanels[ pid ]->mId )
+ {
+ id_exists = true;
+ break;
+ };
+ };
+
+ // id wasn't found so we can use it
+ if ( ! id_exists )
+ {
+ panel->mId = nid;
+ break;
+ };
+ };
+
+ // if we get here and this flag is set, there is no room for any more panels
+ if ( id_exists )
+ {
+ std::cout << "No room for any more panels" << std::endl;
+ }
+ else
+ {
+ // now we have the ID we can use it to make the
+ // pick texture (id is baked into texture pixels)
+ makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels );
+
+ // save this in the list of panels
+ mMediaPanels.push_back( panel );
+
+ // select the panel that was just created
+ selectPanel( panel );
+
+ // load and start the URL
+ panel->mMediaSource->loadURI( url );
+ panel->mMediaSource->start();
+
+ std::cout << "Adding new media panel for " << url << "(" << media_width << "x" << media_height << ") with index " << panel->mId << " - total panels = " << mMediaPanels.size() << std::endl;
+ }
+
+ return panel;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::updateMediaPanel( mediaPanel* panel )
+{
+// checkGLError("LLFBConnectTest::updateMediaPanel");
+
+ if ( ! panel )
+ return;
+
+ if(!panel->mMediaSource || !panel->mMediaSource->textureValid())
+ {
+ panel->mReadyToRender = false;
+ return;
+ }
+
+ // take a reference copy of the plugin values since they
+ // might change during this lifetime of this function
+ int plugin_media_width = panel->mMediaSource->getWidth();
+ int plugin_media_height = panel->mMediaSource->getHeight();
+ int plugin_texture_width = panel->mMediaSource->getBitsWidth();
+ int plugin_texture_height = panel->mMediaSource->getBitsHeight();
+
+ // If the texture isn't created or the media or texture dimensions changed AND
+ // the sizes are valid then we need to delete the old media texture (if necessary)
+ // then make a new one.
+ if ((panel->mMediaTextureHandle == 0 ||
+ panel->mMediaWidth != plugin_media_width ||
+ panel->mMediaHeight != plugin_media_height ||
+ panel->mTextureWidth != plugin_texture_width ||
+ panel->mTextureHeight != plugin_texture_height) &&
+ ( plugin_media_width > 0 && plugin_media_height > 0 &&
+ plugin_texture_width > 0 && plugin_texture_height > 0 ) )
+ {
+ std::cout << "Valid media size (" << plugin_media_width << " x " << plugin_media_height
+ << ") and texture size (" << plugin_texture_width << " x " << plugin_texture_height
+ << ") for panel with ID=" << panel->mId << " - making texture" << std::endl;
+
+ // delete old GL texture
+ if ( isTexture( panel->mMediaTextureHandle ) )
+ {
+ std::cerr << "updateMediaPanel: deleting texture " << panel->mMediaTextureHandle << std::endl;
+ glDeleteTextures( 1, &panel->mMediaTextureHandle );
+ panel->mMediaTextureHandle = 0;
+ }
+
+ std::cerr << "before: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl;
+
+ // make a GL texture based on the dimensions the plugin told us
+ GLuint new_texture = 0;
+ glGenTextures( 1, &new_texture );
+
+ checkGLError("glGenTextures");
+
+ std::cout << "glGenTextures returned " << new_texture << std::endl;
+
+ panel->mMediaTextureHandle = new_texture;
+
+ bindTexture( panel->mMediaTextureHandle );
+
+ std::cout << "Setting texture size to " << plugin_texture_width << " x " << plugin_texture_height << std::endl;
+ glTexImage2D( GL_TEXTURE_2D, 0,
+ GL_RGB,
+ plugin_texture_width, plugin_texture_height,
+ 0, GL_RGB, GL_UNSIGNED_BYTE,
+ 0 );
+
+
+ std::cerr << "after: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl;
+ };
+
+ // update our record of the media and texture dimensions
+ // NOTE: do this after we we check for sizes changes
+ panel->mMediaWidth = plugin_media_width;
+ panel->mMediaHeight = plugin_media_height;
+ panel->mTextureWidth = plugin_texture_width;
+ panel->mTextureHeight = plugin_texture_height;
+ if ( plugin_texture_width > 0 )
+ {
+ panel->mTextureScaleX = (double)panel->mMediaWidth / (double)panel->mTextureWidth;
+ };
+ if ( plugin_texture_height > 0 )
+ {
+ panel->mTextureScaleY = (double)panel->mMediaHeight / (double)panel->mTextureHeight;
+ };
+
+ // update the flag which tells us if the media source uses OprnGL coords or not.
+ panel->mAppTextureCoordsOpenGL = panel->mMediaSource->getTextureCoordsOpenGL();
+
+ // Check to see if we have enough to render this panel.
+ // If we do, set a flag that the display functions use so
+ // they only render a panel with media if it's ready.
+ if ( panel->mMediaWidth < 0 ||
+ panel->mMediaHeight < 0 ||
+ panel->mTextureWidth < 1 ||
+ panel->mTextureHeight < 1 ||
+ panel->mMediaTextureHandle == 0 )
+ {
+ panel->mReadyToRender = false;
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+mediaPanel* LLFBConnectTest::replaceMediaPanel( mediaPanel* panel, std::string url )
+{
+ // no media panels so we can't change anything - have to add
+ if ( mMediaPanels.size() == 0 )
+ return NULL;
+
+ // sanity check
+ if ( ! panel )
+ return NULL;
+
+ int index;
+ for(index = 0; index < (int)mMediaPanels.size(); index++)
+ {
+ if(mMediaPanels[index] == panel)
+ break;
+ }
+
+ if(index >= (int)mMediaPanels.size())
+ {
+ // panel isn't in mMediaPanels
+ return NULL;
+ }
+
+ std::cout << "Replacing media panel with index " << panel->mId << std::endl;
+
+ int panel_id = panel->mId;
+
+ if(mSelectedPanel == panel)
+ mSelectedPanel = NULL;
+
+ delete panel;
+
+ // Get the plugin filename using the URL
+ std::string mime_type = mimeTypeFromUrl( url );
+ std::string plugin_name = pluginNameFromMimeType( mime_type );
+
+ // create a random size for the new media
+ int media_width;
+ int media_height;
+ getRandomMediaSize( media_width, media_height, mime_type );
+
+ // make a new plugin
+ LLPluginClassMedia* media_source = new LLPluginClassMedia(this);
+
+ // tell the plugin what size we asked for
+ media_source->setSize( media_width, media_height );
+
+ // Use the launcher start and initialize the plugin
+#if LL_DARWIN || LL_LINUX
+ std::string launcher_name( "SLPlugin" );
+#elif LL_WINDOWS
+ std::string launcher_name( "SLPlugin.exe" );
+#endif
+
+ // for this test app, use the cwd as the user data path (ugh).
+#if LL_WINDOWS
+ std::string user_data_path = ".\\";
+#else
+ char cwd[ FILENAME_MAX ];
+ if (NULL == getcwd( cwd, FILENAME_MAX - 1 ))
+ {
+ std::cerr << "Couldn't get cwd - probably too long - failing to init." << std::endl;
+ return NULL;
+ }
+ std::string user_data_path = std::string( cwd ) + "/";
+#endif
+
+ media_source->setUserDataPath(user_data_path);
+ media_source->init( launcher_name, user_data_path, plugin_name, false );
+ //media_source->setDisableTimeout(mDisableTimeout);
+
+ // make a new panel and save parameters
+ panel = new mediaPanel;
+ panel->mMediaSource = media_source;
+ panel->mStartUrl = url;
+ panel->mMimeType = mime_type;
+ panel->mMediaWidth = media_width;
+ panel->mMediaHeight = media_height;
+ panel->mTextureWidth = 0;
+ panel->mTextureHeight = 0;
+ panel->mTextureScaleX = 0;
+ panel->mTextureScaleY = 0;
+ panel->mMediaTextureHandle = 0;
+ panel->mPickTextureHandle = 0;
+ panel->mAppTextureCoordsOpenGL = false; // really need an 'undefined' state here too
+ panel->mReadyToRender = false;
+
+ panel->mId = panel_id;
+
+ // Replace the entry in the panels array
+ mMediaPanels[index] = panel;
+
+ // now we have the ID we can use it to make the
+ // pick texture (id is baked into texture pixels)
+ makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels );
+
+ // select the panel that was just created
+ selectPanel( panel );
+
+ // load and start the URL
+ panel->mMediaSource->loadURI( url );
+ panel->mMediaSource->start();
+
+ return panel;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::getRandomMediaSize( int& width, int& height, std::string mime_type )
+{
+ // Make a new media source with a random size which we'll either
+ // directly or the media plugin will tell us what it wants later.
+ // Use a random size so we can test support for weird media sizes.
+ // (Almost everything else will get filled in later once the
+ // plugin responds)
+ // NB. Do we need to enforce that width is on 4 pixel boundary?
+ width = ( ( rand() % 170 ) + 30 ) * 4;
+ height = ( ( rand() % 170 ) + 30 ) * 4;
+
+ // adjust this random size if it's a browser so we get
+ // a more useful size for testing..
+ if ( mime_type == "text/html" || mime_type == "example/example" )
+ {
+ width = ( ( rand() % 100 ) + 100 ) * 4;
+ height = ( width * ( ( rand() % 400 ) + 1000 ) ) / 1000;
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::remMediaPanel( mediaPanel* panel )
+{
+ // always leave one panel
+ if ( mMediaPanels.size() == 1 )
+ return;
+
+ // sanity check - don't think this can happen but see above for a case where it might...
+ if ( ! panel )
+ return;
+
+ std::cout << "Removing media panel with index " << panel->mId << " - total panels = " << mMediaPanels.size() - 1 << std::endl;
+
+ if(mSelectedPanel == panel)
+ mSelectedPanel = NULL;
+
+ delete panel;
+
+ // remove from storage list
+ for( int i = 0; i < (int)mMediaPanels.size(); ++i )
+ {
+ if ( mMediaPanels[ i ] == panel )
+ {
+ mMediaPanels.erase( mMediaPanels.begin() + i );
+ break;
+ };
+ };
+
+ // select the first panel
+ selectPanel( mMediaPanels[ 0 ] );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::updateStatusBar()
+{
+ if ( ! mSelectedPanel )
+ return;
+
+ // cache results - this is a very slow function
+ static int cached_id = -1;
+ static int cached_media_width = -1;
+ static int cached_media_height = -1;
+ static int cached_texture_width = -1;
+ static int cached_texture_height = -1;
+ static bool cached_supports_browser_media = true;
+ static bool cached_supports_time_media = false;
+ static int cached_movie_time = -1;
+ static GLfloat cached_distance = -1.0f;
+
+ static std::string cached_plugin_version = "";
+ if (
+ cached_id == mSelectedPanel->mId &&
+ cached_media_width == mSelectedPanel->mMediaWidth &&
+ cached_media_height == mSelectedPanel->mMediaHeight &&
+ cached_texture_width == mSelectedPanel->mTextureWidth &&
+ cached_texture_height == mSelectedPanel->mTextureHeight &&
+ cached_supports_browser_media == mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() &&
+ cached_supports_time_media == mSelectedPanel->mMediaSource->pluginSupportsMediaTime() &&
+ cached_plugin_version == mSelectedPanel->mMediaSource->getPluginVersion() &&
+ cached_movie_time == (int)mSelectedPanel->mMediaSource->getCurrentTime() &&
+ cached_distance == mDistanceCameraToSelectedGeometry
+ )
+ {
+ // nothing changed so don't spend time here
+ return;
+ };
+
+ std::ostringstream stream( "" );
+
+ stream.str( "" );
+ stream.clear();
+
+ stream << "Id: ";
+ stream << std::setw( 2 ) << std::setfill( '0' );
+ stream << mSelectedPanel->mId;
+ stream << " | ";
+ stream << "Media: ";
+ stream << std::setw( 3 ) << std::setfill( '0' );
+ stream << mSelectedPanel->mMediaWidth;
+ stream << " x ";
+ stream << std::setw( 3 ) << std::setfill( '0' );
+ stream << mSelectedPanel->mMediaHeight;
+ stream << " | ";
+ stream << "Texture: ";
+ stream << std::setw( 4 ) << std::setfill( '0' );
+ stream << mSelectedPanel->mTextureWidth;
+ stream << " x ";
+ stream << std::setw( 4 ) << std::setfill( '0' );
+ stream << mSelectedPanel->mTextureHeight;
+
+ stream << " | ";
+ stream << "Distance: ";
+ stream << std::setw( 6 );
+ stream << std::setprecision( 3 );
+ stream << std::setprecision( 3 );
+ stream << mDistanceCameraToSelectedGeometry;
+ stream << " | ";
+
+ if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() )
+ stream << "BROWSER";
+ else
+ if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() )
+ stream << "TIME ";
+ stream << " | ";
+ stream << mSelectedPanel->mMediaSource->getPluginVersion();
+ stream << " | ";
+ if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() )
+ {
+ stream << std::setw( 3 ) << std::setfill( '0' );
+ stream << (int)mSelectedPanel->mMediaSource->getCurrentTime();
+ stream << " / ";
+ stream << std::setw( 3 ) << std::setfill( '0' );
+ stream << (int)mSelectedPanel->mMediaSource->getDuration();
+ stream << " @ ";
+ stream << (int)mSelectedPanel->mMediaSource->getCurrentPlayRate();
+ stream << " | ";
+ };
+
+ glutSetWindow( mBottomGLUIWindow->get_glut_window_id() );
+ mStatusText->set_text( const_cast< char*>( stream.str().c_str() ) );
+ glutSetWindow( mAppWindow );
+
+ // caching
+ cached_id = mSelectedPanel->mId;
+ cached_media_width = mSelectedPanel->mMediaWidth;
+ cached_media_height = mSelectedPanel->mMediaHeight;
+ cached_texture_width = mSelectedPanel->mTextureWidth;
+ cached_texture_height = mSelectedPanel->mTextureHeight;
+ cached_supports_browser_media = mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser();
+ cached_supports_time_media = mSelectedPanel->mMediaSource->pluginSupportsMediaTime();
+ cached_plugin_version = mSelectedPanel->mMediaSource->getPluginVersion();
+ cached_movie_time = (int)mSelectedPanel->mMediaSource->getCurrentTime();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::dumpPanelInfo()
+{
+ std::cout << std::endl << "===== Media Panels =====" << std::endl;
+ for( int i = 0; i < (int)mMediaPanels.size(); ++i )
+ {
+ std::cout << std::setw( 2 ) << std::setfill( '0' );
+ std::cout << i + 1 << "> ";
+ std::cout << "Id: ";
+ std::cout << std::setw( 2 ) << std::setfill( '0' );
+ std::cout << mMediaPanels[ i ]->mId;
+ std::cout << " | ";
+ std::cout << "Media: ";
+ std::cout << std::setw( 3 ) << std::setfill( '0' );
+ std::cout << mMediaPanels[ i ]->mMediaWidth;
+ std::cout << " x ";
+ std::cout << std::setw( 3 ) << std::setfill( '0' );
+ std::cout << mMediaPanels[ i ]->mMediaHeight;
+ std::cout << " | ";
+ std::cout << "Texture: ";
+ std::cout << std::setw( 4 ) << std::setfill( '0' );
+ std::cout << mMediaPanels[ i ]->mTextureWidth;
+ std::cout << " x ";
+ std::cout << std::setw( 4 ) << std::setfill( '0' );
+ std::cout << mMediaPanels[ i ]->mTextureHeight;
+ std::cout << " | ";
+ if ( mMediaPanels[ i ] == mSelectedPanel )
+ std::cout << "(selected)";
+
+ std::cout << std::endl;
+ };
+ std::cout << "========================" << std::endl;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void LLFBConnectTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
+{
+ // Uncomment this to make things much, much quieter.
+// return;
+
+ switch(event)
+ {
+ case MEDIA_EVENT_CONTENT_UPDATED:
+ // too spammy -- don't log these
+// std::cerr << "Media event: MEDIA_EVENT_CONTENT_UPDATED " << std::endl;
+ break;
+
+ case MEDIA_EVENT_TIME_DURATION_UPDATED:
+ // too spammy -- don't log these
+// std::cerr << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << std::endl;
+ break;
+
+ case MEDIA_EVENT_SIZE_CHANGED:
+ std::cerr << "Media event: MEDIA_EVENT_SIZE_CHANGED " << std::endl;
+ break;
+
+ case MEDIA_EVENT_CURSOR_CHANGED:
+ std::cerr << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << std::endl;
+ break;
+
+ case MEDIA_EVENT_NAVIGATE_BEGIN:
+ std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN " << std::endl;
+ break;
+
+ case MEDIA_EVENT_NAVIGATE_COMPLETE:
+ std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << std::endl;
+ break;
+
+ case MEDIA_EVENT_PROGRESS_UPDATED:
+ std::cerr << "Media event: MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << std::endl;
+ break;
+
+ case MEDIA_EVENT_STATUS_TEXT_CHANGED:
+ std::cerr << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << std::endl;
+ break;
+
+ case MEDIA_EVENT_NAME_CHANGED:
+ std::cerr << "Media event: MEDIA_EVENT_NAME_CHANGED, new name is: " << self->getMediaName() << std::endl;
+ glutSetWindowTitle( self->getMediaName().c_str() );
+ break;
+
+ case MEDIA_EVENT_LOCATION_CHANGED:
+ {
+ std::cerr << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << std::endl;
+ mediaPanel* panel = findMediaPanel(self);
+ if(panel != NULL)
+ {
+ panel->mStartUrl = self->getLocation();
+ if(panel == mSelectedPanel)
+ {
+ mUrlEdit->set_text(const_cast<char*>(panel->mStartUrl.c_str()) );
+ }
+ }
+ }
+ break;
+
+ case MEDIA_EVENT_NAVIGATE_ERROR_PAGE:
+ std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_ERROR_PAGE, uri is: " << self->getClickURL() << std::endl;
+ break;
+
+ case MEDIA_EVENT_CLICK_LINK_HREF:
+ {
+ std::cerr << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, uri is " << self->getClickURL() << ", target is " << self->getClickTarget() << std::endl;
+ // retrieve the event parameters
+ std::string url = self->getClickURL();
+ std::string target = self->getClickTarget();
+
+ if(target == "_external")
+ {
+ // this should open in an external browser, but since this is a test app we don't care.
+ }
+ else if(target == "_blank")
+ {
+ // Create a new panel with the specified URL.
+ addMediaPanel(url);
+ }
+ else // other named target
+ {
+ mediaPanel *target_panel = findMediaPanel(target);
+ if(target_panel)
+ {
+ target_panel = replaceMediaPanel(target_panel, url);
+ }
+ else
+ {
+ target_panel = addMediaPanel(url);
+ }
+
+ if(target_panel)
+ {
+ target_panel->mTarget = target;
+ }
+ }
+ }
+ break;
+
+ case MEDIA_EVENT_CLICK_LINK_NOFOLLOW:
+ std::cerr << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << std::endl;
+ break;
+
+ case MEDIA_EVENT_PLUGIN_FAILED:
+ std::cerr << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << std::endl;
+ break;
+
+ case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH:
+ std::cerr << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << std::endl;
+ break;
+
+ case MEDIA_EVENT_CLOSE_REQUEST:
+ std::cerr << "Media event: MEDIA_EVENT_CLOSE_REQUEST" << std::endl;
+ break;
+
+ case MEDIA_EVENT_PICK_FILE_REQUEST:
+ std::cerr << "Media event: MEDIA_EVENT_PICK_FILE_REQUEST" << std::endl;
+ // TODO: display an actual file picker
+ self->sendPickFileResponse("cake");
+ break;
+
+ case MEDIA_EVENT_GEOMETRY_CHANGE:
+ std::cerr << "Media event: MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID()
+ << ", x = " << self->getGeometryX()
+ << ", y = " << self->getGeometryY()
+ << ", width = " << self->getGeometryWidth()
+ << ", height = " << self->getGeometryHeight()
+ << std::endl;
+ break;
+
+ case MEDIA_EVENT_AUTH_REQUEST:
+ {
+ //std::cerr << "Media event: MEDIA_EVENT_AUTH_REQUEST, url " << self->getAuthURL() ", realm " << self->getAuthRealm() << std::endl;
+
+ // TODO: display an auth dialog
+ self->sendAuthResponse(false, "", "");
+ }
+ break;
+
+ case MEDIA_EVENT_LINK_HOVERED:
+ {
+ std::cerr << "Media event: MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << std::endl;
+ }
+ break;
+
+ default:
+ {
+ std::cerr << "Media event: <unknown>, code is: " << int(event) << std::endl;
+ }
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+static void gluiCallbackWrapper( int control_id )
+{
+ if ( gApplication )
+ gApplication->gluiCallback( control_id );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void glutReshape( int width, int height )
+{
+ if ( gApplication )
+ gApplication->reshape( width, height );
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void glutDisplay()
+{
+ if ( gApplication )
+ gApplication->display();
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void glutIdle(int update_ms)
+{
+ GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms);
+
+ if ( gApplication )
+ gApplication->idle();
+
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void glutKeyboard( unsigned char key, int x, int y )
+{
+ if ( gApplication )
+ gApplication->keyboard( key );
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void glutMousePassive( int x, int y )
+{
+ if ( gApplication )
+ gApplication->mousePassive( x, y );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void glutMouseMove( int x , int y )
+{
+ if ( gApplication )
+ gApplication->mouseMove( x, y );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void glutMouseButton( int button, int state, int x, int y )
+{
+ if ( gApplication )
+ gApplication->mouseButton( button, state, x, y );
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+int main( int argc, char* argv[] )
+{
+#if LL_DARWIN
+ // Set the current working directory to <application bundle>/Contents/Resources/
+ CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
+ if(resources_url != NULL)
+ {
+ CFStringRef resources_string = CFURLCopyFileSystemPath(resources_url, kCFURLPOSIXPathStyle);
+ CFRelease(resources_url);
+ if(resources_string != NULL)
+ {
+ char buffer[PATH_MAX] = "";
+ if(CFStringGetCString(resources_string, buffer, sizeof(buffer), kCFStringEncodingUTF8))
+ {
+ chdir(buffer);
+ }
+ CFRelease(resources_string);
+ }
+ }
+#endif
+
+ glutInit( &argc, argv );
+ glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB );
+
+ const int app_window_x = 80;
+ const int app_window_y = 0;
+ const int app_window_width = 960;
+ const int app_window_height = 960;
+
+ glutInitWindowPosition( app_window_x, app_window_y );
+ glutInitWindowSize( app_window_width, app_window_height );
+
+ int app_window_handle = glutCreateWindow( "LLFBConnectTest" );
+
+ glutDisplayFunc( glutDisplay );
+
+ GLUI_Master.set_glutReshapeFunc( glutReshape );
+ GLUI_Master.set_glutKeyboardFunc( glutKeyboard );
+ GLUI_Master.set_glutMouseFunc( glutMouseButton );
+
+ glutPassiveMotionFunc( glutMousePassive );
+ glutMotionFunc( glutMouseMove );
+
+ glutSetWindow( app_window_handle );
+
+ gApplication = new LLFBConnectTest( app_window_handle, app_window_width, app_window_height );
+
+ // update at approximately 60hz
+ int update_ms = 1000 / 60;
+
+ GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms);
+
+ glutMainLoop();
+
+ delete gApplication;
+}
diff --git a/indra/test_apps/llfbconnecttest/llfbconnecttest.h b/indra/test_apps/llfbconnecttest/llfbconnecttest.h
new file mode 100644
index 0000000000..6f442a55b3
--- /dev/null
+++ b/indra/test_apps/llfbconnecttest/llfbconnecttest.h
@@ -0,0 +1,207 @@
+/**
+ * @file LLFBConnectTest.cpp
+ * @brief Facebook Connect Test App
+ *
+ * $LicenseInfo:firstyear=2008&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_FB_CONNECT_H
+#define LL_FB_CONNECT_H
+
+#include <vector>
+#include <string>
+#include "llpluginclassmedia.h"
+#include "llgl.h"
+
+// Forward declarations
+class GLUI_Rotation;
+class GLUI_Translation;
+class GLUI_Listbox;
+class GLUI_EditText;
+class GLUI_StaticText;
+class GLUI;
+class GLUI_Button;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+struct mediaPanel
+{
+ public:
+ mediaPanel();
+ ~mediaPanel();
+ int mId;
+ std::string mStartUrl;
+ std::string mMimeType;
+ std::string mTarget;
+ LLPluginClassMedia *mMediaSource;
+ int mMediaWidth;
+ int mMediaHeight;
+ int mTextureWidth;
+ int mTextureHeight;
+ double mTextureScaleX;
+ double mTextureScaleY;
+ GLuint mMediaTextureHandle;
+ GLuint mPickTextureHandle;
+ unsigned char* mPickTexturePixels;
+ bool mAppTextureCoordsOpenGL;
+ bool mReadyToRender;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+class LLFBConnectTest : public LLPluginClassMediaOwner
+{
+ public:
+ LLFBConnectTest( int app_window, int window_width, int window_height );
+ ~LLFBConnectTest();
+
+ void reshape( int width, int height );
+ void display();
+ void idle();
+ void gluiCallback( int control_id );
+ void keyboard( int key );
+ void mousePassive( int x, int y );
+ void mouseButton( int button, int state, int x, int y );
+ void mouseMove( int x, int y );
+
+ void bindTexture(GLuint texture, GLint row_length = 0, GLint alignment = 1);
+ bool checkGLError(const char *name = "OpenGL");
+ void drawGeometry( int panel, bool selected );
+ void startPanelHighlight( float red, float green, float blue, float line_width );
+ void endPanelHighlight();
+ enum { DrawTypePickTexture, DrawTypeMediaTexture };
+ void draw( int draw_type );
+ void windowPosToTexturePos( int window_x, int window_y, int& media_x, int& media_y, int& id );
+
+ mediaPanel* addMediaPanel( std::string url );
+ void updateMediaPanel( mediaPanel* panel );
+ void remMediaPanel( mediaPanel* panel );
+ mediaPanel* replaceMediaPanel( mediaPanel* panel, std::string url );
+ void getRandomMediaSize( int& width, int& height, std::string mime_type );
+ void navigateToNewURI( std::string uri );
+ void initUrlHistory( std::string uri );
+ void selectPanelById( int id );
+ void selectPanel( mediaPanel* panel );
+ mediaPanel* findMediaPanel( LLPluginClassMedia* panel );
+ mediaPanel* findMediaPanel( const std::string &target_name );
+ void makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels );
+ void makeChrome();
+ void resetView();
+
+ void dumpPanelInfo();
+ void updateStatusBar();
+
+ GLfloat distanceToCamera( GLfloat point_x, GLfloat point_y, GLfloat point_z );
+
+
+ // Inherited from LLPluginClassMediaOwner
+ /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent);
+
+ private:
+ const int mVersionMajor;
+ const int mVersionMinor;
+ const int mVersionPatch;
+ const int mMaxPanels;
+ int mAppWindow;
+ int mWindowWidth;
+ int mWindowHeight;
+ int mCurMouseX;
+ int mCurMouseY;
+ unsigned char mPixelReadColor[ 3 ];
+ bool mFuzzyMedia;
+ const std::string mHomeWebUrl;
+
+ std::vector< mediaPanel* > mMediaPanels;
+ mediaPanel* mSelectedPanel;
+ std::string mimeTypeFromUrl( std::string& url );
+ std::string pluginNameFromMimeType( std::string& mime_type );
+
+ GLUI_Rotation* mViewRotationCtrl;
+ GLUI_Translation* mViewScaleCtrl;
+ GLUI_Translation* mViewTranslationCtrl;
+ float mViewportAspect;
+ float mViewPos[ 3 ];
+ float mViewRotation[ 16 ];
+
+ float mDistanceCameraToSelectedGeometry;
+
+ int mIdControlAddPanel;
+ int mIdControlRemPanel;
+
+ std::vector< std::pair< std::string, std::string > > mBookmarks;
+ GLUI_Listbox* mBookmarkList;
+ int mIdBookmarks;
+ int mIdUrlEdit;
+ GLUI_EditText* mUrlEdit;
+ //int mIdUrlInitHistoryEdit;
+ //GLUI_EditText* mUrlInitHistoryEdit;
+ int mSelBookmark;
+ //int mIdRandomPanelCount;
+ //int mRandomPanelCount;
+ //int mIdRandomBookmarks;
+ //int mRandomBookmarks;
+ //int mIdDisableTimeout;
+ //int mDisableTimeout;
+ //int mIdUsePluginReadThread;
+ //int mUsePluginReadThread;
+ //int mIdLargePanelSpacing;
+ //int mLargePanelSpacing;
+ //int mIdControlCrashPlugin;
+ //int mIdControlHangPlugin;
+ int mIdControlExitApp;
+
+ //GLUI* mGluiMediaTimeControlWindow;
+ //int mIdMediaTimeControlPlay;
+ //int mIdMediaTimeControlLoop;
+ //int mIdMediaTimeControlPause;
+ //int mIdMediaTimeControlStop;
+ //int mIdMediaTimeControlSeek;
+ //int mIdMediaTimeControlVolume;
+ //int mMediaTimeControlVolume;
+ //int mIdMediaTimeControlSeekSeconds;
+ //int mMediaTimeControlSeekSeconds;
+ //int mIdMediaTimeControlRewind;
+ //int mIdMediaTimeControlFastForward;
+
+ GLUI* mGluiMediaBrowserControlWindow;
+ int mIdMediaBrowserControlBack;
+ GLUI_Button* mMediaBrowserControlBackButton;
+ int mIdMediaBrowserControlStop;
+ int mIdMediaBrowserControlForward;
+ GLUI_Button* mMediaBrowserControlForwardButton;
+ bool mGluiMediaTimeControlWindowFlag;
+ bool mGluiMediaBrowserControlWindowFlag;
+ bool mMediaBrowserControlBackButtonFlag;
+ bool mMediaBrowserControlForwardButtonFlag;
+ int mIdMediaBrowserControlHome;
+ int mIdMediaBrowserControlReload;
+ int mIdMediaBrowserControlClearCache;
+ int mIdMediaBrowserControlClearCookies;
+ int mIdMediaBrowserControlEnableCookies;
+ int mMediaBrowserControlEnableCookies;
+
+ GLUI* mBottomGLUIWindow;
+ GLUI_StaticText* mStatusText;
+};
+
+#endif // LL_FB_CONNECT_H
+
diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp
index d6e06e5316..dc8ff2f644 100755
--- a/indra/viewer_components/updater/llupdatechecker.cpp
+++ b/indra/viewer_components/updater/llupdatechecker.cpp
@@ -79,7 +79,6 @@ void LLUpdateChecker::checkVersion(std::string const & urlBase,
//-----------------------------------------------------------------------------
-const char * LLUpdateChecker::Implementation::sLegacyProtocolVersion = "v1.0";
const char * LLUpdateChecker::Implementation::sProtocolVersion = "v1.1";
@@ -150,40 +149,11 @@ void LLUpdateChecker::Implementation::completed(U32 status,
server_error += content["error_text"].asString();
}
- if (status == 404)
- {
- if (mProtocol == sProtocolVersion)
- {
- mProtocol = sLegacyProtocolVersion;
- std::string retryUrl = buildUrl(mUrlBase, mChannel, mVersion, mPlatform, mPlatformVersion, mUniqueId, mWillingToTest);
-
- LL_WARNS("UpdaterService")
- << "update response using " << sProtocolVersion
- << " was HTTP 404 (" << server_error
- << "); retry with legacy protocol " << mProtocol
- << "\n at " << retryUrl
- << LL_ENDL;
-
- mHttpClient.get(retryUrl, this);
- }
- else
- {
- LL_WARNS("UpdaterService")
- << "update response using " << sLegacyProtocolVersion
- << " was 404 (" << server_error
- << "); request failed"
- << LL_ENDL;
- mClient.error(reason);
- }
- }
- else
- {
- LL_WARNS("UpdaterService") << "response error " << status
- << " " << reason
- << " (" << server_error << ")"
- << LL_ENDL;
- mClient.error(reason);
- }
+ LL_WARNS("UpdaterService") << "response error " << status
+ << " " << reason
+ << " (" << server_error << ")"
+ << LL_ENDL;
+ mClient.error(reason);
}
else
{
@@ -213,11 +183,8 @@ std::string LLUpdateChecker::Implementation::buildUrl(std::string const & urlBas
path.append(channel);
path.append(version);
path.append(platform);
- if (mProtocol != sLegacyProtocolVersion)
- {
- path.append(platform_version);
- path.append(willing_to_test ? "testok" : "testno");
- path.append((char*)uniqueid);
- }
+ path.append(platform_version);
+ path.append(willing_to_test ? "testok" : "testno");
+ path.append((char*)uniqueid);
return LLURI::buildHTTP(urlBase, path).asString();
}