summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCho <cho@lindenlab.com>2014-06-26 19:31:55 +0100
committerCho <cho@lindenlab.com>2014-06-26 19:31:55 +0100
commit8ab5a25b12cb059c7408721ff5cf9556b47fb94a (patch)
treee6a7a556e9cd88856c7fc44fd0dd8db9f4c8941b
parent9b6598388a5fa085d89b77b57f0b361d53a680cb (diff)
parentc7057ef56efc1cca5683ca22e2ad410198d131e8 (diff)
merge
-rwxr-xr-x.hgtags1
-rwxr-xr-xindra/integration_tests/llimage_libtest/CMakeLists.txt3
-rw-r--r--indra/integration_tests/llimage_libtest/filters/1970colorize.xml41
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/autocontrast.xml11
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/badtrip.xml36
-rw-r--r--indra/integration_tests/llimage_libtest/filters/blowhighlights.xml25
-rw-r--r--indra/integration_tests/llimage_libtest/filters/blur.xml7
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/brighten.xml11
-rw-r--r--indra/integration_tests/llimage_libtest/filters/colorize.xml24
-rw-r--r--indra/integration_tests/llimage_libtest/filters/colortransform.xml16
-rw-r--r--indra/integration_tests/llimage_libtest/filters/contrast.xml11
-rw-r--r--indra/integration_tests/llimage_libtest/filters/convolve.xml18
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/darken.xml11
-rw-r--r--indra/integration_tests/llimage_libtest/filters/dodgeandburn.xml47
-rw-r--r--indra/integration_tests/llimage_libtest/filters/edges.xml24
-rw-r--r--indra/integration_tests/llimage_libtest/filters/focus.xml39
-rw-r--r--indra/integration_tests/llimage_libtest/filters/gamma.xml11
-rw-r--r--indra/integration_tests/llimage_libtest/filters/grayscale.xml14
-rw-r--r--indra/integration_tests/llimage_libtest/filters/heatwave.xml38
-rw-r--r--indra/integration_tests/llimage_libtest/filters/horizontalscreen.xml20
-rw-r--r--indra/integration_tests/llimage_libtest/filters/julesverne.xml20
-rw-r--r--indra/integration_tests/llimage_libtest/filters/lensflare.xml131
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/lightleak.xml78
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/linearize.xml11
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/miniature.xml118
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/newsscreen.xml20
-rw-r--r--indra/integration_tests/llimage_libtest/filters/overcast.xml24
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/pixelate.xml35
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/posterize.xml11
-rw-r--r--indra/integration_tests/llimage_libtest/filters/rotatecolors180.xml8
-rw-r--r--indra/integration_tests/llimage_libtest/filters/saturate.xml8
-rw-r--r--indra/integration_tests/llimage_libtest/filters/sepia.xml14
-rw-r--r--indra/integration_tests/llimage_libtest/filters/sharpen.xml7
-rw-r--r--indra/integration_tests/llimage_libtest/filters/slantedscreen.xml20
-rw-r--r--indra/integration_tests/llimage_libtest/filters/spotlight.xml45
-rw-r--r--indra/integration_tests/llimage_libtest/filters/stencilgradient.xml24
-rw-r--r--indra/integration_tests/llimage_libtest/filters/stencilscanlines.xml22
-rw-r--r--indra/integration_tests/llimage_libtest/filters/stenciluniform.xml20
-rw-r--r--indra/integration_tests/llimage_libtest/filters/stencilvignette.xml24
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/thematrix.xml42
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/toycamera.xml46
-rw-r--r--indra/integration_tests/llimage_libtest/filters/verticalscreen.xml20
-rwxr-xr-xindra/integration_tests/llimage_libtest/filters/video.xml44
-rwxr-xr-xindra/integration_tests/llimage_libtest/llimage_libtest.cpp39
-rwxr-xr-xindra/llimage/CMakeLists.txt2
-rwxr-xr-xindra/llimage/llimage.cpp80
-rwxr-xr-xindra/llimage/llimage.h3
-rwxr-xr-xindra/llimage/llimagefilter.cpp939
-rwxr-xr-xindra/llimage/llimagefilter.h137
-rwxr-xr-xindra/newview/CMakeLists.txt16
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rwxr-xr-xindra/newview/app_settings/commands.xml32
-rwxr-xr-xindra/newview/app_settings/filters/Autocontrast.xml11
-rw-r--r--indra/newview/app_settings/filters/BlackAndWhite.xml21
-rw-r--r--indra/newview/app_settings/filters/Colors1970.xml47
-rw-r--r--indra/newview/app_settings/filters/Intense.xml8
-rw-r--r--indra/newview/app_settings/filters/LensFlare.xml131
-rwxr-xr-xindra/newview/app_settings/filters/Miniature.xml118
-rwxr-xr-xindra/newview/app_settings/filters/Newspaper.xml20
-rw-r--r--indra/newview/app_settings/filters/Sepia.xml32
-rw-r--r--indra/newview/app_settings/filters/Spotlight.xml47
-rwxr-xr-xindra/newview/app_settings/filters/Toycamera.xml46
-rwxr-xr-xindra/newview/app_settings/filters/Video.xml44
-rwxr-xr-xindra/newview/app_settings/settings.xml13
-rwxr-xr-xindra/newview/app_settings/toolbars.xml2
-rwxr-xr-xindra/newview/llfacebookconnect.cpp96
-rw-r--r--indra/newview/llfacebookconnect.h2
-rwxr-xr-xindra/newview/llfilepicker.cpp9
-rwxr-xr-xindra/newview/llfilepicker.h3
-rw-r--r--indra/newview/llflickrconnect.cpp509
-rw-r--r--indra/newview/llflickrconnect.h98
-rw-r--r--indra/newview/llfloaterbigpreview.cpp110
-rw-r--r--indra/newview/llfloaterbigpreview.h54
-rw-r--r--indra/newview/llfloaterfacebook.cpp (renamed from indra/newview/llfloatersocial.cpp)580
-rw-r--r--indra/newview/llfloaterfacebook.h (renamed from indra/newview/llfloatersocial.h)80
-rw-r--r--indra/newview/llfloaterflickr.cpp798
-rw-r--r--indra/newview/llfloaterflickr.h135
-rwxr-xr-xindra/newview/llfloatersnapshot.cpp129
-rwxr-xr-xindra/newview/llfloatersnapshot.h2
-rw-r--r--indra/newview/llfloatertwitter.cpp827
-rw-r--r--indra/newview/llfloatertwitter.h139
-rwxr-xr-xindra/newview/llfloateruipreview.cpp7
-rwxr-xr-xindra/newview/llfloaterwebcontent.cpp34
-rwxr-xr-xindra/newview/llfloaterwebcontent.h3
-rw-r--r--indra/newview/llimagefiltersmanager.cpp115
-rw-r--r--indra/newview/llimagefiltersmanager.h55
-rwxr-xr-xindra/newview/llmediactrl.cpp4
-rwxr-xr-xindra/newview/llmediactrl.h2
-rwxr-xr-xindra/newview/llpanelpeople.cpp6
-rwxr-xr-xindra/newview/llpanelpeople.h2
-rwxr-xr-xindra/newview/llpanelsnapshotoptions.cpp57
-rw-r--r--indra/newview/llsnapshotlivepreview.cpp678
-rw-r--r--indra/newview/llsnapshotlivepreview.h40
-rwxr-xr-xindra/newview/lltexturefetch.cpp3
-rw-r--r--indra/newview/lltwitterconnect.cpp503
-rw-r--r--indra/newview/lltwitterconnect.h99
-rwxr-xr-xindra/newview/llviewerfloaterreg.cpp16
-rwxr-xr-xindra/newview/llviewermedia.cpp29
-rwxr-xr-xindra/newview/llviewermedia.h5
-rwxr-xr-xindra/newview/llviewermenufile.cpp3
-rwxr-xr-xindra/newview/llviewerregion.cpp3
-rwxr-xr-xindra/newview/skins/default/textures/textures.xml4
-rw-r--r--indra/newview/skins/default/textures/toolbar_icons/flickr.pngbin0 -> 15530 bytes
-rw-r--r--indra/newview/skins/default/textures/toolbar_icons/twitter.pngbin0 -> 16051 bytes
-rw-r--r--indra/newview/skins/default/xui/en/floater_big_preview.xml25
-rw-r--r--indra/newview/skins/default/xui/en/floater_facebook.xml111
-rw-r--r--indra/newview/skins/default/xui/en/floater_flickr.xml (renamed from indra/newview/skins/default/xui/en/floater_social.xml)39
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_snapshot.xml28
-rw-r--r--indra/newview/skins/default/xui/en/floater_twitter.xml90
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_viewer.xml30
-rwxr-xr-xindra/newview/skins/default/xui/en/notifications.xml16
-rw-r--r--indra/newview/skins/default/xui/en/panel_facebook_account.xml (renamed from indra/newview/skins/default/xui/en/panel_social_account.xml)6
-rw-r--r--indra/newview/skins/default/xui/en/panel_facebook_friends.xml72
-rw-r--r--indra/newview/skins/default/xui/en/panel_facebook_photo.xml (renamed from indra/newview/skins/default/xui/en/panel_social_photo.xml)60
-rw-r--r--indra/newview/skins/default/xui/en/panel_facebook_place.xml (renamed from indra/newview/skins/default/xui/en/panel_social_place.xml)9
-rw-r--r--indra/newview/skins/default/xui/en/panel_facebook_status.xml (renamed from indra/newview/skins/default/xui/en/panel_social_status.xml)9
-rw-r--r--indra/newview/skins/default/xui/en/panel_flickr_account.xml75
-rw-r--r--indra/newview/skins/default/xui/en/panel_flickr_photo.xml245
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_snapshot_options.xml36
-rw-r--r--indra/newview/skins/default/xui/en/panel_twitter_account.xml75
-rw-r--r--indra/newview/skins/default/xui/en/panel_twitter_photo.xml193
-rwxr-xr-xindra/newview/skins/default/xui/en/strings.xml55
-rwxr-xr-xindra/newview/viewer_manifest.py3
123 files changed, 8719 insertions, 687 deletions
diff --git a/.hgtags b/.hgtags
index b972d376f7..02e19f6f66 100755
--- a/.hgtags
+++ b/.hgtags
@@ -482,3 +482,4 @@ d029faf69f20a23007f32420a1ac6a3b89a6d441 3.7.6-release
83959480cb986522d07b151a0c778ab7f920d41b 3.7.7-release
bba9b3722eea08949e4ff69591f736bf0f808434 3.7.8-release
a9f2d0cb11f73b06858e6083bb50083becc3f9cd 3.7.9-release
+91dae9494b4d147541c7a01902334ba19a7ec05e 3.7.10-release
diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt
index 36a7d38bb7..8a83ac498f 100755
--- a/indra/integration_tests/llimage_libtest/CMakeLists.txt
+++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt
@@ -7,6 +7,7 @@ project (llimage_libtest)
include(00-Common)
include(LLCommon)
include(LLImage)
+include(LLMath)
include(LLImageJ2COJ)
include(LLKDU)
include(LLVFS)
@@ -15,6 +16,7 @@ include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
)
include_directories(SYSTEM
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
@@ -64,6 +66,7 @@ endif (DARWIN)
target_link_libraries(llimage_libtest
${LLCOMMON_LIBRARIES}
${LLVFS_LIBRARIES}
+ ${LLMATH_LIBRARIES}
${LLIMAGE_LIBRARIES}
${LLKDU_LIBRARIES}
${KDU_LIBRARY}
diff --git a/indra/integration_tests/llimage_libtest/filters/1970colorize.xml b/indra/integration_tests/llimage_libtest/filters/1970colorize.xml
new file mode 100644
index 0000000000..0dab2489a0
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/1970colorize.xml
@@ -0,0 +1,41 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>0.8</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.5</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>blend</string>
+ <real>10.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.1</real>
+ <real>0.1</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/autocontrast.xml b/indra/integration_tests/llimage_libtest/filters/autocontrast.xml
new file mode 100755
index 0000000000..ec3d7561bd
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/autocontrast.xml
@@ -0,0 +1,11 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.01</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/badtrip.xml b/indra/integration_tests/llimage_libtest/filters/badtrip.xml
new file mode 100755
index 0000000000..14ee0baff3
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/badtrip.xml
@@ -0,0 +1,36 @@
+<llsd>
+ <array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>posterize</string>
+ <real>10.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>gradient</string>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.15</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/blowhighlights.xml b/indra/integration_tests/llimage_libtest/filters/blowhighlights.xml
new file mode 100644
index 0000000000..2474a1b953
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/blowhighlights.xml
@@ -0,0 +1,25 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>uniform</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>gamma</string>
+ <real>0.25</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/blur.xml b/indra/integration_tests/llimage_libtest/filters/blur.xml
new file mode 100644
index 0000000000..addd056855
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/blur.xml
@@ -0,0 +1,7 @@
+<llsd>
+ <array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/brighten.xml b/indra/integration_tests/llimage_libtest/filters/brighten.xml
new file mode 100755
index 0000000000..9b4232229f
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/brighten.xml
@@ -0,0 +1,11 @@
+<llsd>
+ <array>
+ <array>
+ <string>brighten</string>
+ <real>0.5</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/colorize.xml b/indra/integration_tests/llimage_libtest/filters/colorize.xml
new file mode 100644
index 0000000000..72e58b0ffe
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/colorize.xml
@@ -0,0 +1,24 @@
+<llsd>
+ <array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>10.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.5</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/colortransform.xml b/indra/integration_tests/llimage_libtest/filters/colortransform.xml
new file mode 100644
index 0000000000..de4bebcce2
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/colortransform.xml
@@ -0,0 +1,16 @@
+<llsd>
+ <array>
+ <array>
+ <string>colortransform</string>
+ <real>0.2125</real>
+ <real>0.7154</real>
+ <real>0.0721</real>
+ <real>0.2125</real>
+ <real>0.7154</real>
+ <real>0.0721</real>
+ <real>0.2125</real>
+ <real>0.7154</real>
+ <real>0.0721</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/contrast.xml b/indra/integration_tests/llimage_libtest/filters/contrast.xml
new file mode 100644
index 0000000000..00746b8a9e
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/contrast.xml
@@ -0,0 +1,11 @@
+<llsd>
+ <array>
+ <array>
+ <string>contrast</string>
+ <real>1.5</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/convolve.xml b/indra/integration_tests/llimage_libtest/filters/convolve.xml
new file mode 100644
index 0000000000..6e65b5f88a
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/convolve.xml
@@ -0,0 +1,18 @@
+<llsd>
+ <array>
+ <array>
+ <string>convolve</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>4.0</real>
+ <real>1.0</real>
+ <real>4.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>4.0</real>
+ <real>1.0</real>
+ <real>4.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/darken.xml b/indra/integration_tests/llimage_libtest/filters/darken.xml
new file mode 100755
index 0000000000..5cec3589b6
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/darken.xml
@@ -0,0 +1,11 @@
+<llsd>
+ <array>
+ <array>
+ <string>darken</string>
+ <real>0.5</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/dodgeandburn.xml b/indra/integration_tests/llimage_libtest/filters/dodgeandburn.xml
new file mode 100644
index 0000000000..0e2e0ad68c
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/dodgeandburn.xml
@@ -0,0 +1,47 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.4</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>-0.8</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/edges.xml b/indra/integration_tests/llimage_libtest/filters/edges.xml
new file mode 100644
index 0000000000..a66b81d01e
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/edges.xml
@@ -0,0 +1,24 @@
+<llsd>
+ <array>
+ <array>
+ <string>gradient</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>2.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/focus.xml b/indra/integration_tests/llimage_libtest/filters/focus.xml
new file mode 100644
index 0000000000..d8525fea62
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/focus.xml
@@ -0,0 +1,39 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>0.4</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>sharpen</string>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/gamma.xml b/indra/integration_tests/llimage_libtest/filters/gamma.xml
new file mode 100644
index 0000000000..19af09b046
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/gamma.xml
@@ -0,0 +1,11 @@
+<llsd>
+ <array>
+ <array>
+ <string>gamma</string>
+ <real>1.7</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/grayscale.xml b/indra/integration_tests/llimage_libtest/filters/grayscale.xml
new file mode 100644
index 0000000000..984312c4fd
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/grayscale.xml
@@ -0,0 +1,14 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/heatwave.xml b/indra/integration_tests/llimage_libtest/filters/heatwave.xml
new file mode 100644
index 0000000000..a99f41c833
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/heatwave.xml
@@ -0,0 +1,38 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>0.8</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>fade</string>
+ <real>0.5</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>4.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.4</real>
+ <real>0.0</real>
+ <real>0.2</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/horizontalscreen.xml b/indra/integration_tests/llimage_libtest/filters/horizontalscreen.xml
new file mode 100644
index 0000000000..21cab70e54
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/horizontalscreen.xml
@@ -0,0 +1,20 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>line</string>
+ <real>0.015</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/julesverne.xml b/indra/integration_tests/llimage_libtest/filters/julesverne.xml
new file mode 100644
index 0000000000..981e221da9
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/julesverne.xml
@@ -0,0 +1,20 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>line</string>
+ <real>0.02</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/lensflare.xml b/indra/integration_tests/llimage_libtest/filters/lensflare.xml
new file mode 100644
index 0000000000..0b5af9c82b
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/lensflare.xml
@@ -0,0 +1,131 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.01</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>gradient</string>
+ <string>add</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>-1.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.1</real>
+ <real>0.1</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.5</real>
+ <real>5.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.6</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>5.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.6</real>
+ <real>0.6</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.5</real>
+ <real>-0.5</real>
+ <real>0.10</real>
+ <real>20.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.7</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.6</real>
+ <real>-0.6</real>
+ <real>0.05</real>
+ <real>20.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.7</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.4</real>
+ <real>-0.4</real>
+ <real>0.025</real>
+ <real>20.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.7</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/lightleak.xml b/indra/integration_tests/llimage_libtest/filters/lightleak.xml
new file mode 100755
index 0000000000..6fe496506e
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/lightleak.xml
@@ -0,0 +1,78 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.01</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>brighten</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>gradient</string>
+ <string>add</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>-1.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.1</real>
+ <real>0.1</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.5</real>
+ <real>5.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.8</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>5.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.8</real>
+ <real>0.8</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/linearize.xml b/indra/integration_tests/llimage_libtest/filters/linearize.xml
new file mode 100755
index 0000000000..23d0290e07
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/linearize.xml
@@ -0,0 +1,11 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/miniature.xml b/indra/integration_tests/llimage_libtest/filters/miniature.xml
new file mode 100755
index 0000000000..9aa8a87c6f
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/miniature.xml
@@ -0,0 +1,118 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.02</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.02</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>saturate</string>
+ <real>1.2</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>0.25</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.25</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>sharpen</string>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>gradient</string>
+ <string>blend</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>-1.0</real>
+ <real>0.0</real>
+ <real>-0.25</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>gradient</string>
+ <string>blend</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.25</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd> \ No newline at end of file
diff --git a/indra/integration_tests/llimage_libtest/filters/newsscreen.xml b/indra/integration_tests/llimage_libtest/filters/newsscreen.xml
new file mode 100755
index 0000000000..50ed27c6db
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/newsscreen.xml
@@ -0,0 +1,20 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>2Dsine</string>
+ <real>0.015</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/overcast.xml b/indra/integration_tests/llimage_libtest/filters/overcast.xml
new file mode 100644
index 0000000000..dce5ab3e9e
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/overcast.xml
@@ -0,0 +1,24 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.3</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>saturate</string>
+ <real>0.35</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/pixelate.xml b/indra/integration_tests/llimage_libtest/filters/pixelate.xml
new file mode 100755
index 0000000000..f643419aa0
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/pixelate.xml
@@ -0,0 +1,35 @@
+<llsd>
+ <array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>darken</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>0.9</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>linearize</string>
+ <real>0.01</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>posterize</string>
+ <real>4.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd> \ No newline at end of file
diff --git a/indra/integration_tests/llimage_libtest/filters/posterize.xml b/indra/integration_tests/llimage_libtest/filters/posterize.xml
new file mode 100755
index 0000000000..4d03df3c66
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/posterize.xml
@@ -0,0 +1,11 @@
+<llsd>
+ <array>
+ <array>
+ <string>posterize</string>
+ <real>10.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/rotatecolors180.xml b/indra/integration_tests/llimage_libtest/filters/rotatecolors180.xml
new file mode 100644
index 0000000000..e25029720f
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/rotatecolors180.xml
@@ -0,0 +1,8 @@
+<llsd>
+ <array>
+ <array>
+ <string>rotate</string>
+ <real>180.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/saturate.xml b/indra/integration_tests/llimage_libtest/filters/saturate.xml
new file mode 100644
index 0000000000..b77f07a037
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/saturate.xml
@@ -0,0 +1,8 @@
+<llsd>
+ <array>
+ <array>
+ <string>saturate</string>
+ <real>3.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/sepia.xml b/indra/integration_tests/llimage_libtest/filters/sepia.xml
new file mode 100644
index 0000000000..0304ead015
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/sepia.xml
@@ -0,0 +1,14 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>sepia</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/sharpen.xml b/indra/integration_tests/llimage_libtest/filters/sharpen.xml
new file mode 100644
index 0000000000..6d3f9ae1a2
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/sharpen.xml
@@ -0,0 +1,7 @@
+<llsd>
+ <array>
+ <array>
+ <string>sharpen</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/slantedscreen.xml b/indra/integration_tests/llimage_libtest/filters/slantedscreen.xml
new file mode 100644
index 0000000000..6cd1a96185
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/slantedscreen.xml
@@ -0,0 +1,20 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>line</string>
+ <real>0.015</real>
+ <real>45.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/spotlight.xml b/indra/integration_tests/llimage_libtest/filters/spotlight.xml
new file mode 100644
index 0000000000..203130bdee
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/spotlight.xml
@@ -0,0 +1,45 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>0.8</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>saturate</string>
+ <real>1.5</real>
+ </array>
+ <array>
+ <string>fade</string>
+ <real>1.0</real>
+ <real>0.25</real>
+ </array>
+ <array>
+ <string>saturate</string>
+ <real>0.8</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>brighten</string>
+ <real>30</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/stencilgradient.xml b/indra/integration_tests/llimage_libtest/filters/stencilgradient.xml
new file mode 100644
index 0000000000..d22809a9bf
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/stencilgradient.xml
@@ -0,0 +1,24 @@
+<llsd>
+ <array>
+ <array>
+ <string>stencil</string>
+ <string>gradient</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>-1.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/stencilscanlines.xml b/indra/integration_tests/llimage_libtest/filters/stencilscanlines.xml
new file mode 100644
index 0000000000..3ce428503d
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/stencilscanlines.xml
@@ -0,0 +1,22 @@
+<llsd>
+ <array>
+ <array>
+ <string>stencil</string>
+ <string>scanlines</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.1</real>
+ <real>45.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/stenciluniform.xml b/indra/integration_tests/llimage_libtest/filters/stenciluniform.xml
new file mode 100644
index 0000000000..7d72f0ed93
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/stenciluniform.xml
@@ -0,0 +1,20 @@
+<llsd>
+ <array>
+ <array>
+ <string>stencil</string>
+ <string>uniform</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/stencilvignette.xml b/indra/integration_tests/llimage_libtest/filters/stencilvignette.xml
new file mode 100644
index 0000000000..d30637fef5
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/stencilvignette.xml
@@ -0,0 +1,24 @@
+<llsd>
+ <array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>10.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/thematrix.xml b/indra/integration_tests/llimage_libtest/filters/thematrix.xml
new file mode 100755
index 0000000000..af9a5eced8
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/thematrix.xml
@@ -0,0 +1,42 @@
+<llsd>
+ <array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>posterize</string>
+ <real>50.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>gradient</string>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>line</string>
+ <real>0.025</real>
+ <real>90.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.1</real>
+ <real>0.2</real>
+ <real>0.2</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd> \ No newline at end of file
diff --git a/indra/integration_tests/llimage_libtest/filters/toycamera.xml b/indra/integration_tests/llimage_libtest/filters/toycamera.xml
new file mode 100755
index 0000000000..4e76f6b2fb
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/toycamera.xml
@@ -0,0 +1,46 @@
+<llsd>
+ <array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>fade</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.2</real>
+ <real>3.0</real>
+ </array>
+ <array>
+ <string>linearize</string>
+ <real>0.05</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd> \ No newline at end of file
diff --git a/indra/integration_tests/llimage_libtest/filters/verticalscreen.xml b/indra/integration_tests/llimage_libtest/filters/verticalscreen.xml
new file mode 100644
index 0000000000..0768d1d7e1
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/verticalscreen.xml
@@ -0,0 +1,20 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>line</string>
+ <real>0.015</real>
+ <real>90.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/filters/video.xml b/indra/integration_tests/llimage_libtest/filters/video.xml
new file mode 100755
index 0000000000..fe17f3950a
--- /dev/null
+++ b/indra/integration_tests/llimage_libtest/filters/video.xml
@@ -0,0 +1,44 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>darken</string>
+ <real>0.15</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>uniform</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>line</string>
+ <real>0.02</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>gamma</string>
+ <real>0.25</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
index 034c816742..3d27b4a5b5 100755
--- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
+++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
@@ -32,6 +32,7 @@
// Linden library includes
#include "llimage.h"
+#include "llimagefilter.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagebmp.h"
@@ -39,6 +40,8 @@
#include "llimagej2c.h"
#include "lldir.h"
#include "lldiriterator.h"
+#include "v4coloru.h"
+#include "llsdserialize.h"
// system libraries
#include <iostream>
@@ -83,6 +86,8 @@ static const char USAGE[] = "\n"
" -rev, --reversible\n"
" Set the compression to be lossless (reversible in j2c parlance).\n"
" Only valid for output j2c images.\n"
+" -f, --filter <file>\n"
+" Apply the filter <file> to the input images.\n"
" -log, --logmetrics <metric>\n"
" Log performance data for <metric>. Results in <metric>.slp\n"
" Note: so far, only ImageCompressionTester has been tested.\n"
@@ -99,7 +104,7 @@ static bool sAllDone = false;
// Create an empty formatted image instance of the correct type from the filename
LLPointer<LLImageFormatted> create_image(const std::string &filename)
{
- std::string exten = gDirUtilp->getExtension(filename);
+ std::string exten = gDirUtilp->getExtension(filename);
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromExtension(exten);
return image;
}
@@ -350,6 +355,7 @@ int main(int argc, char** argv)
int blocks_size = -1;
int levels = 0;
bool reversible = false;
+ std::string filter_name = "";
// Init whatever is necessary
ll_init_apr();
@@ -523,7 +529,26 @@ int main(int argc, char** argv)
break;
}
}
- else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a"))
+ else if (!strcmp(argv[arg], "--filter") || !strcmp(argv[arg], "-f"))
+ {
+ // '--filter' needs to be specified with a named filter argument
+ if ((arg + 1) < argc)
+ {
+ filter_name = argv[arg+1];
+ }
+ if (((arg + 1) >= argc) || (filter_name[0] == '-'))
+ {
+ // We don't have an argument left in the arg list or the next argument is another option
+ std::cout << "No --filter argument given, no filter will be applied" << std::endl;
+ }
+ else
+ {
+ arg += 1; // Skip that arg now we know it's a valid test name
+ if ((arg + 1) == argc) // Break out of the loop if we reach the end of the arg list
+ break;
+ }
+ }
+ else if (!strcmp(argv[arg], "--analyzeperformance") || !strcmp(argv[arg], "-a"))
{
analyze_performance = true;
}
@@ -553,7 +578,10 @@ int main(int argc, char** argv)
fast_timer_log_thread = new LogThread(LLFastTimer::sLogName);
fast_timer_log_thread->start();
}
-
+
+ // Load the filter once and for all
+ LLImageFilter filter(filter_name);
+
// Perform action on each input file
std::list<std::string>::iterator in_file = input_filenames.begin();
std::list<std::string>::iterator out_file = output_filenames.begin();
@@ -568,7 +596,10 @@ int main(int argc, char** argv)
std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
continue;
}
-
+
+ // Apply the filter
+ filter.executeFilter(raw_image);
+
// Save file
if (out_file != out_end)
{
diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt
index e837b0cac2..293ada7548 100755
--- a/indra/llimage/CMakeLists.txt
+++ b/indra/llimage/CMakeLists.txt
@@ -27,6 +27,7 @@ set(llimage_SOURCE_FILES
llimage.cpp
llimagedimensionsinfo.cpp
llimagedxt.cpp
+ llimagefilter.cpp
llimagej2c.cpp
llimagejpeg.cpp
llimagepng.cpp
@@ -42,6 +43,7 @@ set(llimage_HEADER_FILES
llimagebmp.h
llimagedimensionsinfo.h
llimagedxt.h
+ llimagefilter.h
llimagej2c.h
llimagejpeg.h
llimagepng.h
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 1ca1bf55a6..d336eeaabc 100755
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -452,18 +452,8 @@ void LLImageRaw::verticalFlip()
void LLImageRaw::expandToPowerOfTwo(S32 max_dim, BOOL scale_image)
{
// Find new sizes
- S32 new_width = MIN_IMAGE_SIZE;
- S32 new_height = MIN_IMAGE_SIZE;
-
- while( (new_width < getWidth()) && (new_width < max_dim) )
- {
- new_width <<= 1;
- }
-
- while( (new_height < getHeight()) && (new_height < max_dim) )
- {
- new_height <<= 1;
- }
+ S32 new_width = expandDimToPowerOfTwo(getWidth(), max_dim);
+ S32 new_height = expandDimToPowerOfTwo(getHeight(), max_dim);
scale( new_width, new_height, scale_image );
}
@@ -471,55 +461,61 @@ void LLImageRaw::expandToPowerOfTwo(S32 max_dim, BOOL scale_image)
void LLImageRaw::contractToPowerOfTwo(S32 max_dim, BOOL scale_image)
{
// Find new sizes
- S32 new_width = max_dim;
- S32 new_height = max_dim;
-
- while( (new_width > getWidth()) && (new_width > MIN_IMAGE_SIZE) )
- {
- new_width >>= 1;
- }
-
- while( (new_height > getHeight()) && (new_height > MIN_IMAGE_SIZE) )
- {
- new_height >>= 1;
- }
+ S32 new_width = contractDimToPowerOfTwo(getWidth(), MIN_IMAGE_SIZE);
+ S32 new_height = contractDimToPowerOfTwo(getHeight(), MIN_IMAGE_SIZE);
scale( new_width, new_height, scale_image );
}
-void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
+// static
+S32 LLImageRaw::biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim)
{
// Strong bias towards rounding down (to save bandwidth)
// No bias would mean THRESHOLD == 1.5f;
- const F32 THRESHOLD = 1.75f;
-
+ const F32 THRESHOLD = 1.75f;
+
// Find new sizes
- S32 larger_w = max_dim; // 2^n >= mWidth
- S32 smaller_w = max_dim; // 2^(n-1) <= mWidth
- while( (smaller_w > getWidth()) && (smaller_w > MIN_IMAGE_SIZE) )
+ S32 larger_dim = max_dim; // 2^n >= curr_dim
+ S32 smaller_dim = max_dim; // 2^(n-1) <= curr_dim
+ while( (smaller_dim > curr_dim) && (smaller_dim > MIN_IMAGE_SIZE) )
{
- larger_w = smaller_w;
- smaller_w >>= 1;
+ larger_dim = smaller_dim;
+ smaller_dim >>= 1;
}
- S32 new_width = ( (F32)getWidth() / smaller_w > THRESHOLD ) ? larger_w : smaller_w;
+ return ( ((F32)curr_dim / (F32)smaller_dim) > THRESHOLD ) ? larger_dim : smaller_dim;
+}
+// static
+S32 LLImageRaw::expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim)
+{
+ S32 new_dim = MIN_IMAGE_SIZE;
+ while( (new_dim < curr_dim) && (new_dim < max_dim) )
+ {
+ new_dim <<= 1;
+ }
+ return new_dim;
+}
- S32 larger_h = max_dim; // 2^m >= mHeight
- S32 smaller_h = max_dim; // 2^(m-1) <= mHeight
- while( (smaller_h > getHeight()) && (smaller_h > MIN_IMAGE_SIZE) )
+// static
+S32 LLImageRaw::contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim)
+{
+ S32 new_dim = MAX_IMAGE_SIZE;
+ while( (new_dim > curr_dim) && (new_dim > min_dim) )
{
- larger_h = smaller_h;
- smaller_h >>= 1;
+ new_dim >>= 1;
}
- S32 new_height = ( (F32)getHeight() / smaller_h > THRESHOLD ) ? larger_h : smaller_h;
+ return new_dim;
+}
+void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
+{
+ // Find new sizes
+ S32 new_width = biasedDimToPowerOfTwo(getWidth(),max_dim);
+ S32 new_height = biasedDimToPowerOfTwo(getHeight(),max_dim);
scale( new_width, new_height );
}
-
-
-
// Calculates (U8)(255*(a/255.f)*(b/255.f) + 0.5f). Thanks, Jim Blinn!
inline U8 LLImageRaw::fastFractionalMult( U8 a, U8 b )
{
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index bf441a008a..cd3f76f1fd 100755
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -209,6 +209,9 @@ public:
void verticalFlip();
+ static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
+ static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
+ static S32 contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim = MIN_IMAGE_SIZE);
void expandToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
void contractToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
void biasedScaleToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE);
diff --git a/indra/llimage/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp
new file mode 100755
index 0000000000..3d0c488768
--- /dev/null
+++ b/indra/llimage/llimagefilter.cpp
@@ -0,0 +1,939 @@
+/**
+ * @file llimagefilter.cpp
+ * @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, 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 "llimagefilter.h"
+
+#include "llmath.h"
+#include "v3color.h"
+#include "v4coloru.h"
+#include "m3math.h"
+#include "v3math.h"
+#include "llsdserialize.h"
+#include "llstring.h"
+
+//---------------------------------------------------------------------------
+// LLImageFilter
+//---------------------------------------------------------------------------
+
+LLImageFilter::LLImageFilter(const std::string& file_path) :
+ mFilterData(LLSD::emptyArray()),
+ mImage(NULL),
+ mHistoRed(NULL),
+ mHistoGreen(NULL),
+ mHistoBlue(NULL),
+ mHistoBrightness(NULL),
+ mStencilBlendMode(STENCIL_BLEND_MODE_BLEND),
+ mStencilShape(STENCIL_SHAPE_UNIFORM),
+ mStencilGamma(1.0),
+ mStencilMin(0.0),
+ mStencilMax(1.0)
+{
+ // Load filter description from file
+ llifstream filter_xml(file_path);
+ if (filter_xml.is_open())
+ {
+ // Load and parse the file
+ LLPointer<LLSDParser> parser = new LLSDXMLParser();
+ parser->parse(filter_xml, mFilterData, LLSDSerialize::SIZE_UNLIMITED);
+ filter_xml.close();
+ }
+}
+
+LLImageFilter::~LLImageFilter()
+{
+ mImage = NULL;
+ ll_aligned_free_16(mHistoRed);
+ ll_aligned_free_16(mHistoGreen);
+ ll_aligned_free_16(mHistoBlue);
+ ll_aligned_free_16(mHistoBrightness);
+}
+
+/*
+ *TODO
+ * Rename stencil to mask
+ * Improve perf: use LUT for alpha blending in uniform case
+ * Add gradient coloring as a filter
+ */
+
+//============================================================================
+// Apply the filter data to the image passed as parameter
+//============================================================================
+
+void LLImageFilter::executeFilter(LLPointer<LLImageRaw> raw_image)
+{
+ mImage = raw_image;
+
+ //std::cout << "Filter : size = " << mFilterData.size() << std::endl;
+ for (S32 i = 0; i < mFilterData.size(); ++i)
+ {
+ std::string filter_name = mFilterData[i][0].asString();
+ // Dump out the filter values (for debug)
+ //std::cout << "Filter : name = " << mFilterData[i][0].asString() << ", params = ";
+ //for (S32 j = 1; j < mFilterData[i].size(); ++j)
+ //{
+ // std::cout << mFilterData[i][j].asString() << ", ";
+ //}
+ //std::cout << std::endl;
+
+ if (filter_name == "stencil")
+ {
+ // Get the shape of the stencil, that is how the procedural alpha is computed geometrically
+ std::string filter_shape = mFilterData[i][1].asString();
+ EStencilShape shape = STENCIL_SHAPE_UNIFORM;
+ if (filter_shape == "uniform")
+ {
+ shape = STENCIL_SHAPE_UNIFORM;
+ }
+ else if (filter_shape == "gradient")
+ {
+ shape = STENCIL_SHAPE_GRADIENT;
+ }
+ else if (filter_shape == "vignette")
+ {
+ shape = STENCIL_SHAPE_VIGNETTE;
+ }
+ else if (filter_shape == "scanlines")
+ {
+ shape = STENCIL_SHAPE_SCAN_LINES;
+ }
+ // Get the blend mode of the stencil, that is how the effect is blended in the background through the stencil
+ std::string filter_mode = mFilterData[i][2].asString();
+ EStencilBlendMode mode = STENCIL_BLEND_MODE_BLEND;
+ if (filter_mode == "blend")
+ {
+ mode = STENCIL_BLEND_MODE_BLEND;
+ }
+ else if (filter_mode == "add")
+ {
+ mode = STENCIL_BLEND_MODE_ADD;
+ }
+ else if (filter_mode == "add_back")
+ {
+ mode = STENCIL_BLEND_MODE_ABACK;
+ }
+ else if (filter_mode == "fade")
+ {
+ mode = STENCIL_BLEND_MODE_FADE;
+ }
+ // Get the float params: mandatory min, max then the optional parameters (4 max)
+ F32 min = (F32)(mFilterData[i][3].asReal());
+ F32 max = (F32)(mFilterData[i][4].asReal());
+ F32 params[4] = {0.0, 0.0, 0.0, 0.0};
+ for (S32 j = 5; (j < mFilterData[i].size()) && (j < 9); j++)
+ {
+ params[j-5] = (F32)(mFilterData[i][j].asReal());
+ }
+ // Set the stencil
+ setStencil(shape,mode,min,max,params);
+ }
+ else if (filter_name == "sepia")
+ {
+ filterSepia();
+ }
+ else if (filter_name == "grayscale")
+ {
+ filterGrayScale();
+ }
+ else if (filter_name == "saturate")
+ {
+ filterSaturate((float)(mFilterData[i][1].asReal()));
+ }
+ else if (filter_name == "rotate")
+ {
+ filterRotate((float)(mFilterData[i][1].asReal()));
+ }
+ else if (filter_name == "gamma")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterGamma((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "colorize")
+ {
+ LLColor3 color((float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()));
+ LLColor3 alpha((F32)(mFilterData[i][4].asReal()),(float)(mFilterData[i][5].asReal()),(float)(mFilterData[i][6].asReal()));
+ filterColorize(color,alpha);
+ }
+ else if (filter_name == "contrast")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterContrast((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "brighten")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterBrightness((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "darken")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterBrightness((float)(-mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "linearize")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterLinearize((float)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "posterize")
+ {
+ LLColor3 color((float)(mFilterData[i][2].asReal()),(float)(mFilterData[i][3].asReal()),(float)(mFilterData[i][4].asReal()));
+ filterEqualize((S32)(mFilterData[i][1].asReal()),color);
+ }
+ else if (filter_name == "screen")
+ {
+ std::string screen_name = mFilterData[i][1].asString();
+ EScreenMode mode = SCREEN_MODE_2DSINE;
+ if (screen_name == "2Dsine")
+ {
+ mode = SCREEN_MODE_2DSINE;
+ }
+ else if (screen_name == "line")
+ {
+ mode = SCREEN_MODE_LINE;
+ }
+ filterScreen(mode,(F32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal()));
+ }
+ else if (filter_name == "blur")
+ {
+ LLMatrix3 kernel;
+ for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[i][j] = 1.0;
+ convolve(kernel,true,false);
+ }
+ else if (filter_name == "sharpen")
+ {
+ LLMatrix3 kernel;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = -1.0;
+ kernel.mMatrix[1][1] = 9.0;
+ convolve(kernel,false,false);
+ }
+ else if (filter_name == "gradient")
+ {
+ LLMatrix3 kernel;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = -1.0;
+ kernel.mMatrix[1][1] = 8.0;
+ convolve(kernel,false,true);
+ }
+ else if (filter_name == "convolve")
+ {
+ LLMatrix3 kernel;
+ S32 index = 1;
+ bool normalize = (mFilterData[i][index++].asReal() > 0.0);
+ bool abs_value = (mFilterData[i][index++].asReal() > 0.0);
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ kernel.mMatrix[k][j] = mFilterData[i][index++].asReal();
+ convolve(kernel,normalize,abs_value);
+ }
+ else if (filter_name == "colortransform")
+ {
+ LLMatrix3 transform;
+ S32 index = 1;
+ for (S32 k = 0; k < NUM_VALUES_IN_MAT3; k++)
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ transform.mMatrix[k][j] = mFilterData[i][index++].asReal();
+ transform.transpose();
+ colorTransform(transform);
+ }
+ else
+ {
+ llwarns << "Filter unknown, cannot execute filter command : " << filter_name << llendl;
+ }
+ }
+}
+
+//============================================================================
+// Filter Primitives
+//============================================================================
+
+void LLImageFilter::blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue)
+{
+ F32 inv_alpha = 1.0 - alpha;
+ switch (mStencilBlendMode)
+ {
+ case STENCIL_BLEND_MODE_BLEND:
+ // Classic blend of incoming color with the background image
+ pixel[VRED] = inv_alpha * pixel[VRED] + alpha * red;
+ pixel[VGREEN] = inv_alpha * pixel[VGREEN] + alpha * green;
+ pixel[VBLUE] = inv_alpha * pixel[VBLUE] + alpha * blue;
+ break;
+ case STENCIL_BLEND_MODE_ADD:
+ // Add incoming color to the background image
+ pixel[VRED] = llclampb(pixel[VRED] + alpha * red);
+ pixel[VGREEN] = llclampb(pixel[VGREEN] + alpha * green);
+ pixel[VBLUE] = llclampb(pixel[VBLUE] + alpha * blue);
+ break;
+ case STENCIL_BLEND_MODE_ABACK:
+ // Add back background image to the incoming color
+ pixel[VRED] = llclampb(inv_alpha * pixel[VRED] + red);
+ pixel[VGREEN] = llclampb(inv_alpha * pixel[VGREEN] + green);
+ pixel[VBLUE] = llclampb(inv_alpha * pixel[VBLUE] + blue);
+ break;
+ case STENCIL_BLEND_MODE_FADE:
+ // Fade incoming color to black
+ pixel[VRED] = alpha * red;
+ pixel[VGREEN] = alpha * green;
+ pixel[VBLUE] = alpha * blue;
+ break;
+ }
+}
+
+void LLImageFilter::colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue)
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ S32 width = mImage->getWidth();
+ S32 height = mImage->getHeight();
+
+ U8* dst_data = mImage->getData();
+ for (S32 j = 0; j < height; j++)
+ {
+ for (S32 i = 0; i < width; i++)
+ {
+ // Blend LUT value
+ blendStencil(getStencilAlpha(i,j), dst_data, lut_red[dst_data[VRED]], lut_green[dst_data[VGREEN]], lut_blue[dst_data[VBLUE]]);
+ dst_data += components;
+ }
+ }
+}
+
+void LLImageFilter::colorTransform(const LLMatrix3 &transform)
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ S32 width = mImage->getWidth();
+ S32 height = mImage->getHeight();
+
+ U8* dst_data = mImage->getData();
+ for (S32 j = 0; j < height; j++)
+ {
+ for (S32 i = 0; i < width; i++)
+ {
+ // Compute transform
+ LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE]));
+ LLVector3 dst = src * transform;
+ dst.clamp(0.0f,255.0f);
+
+ // Blend result
+ blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]);
+ dst_data += components;
+ }
+ }
+}
+
+void LLImageFilter::convolve(const LLMatrix3 &kernel, bool normalize, bool abs_value)
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ // Compute normalization factors
+ F32 kernel_min = 0.0;
+ F32 kernel_max = 0.0;
+ for (S32 i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ {
+ for (S32 j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ {
+ if (kernel.mMatrix[i][j] >= 0.0)
+ kernel_max += kernel.mMatrix[i][j];
+ else
+ kernel_min += kernel.mMatrix[i][j];
+ }
+ }
+ if (abs_value)
+ {
+ kernel_max = llabs(kernel_max);
+ kernel_min = llabs(kernel_min);
+ kernel_max = llmax(kernel_max,kernel_min);
+ kernel_min = 0.0;
+ }
+ F32 kernel_range = kernel_max - kernel_min;
+
+ // Allocate temporary buffers and initialize algorithm's data
+ S32 width = mImage->getWidth();
+ S32 height = mImage->getHeight();
+
+ U8* dst_data = mImage->getData();
+
+ S32 buffer_size = width * components;
+ llassert_always(buffer_size > 0);
+ std::vector<U8> even_buffer(buffer_size);
+ std::vector<U8> odd_buffer(buffer_size);
+
+ U8* south_data = dst_data + buffer_size;
+ U8* east_west_data;
+ U8* north_data;
+
+ // Line 0 : we set the line to 0 (debatable)
+ memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
+ for (S32 i = 0; i < width; i++)
+ {
+ blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0);
+ dst_data += components;
+ }
+ south_data += buffer_size;
+
+ // All other lines
+ for (S32 j = 1; j < (height-1); j++)
+ {
+ // We need to buffer 2 lines. We flip north and east-west (current) to avoid moving too much memory around
+ if (j % 2)
+ {
+ memcpy( &odd_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
+ east_west_data = &odd_buffer[0];
+ north_data = &even_buffer[0];
+ }
+ else
+ {
+ memcpy( &even_buffer[0], dst_data, buffer_size ); /* Flawfinder: ignore */
+ east_west_data = &even_buffer[0];
+ north_data = &odd_buffer[0];
+ }
+ // First pixel : set to 0
+ blendStencil(getStencilAlpha(0,j), dst_data, 0, 0, 0);
+ dst_data += components;
+ // Set pointers to kernel
+ U8* NW = north_data;
+ U8* N = NW+components;
+ U8* NE = N+components;
+ U8* W = east_west_data;
+ U8* C = W+components;
+ U8* E = C+components;
+ U8* SW = south_data;
+ U8* S = SW+components;
+ U8* SE = S+components;
+ // All other pixels
+ for (S32 i = 1; i < (width-1); i++)
+ {
+ // Compute convolution
+ LLVector3 dst;
+ dst.mV[VRED] = (kernel.mMatrix[0][0]*NW[VRED] + kernel.mMatrix[0][1]*N[VRED] + kernel.mMatrix[0][2]*NE[VRED] +
+ kernel.mMatrix[1][0]*W[VRED] + kernel.mMatrix[1][1]*C[VRED] + kernel.mMatrix[1][2]*E[VRED] +
+ kernel.mMatrix[2][0]*SW[VRED] + kernel.mMatrix[2][1]*S[VRED] + kernel.mMatrix[2][2]*SE[VRED]);
+ dst.mV[VGREEN] = (kernel.mMatrix[0][0]*NW[VGREEN] + kernel.mMatrix[0][1]*N[VGREEN] + kernel.mMatrix[0][2]*NE[VGREEN] +
+ kernel.mMatrix[1][0]*W[VGREEN] + kernel.mMatrix[1][1]*C[VGREEN] + kernel.mMatrix[1][2]*E[VGREEN] +
+ kernel.mMatrix[2][0]*SW[VGREEN] + kernel.mMatrix[2][1]*S[VGREEN] + kernel.mMatrix[2][2]*SE[VGREEN]);
+ dst.mV[VBLUE] = (kernel.mMatrix[0][0]*NW[VBLUE] + kernel.mMatrix[0][1]*N[VBLUE] + kernel.mMatrix[0][2]*NE[VBLUE] +
+ kernel.mMatrix[1][0]*W[VBLUE] + kernel.mMatrix[1][1]*C[VBLUE] + kernel.mMatrix[1][2]*E[VBLUE] +
+ kernel.mMatrix[2][0]*SW[VBLUE] + kernel.mMatrix[2][1]*S[VBLUE] + kernel.mMatrix[2][2]*SE[VBLUE]);
+ if (abs_value)
+ {
+ dst.mV[VRED] = llabs(dst.mV[VRED]);
+ dst.mV[VGREEN] = llabs(dst.mV[VGREEN]);
+ dst.mV[VBLUE] = llabs(dst.mV[VBLUE]);
+ }
+ if (normalize)
+ {
+ dst.mV[VRED] = (dst.mV[VRED] - kernel_min)/kernel_range;
+ dst.mV[VGREEN] = (dst.mV[VGREEN] - kernel_min)/kernel_range;
+ dst.mV[VBLUE] = (dst.mV[VBLUE] - kernel_min)/kernel_range;
+ }
+ dst.clamp(0.0f,255.0f);
+
+ // Blend result
+ blendStencil(getStencilAlpha(i,j), dst_data, dst.mV[VRED], dst.mV[VGREEN], dst.mV[VBLUE]);
+
+ // Next pixel
+ dst_data += components;
+ NW += components;
+ N += components;
+ NE += components;
+ W += components;
+ C += components;
+ E += components;
+ SW += components;
+ S += components;
+ SE += components;
+ }
+ // Last pixel : set to 0
+ blendStencil(getStencilAlpha(width-1,j), dst_data, 0, 0, 0);
+ dst_data += components;
+ south_data += buffer_size;
+ }
+
+ // Last line
+ for (S32 i = 0; i < width; i++)
+ {
+ blendStencil(getStencilAlpha(i,0), dst_data, 0, 0, 0);
+ dst_data += components;
+ }
+}
+
+void LLImageFilter::filterScreen(EScreenMode mode, const F32 wave_length, const F32 angle)
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ S32 width = mImage->getWidth();
+ S32 height = mImage->getHeight();
+
+ F32 wave_length_pixels = wave_length * (F32)(height) / 2.0;
+ F32 sin = sinf(angle*DEG_TO_RAD);
+ F32 cos = cosf(angle*DEG_TO_RAD);
+
+ // Precompute the gamma table : gives us the gray level to use when cutting outside the screen (prevents strong aliasing on the screen)
+ U8 gamma[256];
+ for (S32 i = 0; i < 256; i++)
+ {
+ F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/4.0)));
+ gamma[i] = (U8)(255.0 * gamma_i);
+ }
+
+ U8* dst_data = mImage->getData();
+ for (S32 j = 0; j < height; j++)
+ {
+ for (S32 i = 0; i < width; i++)
+ {
+ // Compute screen value
+ F32 value = 0.0;
+ F32 di = 0.0;
+ F32 dj = 0.0;
+ switch (mode)
+ {
+ case SCREEN_MODE_2DSINE:
+ di = cos*i + sin*j;
+ dj = -sin*i + cos*j;
+ value = (sinf(2*F_PI*di/wave_length_pixels)*sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
+ break;
+ case SCREEN_MODE_LINE:
+ dj = sin*i - cos*j;
+ value = (sinf(2*F_PI*dj/wave_length_pixels)+1.0)*255.0/2.0;
+ break;
+ }
+ U8 dst_value = (dst_data[VRED] >= (U8)(value) ? gamma[dst_data[VRED] - (U8)(value)] : 0);
+
+ // Blend result
+ blendStencil(getStencilAlpha(i,j), dst_data, dst_value, dst_value, dst_value);
+ dst_data += components;
+ }
+ }
+}
+
+//============================================================================
+// Procedural Stencils
+//============================================================================
+void LLImageFilter::setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params)
+{
+ mStencilShape = shape;
+ mStencilBlendMode = mode;
+ mStencilMin = llmin(llmax(min, -1.0f), 1.0f);
+ mStencilMax = llmin(llmax(max, -1.0f), 1.0f);
+
+ // Each shape will interpret the 4 params differenly.
+ // We compute each systematically, though, clearly, values are meaningless when the shape doesn't correspond to the parameters
+ mStencilCenterX = (S32)(mImage->getWidth() + params[0] * (F32)(mImage->getHeight()))/2;
+ mStencilCenterY = (S32)(mImage->getHeight() + params[1] * (F32)(mImage->getHeight()))/2;
+ mStencilWidth = (S32)(params[2] * (F32)(mImage->getHeight()))/2;
+ mStencilGamma = (params[3] <= 0.0 ? 1.0 : params[3]);
+
+ mStencilWavelength = (params[0] <= 0.0 ? 10.0 : params[0] * (F32)(mImage->getHeight()) / 2.0);
+ mStencilSine = sinf(params[1]*DEG_TO_RAD);
+ mStencilCosine = cosf(params[1]*DEG_TO_RAD);
+
+ mStencilStartX = ((F32)(mImage->getWidth()) + params[0] * (F32)(mImage->getHeight()))/2.0;
+ mStencilStartY = ((F32)(mImage->getHeight()) + params[1] * (F32)(mImage->getHeight()))/2.0;
+ F32 end_x = ((F32)(mImage->getWidth()) + params[2] * (F32)(mImage->getHeight()))/2.0;
+ F32 end_y = ((F32)(mImage->getHeight()) + params[3] * (F32)(mImage->getHeight()))/2.0;
+ mStencilGradX = end_x - mStencilStartX;
+ mStencilGradY = end_y - mStencilStartY;
+ mStencilGradN = mStencilGradX*mStencilGradX + mStencilGradY*mStencilGradY;
+}
+
+F32 LLImageFilter::getStencilAlpha(S32 i, S32 j)
+{
+ F32 alpha = 1.0; // That init actually takes care of the STENCIL_SHAPE_UNIFORM case...
+ if (mStencilShape == STENCIL_SHAPE_VIGNETTE)
+ {
+ // alpha is a modified gaussian value, with a center and fading in a circular pattern toward the edges
+ // The gamma parameter controls the intensity of the drop down from alpha 1.0 (center) to 0.0
+ F32 d_center_square = (i - mStencilCenterX)*(i - mStencilCenterX) + (j - mStencilCenterY)*(j - mStencilCenterY);
+ alpha = powf(F_E, -(powf((d_center_square/(mStencilWidth*mStencilWidth)),mStencilGamma)/2.0f));
+ }
+ else if (mStencilShape == STENCIL_SHAPE_SCAN_LINES)
+ {
+ // alpha varies according to a squared sine function.
+ F32 d = mStencilSine*i - mStencilCosine*j;
+ alpha = (sinf(2*F_PI*d/mStencilWavelength) > 0.0 ? 1.0 : 0.0);
+ }
+ else if (mStencilShape == STENCIL_SHAPE_GRADIENT)
+ {
+ alpha = (((F32)(i) - mStencilStartX)*mStencilGradX + ((F32)(j) - mStencilStartY)*mStencilGradY) / mStencilGradN;
+ alpha = llclampf(alpha);
+ }
+
+ // We rescale alpha between min and max
+ return (mStencilMin + alpha * (mStencilMax - mStencilMin));
+}
+
+//============================================================================
+// Histograms
+//============================================================================
+
+U32* LLImageFilter::getBrightnessHistogram()
+{
+ if (!mHistoBrightness)
+ {
+ computeHistograms();
+ }
+ return mHistoBrightness;
+}
+
+void LLImageFilter::computeHistograms()
+{
+ const S32 components = mImage->getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ // Allocate memory for the histograms
+ if (!mHistoRed)
+ {
+ mHistoRed = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoGreen)
+ {
+ mHistoGreen = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoBlue)
+ {
+ mHistoBlue = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+ if (!mHistoBrightness)
+ {
+ mHistoBrightness = (U32*) ll_aligned_malloc_16(256*sizeof(U32));
+ }
+
+ // Initialize them
+ for (S32 i = 0; i < 256; i++)
+ {
+ mHistoRed[i] = 0;
+ mHistoGreen[i] = 0;
+ mHistoBlue[i] = 0;
+ mHistoBrightness[i] = 0;
+ }
+
+ // Compute them
+ S32 pixels = mImage->getWidth() * mImage->getHeight();
+ U8* dst_data = mImage->getData();
+ for (S32 i = 0; i < pixels; i++)
+ {
+ mHistoRed[dst_data[VRED]]++;
+ mHistoGreen[dst_data[VGREEN]]++;
+ mHistoBlue[dst_data[VBLUE]]++;
+ // Note: this is a very simple shorthand for brightness but it's OK for our use
+ S32 brightness = ((S32)(dst_data[VRED]) + (S32)(dst_data[VGREEN]) + (S32)(dst_data[VBLUE])) / 3;
+ mHistoBrightness[brightness]++;
+ // next pixel...
+ dst_data += components;
+ }
+}
+
+//============================================================================
+// Secondary Filters
+//============================================================================
+
+void LLImageFilter::filterGrayScale()
+{
+ LLMatrix3 gray_scale;
+ LLVector3 luminosity(0.2125, 0.7154, 0.0721);
+ gray_scale.setRows(luminosity, luminosity, luminosity);
+ gray_scale.transpose();
+ colorTransform(gray_scale);
+}
+
+void LLImageFilter::filterSepia()
+{
+ LLMatrix3 sepia;
+ sepia.setRows(LLVector3(0.3588, 0.7044, 0.1368),
+ LLVector3(0.2990, 0.5870, 0.1140),
+ LLVector3(0.2392, 0.4696, 0.0912));
+ sepia.transpose();
+ colorTransform(sepia);
+}
+
+void LLImageFilter::filterSaturate(F32 saturation)
+{
+ // Matrix to Lij
+ LLMatrix3 r_a;
+ LLMatrix3 r_b;
+
+ // 45 degre rotation around z
+ r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0),
+ LLVector3(-OO_SQRT2, OO_SQRT2, 0.0),
+ LLVector3( 0.0, 0.0, 1.0));
+ // 54.73 degre rotation around y
+ float oo_sqrt3 = 1.0f / F_SQRT3;
+ float sin_54 = F_SQRT2 * oo_sqrt3;
+ r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54),
+ LLVector3(0.0, 1.0, 0.0),
+ LLVector3(sin_54, 0.0, oo_sqrt3));
+
+ // Coordinate conversion
+ LLMatrix3 Lij = r_b * r_a;
+ LLMatrix3 Lij_inv = Lij;
+ Lij_inv.transpose();
+
+ // Local saturation transform
+ LLMatrix3 s;
+ s.setRows(LLVector3(saturation, 0.0, 0.0),
+ LLVector3(0.0, saturation, 0.0),
+ LLVector3(0.0, 0.0, 1.0));
+
+ // Global saturation transform
+ LLMatrix3 transfo = Lij_inv * s * Lij;
+ colorTransform(transfo);
+}
+
+void LLImageFilter::filterRotate(F32 angle)
+{
+ // Matrix to Lij
+ LLMatrix3 r_a;
+ LLMatrix3 r_b;
+
+ // 45 degre rotation around z
+ r_a.setRows(LLVector3( OO_SQRT2, OO_SQRT2, 0.0),
+ LLVector3(-OO_SQRT2, OO_SQRT2, 0.0),
+ LLVector3( 0.0, 0.0, 1.0));
+ // 54.73 degre rotation around y
+ float oo_sqrt3 = 1.0f / F_SQRT3;
+ float sin_54 = F_SQRT2 * oo_sqrt3;
+ r_b.setRows(LLVector3(oo_sqrt3, 0.0, -sin_54),
+ LLVector3(0.0, 1.0, 0.0),
+ LLVector3(sin_54, 0.0, oo_sqrt3));
+
+ // Coordinate conversion
+ LLMatrix3 Lij = r_b * r_a;
+ LLMatrix3 Lij_inv = Lij;
+ Lij_inv.transpose();
+
+ // Local color rotation transform
+ LLMatrix3 r;
+ angle *= DEG_TO_RAD;
+ r.setRows(LLVector3( cosf(angle), sinf(angle), 0.0),
+ LLVector3(-sinf(angle), cosf(angle), 0.0),
+ LLVector3( 0.0, 0.0, 1.0));
+
+ // Global color rotation transform
+ LLMatrix3 transfo = Lij_inv * r * Lij;
+ colorTransform(transfo);
+}
+
+void LLImageFilter::filterGamma(F32 gamma, const LLColor3& alpha)
+{
+ U8 gamma_red_lut[256];
+ U8 gamma_green_lut[256];
+ U8 gamma_blue_lut[256];
+
+ for (S32 i = 0; i < 256; i++)
+ {
+ F32 gamma_i = llclampf((float)(powf((float)(i)/255.0,1.0/gamma)));
+ // Blend in with alpha values
+ gamma_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * 255.0 * gamma_i);
+ gamma_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * 255.0 * gamma_i);
+ gamma_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * 255.0 * gamma_i);
+ }
+
+ colorCorrect(gamma_red_lut,gamma_green_lut,gamma_blue_lut);
+}
+
+void LLImageFilter::filterLinearize(F32 tail, const LLColor3& alpha)
+{
+ // Get the histogram
+ U32* histo = getBrightnessHistogram();
+
+ // Compute cumulated histogram
+ U32 cumulated_histo[256];
+ cumulated_histo[0] = histo[0];
+ for (S32 i = 1; i < 256; i++)
+ {
+ cumulated_histo[i] = cumulated_histo[i-1] + histo[i];
+ }
+
+ // Compute min and max counts minus tail
+ tail = llclampf(tail);
+ S32 total = cumulated_histo[255];
+ S32 min_c = (S32)((F32)(total) * tail);
+ S32 max_c = (S32)((F32)(total) * (1.0 - tail));
+
+ // Find min and max values
+ S32 min_v = 0;
+ while (cumulated_histo[min_v] < min_c)
+ {
+ min_v++;
+ }
+ S32 max_v = 255;
+ while (cumulated_histo[max_v] > max_c)
+ {
+ max_v--;
+ }
+
+ // Compute linear lookup table
+ U8 linear_red_lut[256];
+ U8 linear_green_lut[256];
+ U8 linear_blue_lut[256];
+ if (max_v == min_v)
+ {
+ // Degenerated binary split case
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (i < min_v ? 0 : 255);
+ // Blend in with alpha values
+ linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+ }
+ else
+ {
+ // Linearize between min and max
+ F32 slope = 255.0 / (F32)(max_v - min_v);
+ F32 translate = -min_v * slope;
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (U8)(llclampb((S32)(slope*i + translate)));
+ // Blend in with alpha values
+ linear_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ linear_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ linear_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+ }
+
+ // Apply lookup table
+ colorCorrect(linear_red_lut,linear_green_lut,linear_blue_lut);
+}
+
+void LLImageFilter::filterEqualize(S32 nb_classes, const LLColor3& alpha)
+{
+ // Regularize the parameter: must be between 2 and 255
+ nb_classes = llmax(nb_classes,2);
+ nb_classes = llclampb(nb_classes);
+
+ // Get the histogram
+ U32* histo = getBrightnessHistogram();
+
+ // Compute cumulated histogram
+ U32 cumulated_histo[256];
+ cumulated_histo[0] = histo[0];
+ for (S32 i = 1; i < 256; i++)
+ {
+ cumulated_histo[i] = cumulated_histo[i-1] + histo[i];
+ }
+
+ // Compute deltas
+ S32 total = cumulated_histo[255];
+ S32 delta_count = total / nb_classes;
+ S32 current_count = delta_count;
+ S32 delta_value = 256 / (nb_classes - 1);
+ S32 current_value = 0;
+
+ // Compute equalized lookup table
+ U8 equalize_red_lut[256];
+ U8 equalize_green_lut[256];
+ U8 equalize_blue_lut[256];
+ for (S32 i = 0; i < 256; i++)
+ {
+ // Blend in current_value with alpha values
+ equalize_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * current_value);
+ equalize_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * current_value);
+ equalize_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * current_value);
+ if (cumulated_histo[i] >= current_count)
+ {
+ current_count += delta_count;
+ current_value += delta_value;
+ current_value = llclampb(current_value);
+ }
+ }
+
+ // Apply lookup table
+ colorCorrect(equalize_red_lut,equalize_green_lut,equalize_blue_lut);
+}
+
+void LLImageFilter::filterColorize(const LLColor3& color, const LLColor3& alpha)
+{
+ U8 red_lut[256];
+ U8 green_lut[256];
+ U8 blue_lut[256];
+
+ F32 red_composite = 255.0 * alpha.mV[0] * color.mV[0];
+ F32 green_composite = 255.0 * alpha.mV[1] * color.mV[1];
+ F32 blue_composite = 255.0 * alpha.mV[2] * color.mV[2];
+
+ for (S32 i = 0; i < 256; i++)
+ {
+ red_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[0]) * (F32)(i) + red_composite)));
+ green_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[1]) * (F32)(i) + green_composite)));
+ blue_lut[i] = (U8)(llclampb((S32)((1.0 - alpha.mV[2]) * (F32)(i) + blue_composite)));
+ }
+
+ colorCorrect(red_lut,green_lut,blue_lut);
+}
+
+void LLImageFilter::filterContrast(F32 slope, const LLColor3& alpha)
+{
+ U8 contrast_red_lut[256];
+ U8 contrast_green_lut[256];
+ U8 contrast_blue_lut[256];
+
+ F32 translate = 128.0 * (1.0 - slope);
+
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (U8)(llclampb((S32)(slope*i + translate)));
+ // Blend in with alpha values
+ contrast_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ contrast_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ contrast_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+
+ colorCorrect(contrast_red_lut,contrast_green_lut,contrast_blue_lut);
+}
+
+void LLImageFilter::filterBrightness(F32 add, const LLColor3& alpha)
+{
+ U8 brightness_red_lut[256];
+ U8 brightness_green_lut[256];
+ U8 brightness_blue_lut[256];
+
+ S32 add_value = (S32)(add * 255.0);
+
+ for (S32 i = 0; i < 256; i++)
+ {
+ U8 value_i = (U8)(llclampb(i + add_value));
+ // Blend in with alpha values
+ brightness_red_lut[i] = (U8)((1.0 - alpha.mV[0]) * (float)(i) + alpha.mV[0] * value_i);
+ brightness_green_lut[i] = (U8)((1.0 - alpha.mV[1]) * (float)(i) + alpha.mV[1] * value_i);
+ brightness_blue_lut[i] = (U8)((1.0 - alpha.mV[2]) * (float)(i) + alpha.mV[2] * value_i);
+ }
+
+ colorCorrect(brightness_red_lut,brightness_green_lut,brightness_blue_lut);
+}
+
+//============================================================================
diff --git a/indra/llimage/llimagefilter.h b/indra/llimage/llimagefilter.h
new file mode 100755
index 0000000000..16ec395f76
--- /dev/null
+++ b/indra/llimage/llimagefilter.h
@@ -0,0 +1,137 @@
+/**
+ * @file llimagefilter.h
+ * @brief Simple Image Filtering. See https://wiki.lindenlab.com/wiki/SL_Viewer_Image_Filters for complete documentation.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, 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_LLIMAGEFILTER_H
+#define LL_LLIMAGEFILTER_H
+
+#include "llsd.h"
+#include "llimage.h"
+
+class LLImageRaw;
+class LLColor4U;
+class LLColor3;
+class LLMatrix3;
+
+typedef enum e_stencil_blend_mode
+{
+ STENCIL_BLEND_MODE_BLEND = 0,
+ STENCIL_BLEND_MODE_ADD = 1,
+ STENCIL_BLEND_MODE_ABACK = 2,
+ STENCIL_BLEND_MODE_FADE = 3
+} EStencilBlendMode;
+
+typedef enum e_stencil_shape
+{
+ STENCIL_SHAPE_UNIFORM = 0,
+ STENCIL_SHAPE_GRADIENT = 1,
+ STENCIL_SHAPE_VIGNETTE = 2,
+ STENCIL_SHAPE_SCAN_LINES = 3
+} EStencilShape;
+
+typedef enum e_screen_mode
+{
+ SCREEN_MODE_2DSINE = 0,
+ SCREEN_MODE_LINE = 1
+} EScreenMode;
+
+//============================================================================
+// LLImageFilter
+//============================================================================
+
+class LLImageFilter
+{
+public:
+ LLImageFilter(const std::string& file_path);
+ ~LLImageFilter();
+
+ void executeFilter(LLPointer<LLImageRaw> raw_image);
+
+private:
+ // Filter Operations : Transforms
+ void filterGrayScale(); // Convert to grayscale
+ void filterSepia(); // Convert to sepia
+ void filterSaturate(F32 saturation); // < 1.0 desaturates, > 1.0 saturates
+ void filterRotate(F32 angle); // Rotates hue according to angle, angle in degrees
+
+ // Filter Operations : Color Corrections
+ // When specified, the LLColor3 alpha parameter indicates the intensity of the effect for each color channel
+ // acting in effect as an alpha blending factor different for each channel. For instance (1.0,0.0,0.0) will apply
+ // the effect only to the Red channel. Intermediate values blends the effect with the source color.
+ void filterGamma(F32 gamma, const LLColor3& alpha); // Apply gamma to each channel
+ void filterLinearize(F32 tail, const LLColor3& alpha); // Use histogram to linearize constrast between min and max values minus tail
+ void filterEqualize(S32 nb_classes, const LLColor3& alpha); // Use histogram to equalize constrast between nb_classes throughout the image
+ void filterColorize(const LLColor3& color, const LLColor3& alpha); // Colorize with color and alpha per channel
+ void filterContrast(F32 slope, const LLColor3& alpha); // Change contrast according to slope: > 1.0 more contrast, < 1.0 less contrast
+ void filterBrightness(F32 add, const LLColor3& alpha); // Change brightness according to add: > 0 brighter, < 0 darker
+
+ // Filter Primitives
+ void colorTransform(const LLMatrix3 &transform);
+ void colorCorrect(const U8* lut_red, const U8* lut_green, const U8* lut_blue);
+ void filterScreen(EScreenMode mode, const F32 wave_length, const F32 angle);
+ void blendStencil(F32 alpha, U8* pixel, U8 red, U8 green, U8 blue);
+ void convolve(const LLMatrix3 &kernel, bool normalize, bool abs_value);
+
+ // Procedural Stencils
+ void setStencil(EStencilShape shape, EStencilBlendMode mode, F32 min, F32 max, F32* params);
+ F32 getStencilAlpha(S32 i, S32 j);
+
+ // Histograms
+ U32* getBrightnessHistogram();
+ void computeHistograms();
+
+ LLSD mFilterData;
+ LLPointer<LLImageRaw> mImage;
+
+ // Histograms (if we ever happen to need them)
+ U32 *mHistoRed;
+ U32 *mHistoGreen;
+ U32 *mHistoBlue;
+ U32 *mHistoBrightness;
+
+ // Current Stencil Settings
+ EStencilBlendMode mStencilBlendMode;
+ EStencilShape mStencilShape;
+ F32 mStencilMin;
+ F32 mStencilMax;
+
+ S32 mStencilCenterX;
+ S32 mStencilCenterY;
+ S32 mStencilWidth;
+ F32 mStencilGamma;
+
+ F32 mStencilWavelength;
+ F32 mStencilSine;
+ F32 mStencilCosine;
+
+ F32 mStencilStartX;
+ F32 mStencilStartY;
+ F32 mStencilGradX;
+ F32 mStencilGradY;
+ F32 mStencilGradN;
+};
+
+
+#endif
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 80981d04bd..0aadf3e0e8 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -201,6 +201,7 @@ set(viewer_SOURCE_FILES
llfilteredwearablelist.cpp
llfirstuse.cpp
llflexibleobject.cpp
+ llflickrconnect.cpp
llfloaterabout.cpp
llfloaterbvhpreview.cpp
llfloaterauction.cpp
@@ -209,6 +210,7 @@ set(viewer_SOURCE_FILES
llfloateravatarpicker.cpp
llfloateravatartextures.cpp
llfloaterbeacons.cpp
+ llfloaterbigpreview.cpp
llfloaterbuildoptions.cpp
llfloaterbulkpermission.cpp
llfloaterbump.cpp
@@ -230,6 +232,8 @@ set(viewer_SOURCE_FILES
llfloatereditwater.cpp
llfloaterenvironmentsettings.cpp
llfloaterevent.cpp
+ llfloaterfacebook.cpp
+ llfloaterflickr.cpp
llfloaterexperiencepicker.cpp
llfloaterexperienceprofile.cpp
llfloaterexperiences.cpp
@@ -283,7 +287,6 @@ set(viewer_SOURCE_FILES
llfloatersettingsdebug.cpp
llfloatersidepanelcontainer.cpp
llfloatersnapshot.cpp
- llfloatersocial.cpp
llfloatersounddevices.cpp
llfloaterspellchecksettings.cpp
llfloatertelehub.cpp
@@ -295,6 +298,7 @@ set(viewer_SOURCE_FILES
llfloatertos.cpp
llfloatertoybox.cpp
llfloatertranslationsettings.cpp
+ llfloatertwitter.cpp
llfloateruipreview.cpp
llfloaterurlentry.cpp
llfloatervoiceeffect.cpp
@@ -332,6 +336,7 @@ set(viewer_SOURCE_FILES
llhudrender.cpp
llhudtext.cpp
llhudview.cpp
+ llimagefiltersmanager.cpp
llimhandler.cpp
llimview.cpp
llinspect.cpp
@@ -580,6 +585,7 @@ set(viewer_SOURCE_FILES
lltransientdockablefloater.cpp
lltransientfloatermgr.cpp
lltranslate.cpp
+ lltwitterconnect.cpp
lluilistener.cpp
lluploaddialog.cpp
lluploadfloaterobservers.cpp
@@ -803,6 +809,7 @@ set(viewer_HEADER_FILES
llfilteredwearablelist.h
llfirstuse.h
llflexibleobject.h
+ llflickrconnect.h
llfloaterabout.h
llfloaterbvhpreview.h
llfloaterauction.h
@@ -811,6 +818,7 @@ set(viewer_HEADER_FILES
llfloateravatarpicker.h
llfloateravatartextures.h
llfloaterbeacons.h
+ llfloaterbigpreview.h
llfloaterbuildoptions.h
llfloaterbulkpermission.h
llfloaterbump.h
@@ -832,6 +840,8 @@ set(viewer_HEADER_FILES
llfloatereditwater.h
llfloaterenvironmentsettings.h
llfloaterevent.h
+ llfloaterfacebook.h
+ llfloaterflickr.h
llfloaterexperiencepicker.h
llfloaterexperienceprofile.h
llfloaterexperiences.h
@@ -888,7 +898,6 @@ set(viewer_HEADER_FILES
llfloatersettingsdebug.h
llfloatersidepanelcontainer.h
llfloatersnapshot.h
- llfloatersocial.h
llfloatersounddevices.h
llfloaterspellchecksettings.h
llfloatertelehub.h
@@ -900,6 +909,7 @@ set(viewer_HEADER_FILES
llfloatertos.h
llfloatertoybox.h
llfloatertranslationsettings.h
+ llfloatertwitter.h
llfloateruipreview.h
llfloaterurlentry.h
llfloatervoiceeffect.h
@@ -936,6 +946,7 @@ set(viewer_HEADER_FILES
llhudrender.h
llhudtext.h
llhudview.h
+ llimagefiltersmanager.h
llimview.h
llinspect.h
llinspectavatar.h
@@ -1172,6 +1183,7 @@ set(viewer_HEADER_FILES
lltransientdockablefloater.h
lltransientfloatermgr.h
lltranslate.h
+ lltwitterconnect.h
lluiconstants.h
lluilistener.h
lluploaddialog.h
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index f06fb9e915..584a914c29 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-3.7.10
+3.7.11
diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml
index 60c942094a..7b329e2092 100755
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -216,15 +216,35 @@
is_running_function="Floater.IsOpen"
is_running_parameters="snapshot"
/>
- <command name="social"
+ <command name="facebook"
available_in_toybox="true"
- icon="Command_Social_Icon"
- label_ref="Command_Social_Label"
- tooltip_ref="Command_Social_Tooltip"
+ icon="Command_Facebook_Icon"
+ label_ref="Command_Facebook_Label"
+ tooltip_ref="Command_Facebook_Tooltip"
execute_function="Floater.ToggleOrBringToFront"
- execute_parameters="social"
+ execute_parameters="facebook"
is_running_function="Floater.IsOpen"
- is_running_parameters="social"
+ is_running_parameters="facebook"
+ />
+ <command name="flickr"
+ available_in_toybox="true"
+ icon="Command_Flickr_Icon"
+ label_ref="Command_Flickr_Label"
+ tooltip_ref="Command_Flickr_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="flickr"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="flickr"
+ />
+ <command name="twitter"
+ available_in_toybox="true"
+ icon="Command_Twitter_Icon"
+ label_ref="Command_Twitter_Label"
+ tooltip_ref="Command_Twitter_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="twitter"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="twitter"
/>
<command name="speak"
available_in_toybox="true"
diff --git a/indra/newview/app_settings/filters/Autocontrast.xml b/indra/newview/app_settings/filters/Autocontrast.xml
new file mode 100755
index 0000000000..ec3d7561bd
--- /dev/null
+++ b/indra/newview/app_settings/filters/Autocontrast.xml
@@ -0,0 +1,11 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.01</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/filters/BlackAndWhite.xml b/indra/newview/app_settings/filters/BlackAndWhite.xml
new file mode 100644
index 0000000000..101ed8233a
--- /dev/null
+++ b/indra/newview/app_settings/filters/BlackAndWhite.xml
@@ -0,0 +1,21 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.01</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>0.8</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/filters/Colors1970.xml b/indra/newview/app_settings/filters/Colors1970.xml
new file mode 100644
index 0000000000..730d907fa7
--- /dev/null
+++ b/indra/newview/app_settings/filters/Colors1970.xml
@@ -0,0 +1,47 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>0.8</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.3</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>10.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.1</real>
+ <real>0.1</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/filters/Intense.xml b/indra/newview/app_settings/filters/Intense.xml
new file mode 100644
index 0000000000..b77f07a037
--- /dev/null
+++ b/indra/newview/app_settings/filters/Intense.xml
@@ -0,0 +1,8 @@
+<llsd>
+ <array>
+ <array>
+ <string>saturate</string>
+ <real>3.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/filters/LensFlare.xml b/indra/newview/app_settings/filters/LensFlare.xml
new file mode 100644
index 0000000000..e9aef6eea4
--- /dev/null
+++ b/indra/newview/app_settings/filters/LensFlare.xml
@@ -0,0 +1,131 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.01</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>gradient</string>
+ <string>add</string>
+ <real>0.5</real>
+ <real>0.0</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>-1.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.1</real>
+ <real>0.1</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.5</real>
+ <real>5.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.6</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>-1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>5.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.6</real>
+ <real>0.6</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.5</real>
+ <real>-0.5</real>
+ <real>0.10</real>
+ <real>20.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.7</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.6</real>
+ <real>-0.6</real>
+ <real>0.05</real>
+ <real>20.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.7</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>0.4</real>
+ <real>-0.4</real>
+ <real>0.025</real>
+ <real>20.0</real>
+ </array>
+ <array>
+ <string>colorize</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>0.7</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/filters/Miniature.xml b/indra/newview/app_settings/filters/Miniature.xml
new file mode 100755
index 0000000000..9aa8a87c6f
--- /dev/null
+++ b/indra/newview/app_settings/filters/Miniature.xml
@@ -0,0 +1,118 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.02</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.02</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>saturate</string>
+ <real>1.2</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>0.0</real>
+ <real>0.25</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.25</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>sharpen</string>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>gradient</string>
+ <string>blend</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>-1.0</real>
+ <real>0.0</real>
+ <real>-0.25</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>gradient</string>
+ <string>blend</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.25</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd> \ No newline at end of file
diff --git a/indra/newview/app_settings/filters/Newspaper.xml b/indra/newview/app_settings/filters/Newspaper.xml
new file mode 100755
index 0000000000..6cfe319281
--- /dev/null
+++ b/indra/newview/app_settings/filters/Newspaper.xml
@@ -0,0 +1,20 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>2Dsine</string>
+ <real>0.02</real>
+ <real>0.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/filters/Sepia.xml b/indra/newview/app_settings/filters/Sepia.xml
new file mode 100644
index 0000000000..3d577b2998
--- /dev/null
+++ b/indra/newview/app_settings/filters/Sepia.xml
@@ -0,0 +1,32 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.01</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>0.8</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>fade</string>
+ <real>0.5</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>4.0</real>
+ </array>
+ <array>
+ <string>sepia</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/filters/Spotlight.xml b/indra/newview/app_settings/filters/Spotlight.xml
new file mode 100644
index 0000000000..0e2e0ad68c
--- /dev/null
+++ b/indra/newview/app_settings/filters/Spotlight.xml
@@ -0,0 +1,47 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.4</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>add</string>
+ <real>-0.8</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/filters/Toycamera.xml b/indra/newview/app_settings/filters/Toycamera.xml
new file mode 100755
index 0000000000..4e76f6b2fb
--- /dev/null
+++ b/indra/newview/app_settings/filters/Toycamera.xml
@@ -0,0 +1,46 @@
+<llsd>
+ <array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>fade</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.2</real>
+ <real>3.0</real>
+ </array>
+ <array>
+ <string>linearize</string>
+ <real>0.05</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>grayscale</string>
+ </array>
+ <array>
+ <string>contrast</string>
+ <real>1.1</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>vignette</string>
+ <string>blend</string>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.5</real>
+ <real>2.0</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd> \ No newline at end of file
diff --git a/indra/newview/app_settings/filters/Video.xml b/indra/newview/app_settings/filters/Video.xml
new file mode 100755
index 0000000000..fe17f3950a
--- /dev/null
+++ b/indra/newview/app_settings/filters/Video.xml
@@ -0,0 +1,44 @@
+<llsd>
+ <array>
+ <array>
+ <string>linearize</string>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>darken</string>
+ <real>0.15</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>stencil</string>
+ <string>uniform</string>
+ <string>add</string>
+ <real>0.0</real>
+ <real>0.5</real>
+ </array>
+ <array>
+ <string>screen</string>
+ <string>line</string>
+ <real>0.02</real>
+ <real>0.0</real>
+ </array>
+ <array>
+ <string>gamma</string>
+ <real>0.25</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ <real>1.0</real>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ <array>
+ <string>blur</string>
+ </array>
+ </array>
+</llsd>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 8d3a7327b9..7dde3ec84b 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11495,6 +11495,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>SnapshotFiltersEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable filters in the Snapshot Advanced panel (experimental).</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>SnapshotFormat</key>
<map>
<key>Comment</key>
@@ -13313,7 +13324,7 @@
<key>SocialPhotoResolution</key>
<map>
<key>Comment</key>
- <string>Default resolution when sharing photo using the social floater</string>
+ <string>Default resolution when sharing photo using the social floaters</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
diff --git a/indra/newview/app_settings/toolbars.xml b/indra/newview/app_settings/toolbars.xml
index 86f9912815..d61aee9a14 100755
--- a/indra/newview/app_settings/toolbars.xml
+++ b/indra/newview/app_settings/toolbars.xml
@@ -6,7 +6,6 @@
<command name="speak"/>
<command name="destinations"/>
<command name="people"/>
- <command name="social"/>
<command name="profile"/>
<command name="move"/>
<command name="view"/>
@@ -22,5 +21,6 @@
<command name="voice"/>
<command name="minimap"/>
<command name="snapshot"/>
+ <command name="facebook"/>
</left_toolbar>
</toolbars>
diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp
index e2fa117453..28319564e4 100755
--- a/indra/newview/llfacebookconnect.cpp
+++ b/indra/newview/llfacebookconnect.cpp
@@ -28,6 +28,8 @@
#include "llviewerprecompiledheaders.h"
#include "llfacebookconnect.h"
+#include "llflickrconnect.h"
+#include "lltwitterconnect.h"
#include "llagent.h"
#include "llcallingcard.h" // for LLAvatarTracker
@@ -58,7 +60,7 @@ void log_facebook_connect_error(const std::string& request, U32 status, const st
}
}
-void toast_user_for_success()
+void toast_user_for_facebook_success()
{
LLSD args;
args["MESSAGE"] = LLTrans::getString("facebook_post_success");
@@ -74,23 +76,46 @@ public:
bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
{
- if (tokens.size() > 0)
+ if (tokens.size() >= 1)
{
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)
+ if (tokens.size() >= 2 && tokens[1].asString() == "flickr")
{
- fbc_web->closeFloater();
+ // this command probably came from the flickr_web browser, so close it
+ LLFloaterReg::hideInstance("flickr_web");
+
+ // connect to flickr
+ if (query_map.has("oauth_token"))
+ {
+ LLFlickrConnect::instance().connectToFlickr(query_map["oauth_token"], query_map.get("oauth_verifier"));
+ }
+ return true;
}
-
- // connect to facebook
- if (query_map.has("code"))
+ else if (tokens.size() >= 2 && tokens[1].asString() == "twitter")
+ {
+ // this command probably came from the twitter_web browser, so close it
+ LLFloaterReg::hideInstance("twitter_web");
+
+ // connect to twitter
+ if (query_map.has("oauth_token"))
+ {
+ LLTwitterConnect::instance().connectToTwitter(query_map["oauth_token"], query_map.get("oauth_verifier"));
+ }
+ return true;
+ }
+ else //if (tokens.size() >= 2 && tokens[1].asString() == "facebook")
{
- LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state"));
+ // this command probably came from the fbc_web browser, so close it
+ LLFloaterReg::hideInstance("fbc_web");
+
+ // connect to facebook
+ if (query_map.has("code"))
+ {
+ LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state"));
+ }
+ return true;
}
- return true;
}
}
return false;
@@ -156,7 +181,7 @@ public:
/* virtual */ void httpSuccess()
{
- toast_user_for_success();
+ toast_user_for_facebook_success();
LL_DEBUGS("FacebookConnect") << "Post successful. " << dumpResponse() << LL_ENDL;
LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED);
}
@@ -302,9 +327,16 @@ public:
{
if ( HTTP_FOUND == getStatus() )
{
- LL_INFOS() << "Facebook: Info received" << LL_ENDL;
- LL_DEBUGS("FacebookConnect") << "Getting Facebook info successful. info: " << getContent() << LL_ENDL;
- LLFacebookConnect::instance().storeInfo(getContent());
+ const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
+ if (location.empty())
+ {
+ LL_WARNS("FacebookConnect") << "Missing Location header " << dumpResponse()
+ << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
+ }
+ else
+ {
+ LLFacebookConnect::instance().openFacebookWeb(location);
+ }
}
else
{
@@ -371,10 +403,12 @@ 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);
+ p.url(url);
+ p.show_chrome(true);
+ p.allow_address_entry(false);
+ p.allow_back_forward_navigation(false);
+ p.trusted_content(true);
+ p.clean_browser(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
@@ -391,7 +425,8 @@ std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route, b
LLViewerRegion *regionp = gAgent.getRegion();
if (regionp)
{
- url = regionp->getCapability("FacebookConnect");
+ //url = "http://pdp15.lindenlab.com/fbc/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO
+ url = regionp->getCapability("FacebookConnect");
url += route;
if (include_read_from_master && mReadFromMaster)
@@ -406,9 +441,13 @@ void LLFacebookConnect::connectToFacebook(const std::string& auth_code, const st
{
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());
}
@@ -452,15 +491,25 @@ void LLFacebookConnect::postCheckin(const std::string& location, const std::stri
{
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());
@@ -507,7 +556,7 @@ void LLFacebookConnect::sharePhoto(LLPointer<LLImageFormatted> image, const std:
<< caption << "\r\n";
body << "--" << boundary << "\r\n"
- << "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << imageFormat << "\"\r\n"
+ << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << imageFormat << "\"\r\n"
<< "Content-Type: image/" << imageFormat << "\r\n\r\n";
// Insert the image data.
@@ -599,12 +648,13 @@ void LLFacebookConnect::setConnectionState(LLFacebookConnect::EConnectionState c
if (mConnectionState != connection_state)
{
+ // set the connection state before notifying watchers
+ mConnectionState = connection_state;
+
LLSD state_info;
state_info["enum"] = connection_state;
sStateWatcher->post(state_info);
}
-
- mConnectionState = connection_state;
}
void LLFacebookConnect::setConnected(bool connected)
diff --git a/indra/newview/llfacebookconnect.h b/indra/newview/llfacebookconnect.h
index a77ac24167..c157db2178 100644
--- a/indra/newview/llfacebookconnect.h
+++ b/indra/newview/llfacebookconnect.h
@@ -89,7 +89,7 @@ private:
LLFacebookConnect();
~LLFacebookConnect() {};
std::string getFacebookConnectURL(const std::string& route = "", bool include_read_from_master = false);
-
+
EConnectionState mConnectionState;
BOOL mConnected;
LLSD mInfo;
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index a7f5cd9dac..5debf71744 100755
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -167,7 +167,8 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
BOOL res = TRUE;
switch (filter)
{
- case FFLOAD_ALL:
+ case FFLOAD_ALL:
+ case FFLOAD_EXE:
mOFN.lpstrFilter = L"All Files (*.*)\0*.*\0" \
SOUND_FILTER \
IMAGE_FILTER \
@@ -580,6 +581,10 @@ std::vector<std::string>* LLFilePicker::navOpenFilterProc(ELoadFilter filter) //
allowedv->push_back("tpic");
allowedv->push_back("png");
break;
+ case FFLOAD_EXE:
+ allowedv->push_back("app");
+ allowedv->push_back("exe");
+ break;
case FFLOAD_WAV:
allowedv->push_back("wav");
break;
@@ -778,7 +783,7 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
mPickOptions &= ~F_FILE;
}
- if(filter == FFLOAD_ALL) // allow application bundles etc. to be traversed; important for DEV-16869, but generally useful
+ if (filter == FFLOAD_ALL) // allow application bundles etc. to be traversed; important for DEV-16869, but generally useful
{
mPickOptions |= F_NAV_SUPPORT;
}
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index f6a700d1c6..0e0cec3943 100755
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -87,7 +87,8 @@ public:
FFLOAD_COLLADA = 10,
FFLOAD_SCRIPT = 11,
FFLOAD_DICTIONARY = 12,
- FFLOAD_DIRECTORY = 13 //To call from lldirpicker.
+ FFLOAD_DIRECTORY = 13, // To call from lldirpicker.
+ FFLOAD_EXE = 14 // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin
};
enum ESaveFilter
diff --git a/indra/newview/llflickrconnect.cpp b/indra/newview/llflickrconnect.cpp
new file mode 100644
index 0000000000..b715896264
--- /dev/null
+++ b/indra/newview/llflickrconnect.cpp
@@ -0,0 +1,509 @@
+/**
+ * @file llflickrconnect.h
+ * @author Merov, Cho
+ * @brief Connection to Flickr 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 "llflickrconnect.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> LLFlickrConnect::sStateWatcher(new LLEventStream("FlickrConnectState"));
+boost::scoped_ptr<LLEventPump> LLFlickrConnect::sInfoWatcher(new LLEventStream("FlickrConnectInfo"));
+boost::scoped_ptr<LLEventPump> LLFlickrConnect::sContentWatcher(new LLEventStream("FlickrConnectContent"));
+
+// Local functions
+void log_flickr_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("FlickrConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL;
+ }
+}
+
+void toast_user_for_flickr_success()
+{
+ LLSD args;
+ args["MESSAGE"] = LLTrans::getString("flickr_post_success");
+ LLNotificationsUtil::add("FlickrConnect", args);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrConnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrConnectResponder);
+public:
+
+ LLFlickrConnectResponder()
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS);
+ }
+
+ /* virtual */ void httpSuccess()
+ {
+ LL_DEBUGS("FlickrConnect") << "Connect successful. " << dumpResponse() << LL_ENDL;
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED);
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ if ( HTTP_FOUND == getStatus() )
+ {
+ const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
+ if (location.empty())
+ {
+ LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse()
+ << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
+ }
+ else
+ {
+ LLFlickrConnect::instance().openFlickrWeb(location);
+ }
+ }
+ else
+ {
+ LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
+ const LLSD& content = getContent();
+ log_flickr_connect_error("Connect", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrShareResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrShareResponder);
+public:
+
+ LLFlickrShareResponder()
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTING);
+ }
+
+ /* virtual */ void httpSuccess()
+ {
+ toast_user_for_flickr_success();
+ LL_DEBUGS("FlickrConnect") << "Post successful. " << dumpResponse() << LL_ENDL;
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTED);
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ if ( HTTP_FOUND == getStatus() )
+ {
+ const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
+ if (location.empty())
+ {
+ LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse()
+ << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
+ }
+ else
+ {
+ LLFlickrConnect::instance().openFlickrWeb(location);
+ }
+ }
+ else if ( HTTP_NOT_FOUND == getStatus() )
+ {
+ LLFlickrConnect::instance().connectToFlickr();
+ }
+ else
+ {
+ LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POST_FAILED);
+ const LLSD& content = getContent();
+ log_flickr_connect_error("Share", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrDisconnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrDisconnectResponder);
+public:
+
+ LLFlickrDisconnectResponder()
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECTING);
+ }
+
+ void setUserDisconnected()
+ {
+ // Clear data
+ LLFlickrConnect::instance().clearInfo();
+
+ //Notify state change
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED);
+ }
+
+ /* virtual */ void httpSuccess()
+ {
+ LL_DEBUGS("FlickrConnect") << "Disconnect successful. " << dumpResponse() << LL_ENDL;
+ setUserDisconnected();
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ //User not found so already disconnected
+ if ( HTTP_NOT_FOUND == getStatus() )
+ {
+ LL_DEBUGS("FlickrConnect") << "Already disconnected. " << dumpResponse() << LL_ENDL;
+ setUserDisconnected();
+ }
+ else
+ {
+ LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECT_FAILED);
+ const LLSD& content = getContent();
+ log_flickr_connect_error("Disconnect", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrConnectedResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrConnectedResponder);
+public:
+
+ LLFlickrConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect)
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS);
+ }
+
+ /* virtual */ void httpSuccess()
+ {
+ LL_DEBUGS("FlickrConnect") << "Connect successful. " << dumpResponse() << LL_ENDL;
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED);
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ // show the facebook login page if not connected yet
+ if ( HTTP_NOT_FOUND == getStatus() )
+ {
+ LL_DEBUGS("FlickrConnect") << "Not connected. " << dumpResponse() << LL_ENDL;
+ if (mAutoConnect)
+ {
+ LLFlickrConnect::instance().connectToFlickr();
+ }
+ else
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED);
+ }
+ }
+ else
+ {
+ LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
+ const LLSD& content = getContent();
+ log_flickr_connect_error("Connected", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+private:
+ bool mAutoConnect;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrInfoResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrInfoResponder);
+public:
+
+ /* virtual */ void httpSuccess()
+ {
+ LL_INFOS("FlickrConnect") << "Flickr: Info received" << LL_ENDL;
+ LL_DEBUGS("FlickrConnect") << "Getting Flickr info successful. " << dumpResponse() << LL_ENDL;
+ LLFlickrConnect::instance().storeInfo(getContent());
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ if ( HTTP_FOUND == getStatus() )
+ {
+ const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
+ if (location.empty())
+ {
+ LL_WARNS("FlickrConnect") << "Missing Location header " << dumpResponse()
+ << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
+ }
+ else
+ {
+ LLFlickrConnect::instance().openFlickrWeb(location);
+ }
+ }
+ else
+ {
+ LL_WARNS("FlickrConnect") << dumpResponse() << LL_ENDL;
+ const LLSD& content = getContent();
+ log_flickr_connect_error("Info", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLFlickrConnect::LLFlickrConnect()
+: mConnectionState(FLICKR_NOT_CONNECTED),
+ mConnected(false),
+ mInfo(),
+ mRefreshInfo(false),
+ mReadFromMaster(false)
+{
+}
+
+void LLFlickrConnect::openFlickrWeb(std::string url)
+{
+ // Open the URL in an internal browser window without navigation UI
+ LLFloaterWebContent::Params p;
+ p.url(url);
+ p.show_chrome(true);
+ p.allow_address_entry(false);
+ p.allow_back_forward_navigation(false);
+ p.trusted_content(true);
+ p.clean_browser(true);
+ LLFloater *floater = LLFloaterReg::showInstance("flickr_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 "flickr_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.
+ //flickr_web floater contains the "webbrowser" panel. JIRA: ACME-744
+ gFocusMgr.setKeyboardFocus( floater );
+
+ //LLUrlAction::openURLExternal(url);
+}
+
+std::string LLFlickrConnect::getFlickrConnectURL(const std::string& route, bool include_read_from_master)
+{
+ std::string url("");
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ //url = "http://pdp15.lindenlab.com/flickr/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO
+ url = regionp->getCapability("FlickrConnect");
+ url += route;
+
+ if (include_read_from_master && mReadFromMaster)
+ {
+ url += "?read_from_master=true";
+ }
+ }
+ return url;
+}
+
+void LLFlickrConnect::connectToFlickr(const std::string& request_token, const std::string& oauth_verifier)
+{
+ LLSD body;
+ if (!request_token.empty())
+ body["request_token"] = request_token;
+ if (!oauth_verifier.empty())
+ body["oauth_verifier"] = oauth_verifier;
+
+ LLHTTPClient::put(getFlickrConnectURL("/connection"), body, new LLFlickrConnectResponder());
+}
+
+void LLFlickrConnect::disconnectFromFlickr()
+{
+ LLHTTPClient::del(getFlickrConnectURL("/connection"), new LLFlickrDisconnectResponder());
+}
+
+void LLFlickrConnect::checkConnectionToFlickr(bool auto_connect)
+{
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFlickrConnectURL("/connection", true), new LLFlickrConnectedResponder(auto_connect),
+ LLSD(), timeout, follow_redirects);
+}
+
+void LLFlickrConnect::loadFlickrInfo()
+{
+ if(mRefreshInfo)
+ {
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFlickrConnectURL("/info", true), new LLFlickrInfoResponder(),
+ LLSD(), timeout, follow_redirects);
+ }
+}
+
+void LLFlickrConnect::uploadPhoto(const std::string& image_url, const std::string& title, const std::string& description, const std::string& tags, int safety_level)
+{
+ LLSD body;
+ body["image"] = image_url;
+ body["title"] = title;
+ body["description"] = description;
+ body["tags"] = tags;
+ body["safety_level"] = safety_level;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getFlickrConnectURL("/share/photo", true), body, new LLFlickrShareResponder());
+}
+
+void LLFlickrConnect::uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& title, const std::string& description, const std::string& tags, int safety_level)
+{
+ 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=\"title\"\r\n\r\n"
+ << title << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"description\"\r\n\r\n"
+ << description << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"tags\"\r\n\r\n"
+ << tags << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"safety_level\"\r\n\r\n"
+ << safety_level << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << 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(getFlickrConnectURL("/share/photo", true), data, size, new LLFlickrShareResponder(), headers);
+}
+
+void LLFlickrConnect::storeInfo(const LLSD& info)
+{
+ mInfo = info;
+ mRefreshInfo = false;
+
+ sInfoWatcher->post(info);
+}
+
+const LLSD& LLFlickrConnect::getInfo() const
+{
+ return mInfo;
+}
+
+void LLFlickrConnect::clearInfo()
+{
+ mInfo = LLSD();
+}
+
+void LLFlickrConnect::setDataDirty()
+{
+ mRefreshInfo = true;
+}
+
+void LLFlickrConnect::setConnectionState(LLFlickrConnect::EConnectionState connection_state)
+{
+ if(connection_state == FLICKR_CONNECTED)
+ {
+ mReadFromMaster = true;
+ setConnected(true);
+ setDataDirty();
+ }
+ else if(connection_state == FLICKR_NOT_CONNECTED)
+ {
+ setConnected(false);
+ }
+ else if(connection_state == FLICKR_POSTED)
+ {
+ mReadFromMaster = false;
+ }
+
+ if (mConnectionState != connection_state)
+ {
+ // set the connection state before notifying watchers
+ mConnectionState = connection_state;
+
+ LLSD state_info;
+ state_info["enum"] = connection_state;
+ sStateWatcher->post(state_info);
+ }
+}
+
+void LLFlickrConnect::setConnected(bool connected)
+{
+ mConnected = connected;
+}
diff --git a/indra/newview/llflickrconnect.h b/indra/newview/llflickrconnect.h
new file mode 100644
index 0000000000..b127e6e104
--- /dev/null
+++ b/indra/newview/llflickrconnect.h
@@ -0,0 +1,98 @@
+/**
+ * @file llflickrconnect.h
+ * @author Merov, Cho
+ * @brief Connection to Flickr 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_LLFLICKRCONNECT_H
+#define LL_LLFLICKRCONNECT_H
+
+#include "llsingleton.h"
+#include "llimage.h"
+
+class LLEventPump;
+
+/**
+ * @class LLFlickrConnect
+ *
+ * Manages authentication to, and interaction with, a web service allowing the
+ * the viewer to upload photos to Flickr.
+ */
+class LLFlickrConnect : public LLSingleton<LLFlickrConnect>
+{
+ LOG_CLASS(LLFlickrConnect);
+public:
+ enum EConnectionState
+ {
+ FLICKR_NOT_CONNECTED = 0,
+ FLICKR_CONNECTION_IN_PROGRESS = 1,
+ FLICKR_CONNECTED = 2,
+ FLICKR_CONNECTION_FAILED = 3,
+ FLICKR_POSTING = 4,
+ FLICKR_POSTED = 5,
+ FLICKR_POST_FAILED = 6,
+ FLICKR_DISCONNECTING = 7,
+ FLICKR_DISCONNECT_FAILED = 8
+ };
+
+ void connectToFlickr(const std::string& request_token = "", const std::string& oauth_verifier = ""); // Initiate the complete Flickr connection. Please use checkConnectionToFlickr() in normal use.
+ void disconnectFromFlickr(); // Disconnect from the Flickr service.
+ void checkConnectionToFlickr(bool auto_connect = false); // Check if an access token is available on the Flickr service. If not, call connectToFlickr().
+
+ void loadFlickrInfo();
+ void uploadPhoto(const std::string& image_url, const std::string& title, const std::string& description, const std::string& tags, int safety_level);
+ void uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& title, const std::string& description, const std::string& tags, int safety_level);
+
+ void storeInfo(const LLSD& info);
+ const LLSD& getInfo() const;
+ void clearInfo();
+ void setDataDirty();
+
+ void setConnectionState(EConnectionState connection_state);
+ void setConnected(bool connected);
+ bool isConnected() { return mConnected; }
+ bool isTransactionOngoing() { return ((mConnectionState == FLICKR_CONNECTION_IN_PROGRESS) || (mConnectionState == FLICKR_POSTING) || (mConnectionState == FLICKR_DISCONNECTING)); }
+ EConnectionState getConnectionState() { return mConnectionState; }
+
+ void openFlickrWeb(std::string url);
+
+private:
+ friend class LLSingleton<LLFlickrConnect>;
+
+ LLFlickrConnect();
+ ~LLFlickrConnect() {};
+ std::string getFlickrConnectURL(const std::string& route = "", bool include_read_from_master = false);
+
+ EConnectionState mConnectionState;
+ BOOL mConnected;
+ LLSD mInfo;
+ bool mRefreshInfo;
+ bool mReadFromMaster;
+
+ static boost::scoped_ptr<LLEventPump> sStateWatcher;
+ static boost::scoped_ptr<LLEventPump> sInfoWatcher;
+ static boost::scoped_ptr<LLEventPump> sContentWatcher;
+};
+
+#endif // LL_LLFLICKRCONNECT_H
diff --git a/indra/newview/llfloaterbigpreview.cpp b/indra/newview/llfloaterbigpreview.cpp
new file mode 100644
index 0000000000..b516e9dd01
--- /dev/null
+++ b/indra/newview/llfloaterbigpreview.cpp
@@ -0,0 +1,110 @@
+/**
+* @file llfloaterbigpreview.cpp
+* @brief Display of extended (big) preview for snapshots and SL Share
+* @author merov@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 "llfloaterbigpreview.h"
+#include "llsnapshotlivepreview.h"
+
+///////////////////////
+//LLFloaterBigPreview//
+///////////////////////
+
+LLFloaterBigPreview::LLFloaterBigPreview(const LLSD& key) : LLFloater(key),
+ mPreviewPlaceholder(NULL),
+ mFloaterOwner(NULL)
+{
+}
+
+LLFloaterBigPreview::~LLFloaterBigPreview()
+{
+ if (mPreviewHandle.get())
+ {
+ mPreviewHandle.get()->die();
+ }
+}
+
+void LLFloaterBigPreview::onCancel()
+{
+ closeFloater();
+}
+
+void LLFloaterBigPreview::closeOnFloaterOwnerClosing(LLFloater* floaterp)
+{
+ if (isFloaterOwner(floaterp))
+ {
+ closeFloater();
+ }
+}
+
+BOOL LLFloaterBigPreview::postBuild()
+{
+ mPreviewPlaceholder = getChild<LLUICtrl>("big_preview_placeholder");
+ return LLFloater::postBuild();
+}
+
+void LLFloaterBigPreview::draw()
+{
+ LLFloater::draw();
+
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+
+ // Display the preview if one is available
+ if (previewp && previewp->getBigThumbnailImage())
+ {
+ // Get the preview rect
+ const LLRect& preview_rect = mPreviewPlaceholder->getRect();
+
+ // Get the preview texture size
+ S32 thumbnail_w = previewp->getBigThumbnailWidth();
+ S32 thumbnail_h = previewp->getBigThumbnailHeight();
+
+ // Compute the scaling ratio and the size of the final texture in the rect: we want to prevent anisotropic scaling (distorted in x and y)
+ F32 ratio = llmax((F32)(thumbnail_w)/(F32)(preview_rect.getWidth()), (F32)(thumbnail_h)/(F32)(preview_rect.getHeight()));
+ thumbnail_w = (S32)((F32)(thumbnail_w)/ratio);
+ thumbnail_h = (S32)((F32)(thumbnail_h)/ratio);
+
+ // Compute the preview offset within the preview rect: we want to center that preview in the available rect
+ const S32 local_offset_x = (preview_rect.getWidth() - thumbnail_w) / 2 ;
+ const S32 local_offset_y = (preview_rect.getHeight() - thumbnail_h) / 2 ;
+
+ // Compute preview offset within the floater rect
+ S32 offset_x = preview_rect.mLeft + local_offset_x;
+ S32 offset_y = preview_rect.mBottom + local_offset_y;
+
+ 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;
+
+ // Draw the preview texture
+ gl_draw_scaled_image(offset_x, offset_y,
+ thumbnail_w, thumbnail_h,
+ previewp->getBigThumbnailImage(), color % alpha);
+ }
+}
+
diff --git a/indra/newview/llfloaterbigpreview.h b/indra/newview/llfloaterbigpreview.h
new file mode 100644
index 0000000000..63c6784d36
--- /dev/null
+++ b/indra/newview/llfloaterbigpreview.h
@@ -0,0 +1,54 @@
+/**
+* @file llfloaterbigpreview.h
+* @brief Display of extended (big) preview for snapshots and SL Share
+* @author merov@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_LLFLOATERBIGPREVIEW_H
+#define LL_LLFLOATERBIGPREVIEW_H
+
+#include "llfloater.h"
+
+class LLFloaterBigPreview : public LLFloater
+{
+public:
+ LLFloaterBigPreview(const LLSD& key);
+ ~LLFloaterBigPreview();
+
+ BOOL postBuild();
+ void draw();
+ void onCancel();
+
+ void setPreview(LLView* previewp) { mPreviewHandle = previewp->getHandle(); }
+ void setFloaterOwner(LLFloater* floaterp) { mFloaterOwner = floaterp; }
+ bool isFloaterOwner(LLFloater* floaterp) const { return (mFloaterOwner == floaterp); }
+ void closeOnFloaterOwnerClosing(LLFloater* floaterp);
+
+private:
+ LLHandle<LLView> mPreviewHandle;
+ LLUICtrl* mPreviewPlaceholder;
+ LLFloater* mFloaterOwner;
+};
+
+#endif // LL_LLFLOATERBIGPREVIEW_H
+
diff --git a/indra/newview/llfloatersocial.cpp b/indra/newview/llfloaterfacebook.cpp
index ea3d72e116..9e3f917eae 100644
--- a/indra/newview/llfloatersocial.cpp
+++ b/indra/newview/llfloaterfacebook.cpp
@@ -1,6 +1,6 @@
/**
-* @file llfloatersocial.cpp
-* @brief Implementation of llfloatersocial
+* @file llfloaterfacebook.cpp
+* @brief Implementation of llfloaterfacebook
* @author Gilbert@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
@@ -27,15 +27,17 @@
#include "llviewerprecompiledheaders.h"
-#include "llfloatersocial.h"
+#include "llfloaterfacebook.h"
#include "llagent.h"
#include "llagentui.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llfacebookconnect.h"
+#include "llfloaterbigpreview.h"
#include "llfloaterreg.h"
#include "lliconctrl.h"
+#include "llimagefiltersmanager.h"
#include "llresmgr.h" // LLLocale
#include "llsdserialize.h"
#include "llloadingindicator.h"
@@ -46,11 +48,17 @@
#include "llviewerregion.h"
#include "llviewercontrol.h"
#include "llviewermedia.h"
-
-static LLPanelInjector<LLSocialStatusPanel> t_panel_status("llsocialstatuspanel");
-static LLPanelInjector<LLSocialPhotoPanel> t_panel_photo("llsocialphotopanel");
-static LLPanelInjector<LLSocialCheckinPanel> t_panel_checkin("llsocialcheckinpanel");
-static LLPanelInjector<LLSocialAccountPanel> t_panel_account("llsocialaccountpanel");
+#include "lltabcontainer.h"
+#include "llavatarlist.h"
+#include "llpanelpeoplemenus.h"
+#include "llaccordionctrl.h"
+#include "llaccordionctrltab.h"
+
+static LLPanelInjector<LLFacebookStatusPanel> t_panel_status("llfacebookstatuspanel");
+static LLPanelInjector<LLFacebookPhotoPanel> t_panel_photo("llfacebookphotopanel");
+static LLPanelInjector<LLFacebookCheckinPanel> t_panel_checkin("llfacebookcheckinpanel");
+static LLPanelInjector<LLFacebookFriendsPanel> t_panel_friends("llfacebookfriendspanel");
+static LLPanelInjector<LLFacebookAccountPanel> t_panel_account("llfacebookaccountpanel");
const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
const std::string DEFAULT_CHECKIN_LOCATION_URL = "http://maps.secondlife.com/";
@@ -58,12 +66,17 @@ const std::string DEFAULT_CHECKIN_ICON_URL = "http://map.secondlife.com.s3.amazo
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";
+const S32 MAX_QUALITY = 100; // Max quality value for jpeg images
+const S32 MIN_QUALITY = 0; // Min quality value for jpeg images
+const S32 TARGET_DATA_SIZE = 95000; // Size of the image (compressed) we're trying to send to Facebook
+
std::string get_map_url()
{
LLVector3d center_agent;
- if (gAgent.getRegion())
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
{
- center_agent = gAgent.getRegion()->getCenterGlobal();
+ center_agent = regionp->getCenterGlobal();
}
int x_pos = center_agent[0] / 256.0;
int y_pos = center_agent[1] / 256.0;
@@ -71,19 +84,27 @@ std::string get_map_url()
return map_url;
}
+// Compute target jpeg quality : see https://wiki.lindenlab.com/wiki/Facebook_Image_Quality for details
+S32 compute_jpeg_quality(S32 width, S32 height)
+{
+ F32 target_compression_ratio = (F32)(width * height * 3) / (F32)(TARGET_DATA_SIZE);
+ S32 quality = (S32)(110.0f - (2.0f * target_compression_ratio));
+ return llclamp(quality,MIN_QUALITY,MAX_QUALITY);
+}
+
///////////////////////////
-//LLSocialStatusPanel//////
+//LLFacebookStatusPanel//////
///////////////////////////
-LLSocialStatusPanel::LLSocialStatusPanel() :
+LLFacebookStatusPanel::LLFacebookStatusPanel() :
mMessageTextEditor(NULL),
mPostButton(NULL),
mCancelButton(NULL)
{
- mCommitCallbackRegistrar.add("SocialSharing.SendStatus", boost::bind(&LLSocialStatusPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.SendStatus", boost::bind(&LLFacebookStatusPanel::onSend, this));
}
-BOOL LLSocialStatusPanel::postBuild()
+BOOL LLFacebookStatusPanel::postBuild()
{
mMessageTextEditor = getChild<LLUICtrl>("status_message");
mPostButton = getChild<LLUICtrl>("post_status_btn");
@@ -92,7 +113,7 @@ BOOL LLSocialStatusPanel::postBuild()
return LLPanel::postBuild();
}
-void LLSocialStatusPanel::draw()
+void LLFacebookStatusPanel::draw()
{
if (mMessageTextEditor && mPostButton && mCancelButton)
{
@@ -106,10 +127,10 @@ void LLSocialStatusPanel::draw()
LLPanel::draw();
}
-void LLSocialStatusPanel::onSend()
+void LLFacebookStatusPanel::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));
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookStatusPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookStatusPanel", boost::bind(&LLFacebookStatusPanel::onFacebookConnectStateChange, this, _1));
// Connect to Facebook if necessary and then post
if (LLFacebookConnect::instance().isConnected())
@@ -122,7 +143,7 @@ void LLSocialStatusPanel::onSend()
}
}
-bool LLSocialStatusPanel::onFacebookConnectStateChange(const LLSD& data)
+bool LLFacebookStatusPanel::onFacebookConnectStateChange(const LLSD& data)
{
switch (data.get("enum").asInteger())
{
@@ -131,7 +152,7 @@ bool LLSocialStatusPanel::onFacebookConnectStateChange(const LLSD& data)
break;
case LLFacebookConnect::FB_POSTED:
- LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialStatusPanel");
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookStatusPanel");
clearAndClose();
break;
}
@@ -139,7 +160,7 @@ bool LLSocialStatusPanel::onFacebookConnectStateChange(const LLSD& data)
return false;
}
-void LLSocialStatusPanel::sendStatus()
+void LLFacebookStatusPanel::sendStatus()
{
std::string message = mMessageTextEditor->getValue().asString();
if (!message.empty())
@@ -148,7 +169,7 @@ void LLSocialStatusPanel::sendStatus()
}
}
-void LLSocialStatusPanel::clearAndClose()
+void LLFacebookStatusPanel::clearAndClose()
{
mMessageTextEditor->setValue("");
@@ -160,23 +181,27 @@ void LLSocialStatusPanel::clearAndClose()
}
///////////////////////////
-//LLSocialPhotoPanel///////
+//LLFacebookPhotoPanel///////
///////////////////////////
-LLSocialPhotoPanel::LLSocialPhotoPanel() :
+LLFacebookPhotoPanel::LLFacebookPhotoPanel() :
mSnapshotPanel(NULL),
mResolutionComboBox(NULL),
mRefreshBtn(NULL),
+mBtnPreview(NULL),
mWorkingLabel(NULL),
mThumbnailPlaceholder(NULL),
mCaptionTextBox(NULL),
-mPostButton(NULL)
+mPostButton(NULL),
+mBigPreviewFloater(NULL),
+mQuality(MAX_QUALITY)
{
- mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLSocialPhotoPanel::onSend, this));
- mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLSocialPhotoPanel::onClickNewSnapshot, this));
+ mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLFacebookPhotoPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLFacebookPhotoPanel::onClickNewSnapshot, this));
+ mCommitCallbackRegistrar.add("SocialSharing.BigPreview", boost::bind(&LLFacebookPhotoPanel::onClickBigPreview, this));
}
-LLSocialPhotoPanel::~LLSocialPhotoPanel()
+LLFacebookPhotoPanel::~LLFacebookPhotoPanel()
{
if(mPreviewHandle.get())
{
@@ -184,24 +209,65 @@ LLSocialPhotoPanel::~LLSocialPhotoPanel()
}
}
-BOOL LLSocialPhotoPanel::postBuild()
+BOOL LLFacebookPhotoPanel::postBuild()
{
- setVisibleCallback(boost::bind(&LLSocialPhotoPanel::onVisibilityChanged, this, _2));
+ setVisibleCallback(boost::bind(&LLFacebookPhotoPanel::onVisibilityChange, this, _2));
mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
- mResolutionComboBox->setCommitCallback(boost::bind(&LLSocialPhotoPanel::updateResolution, this, TRUE));
+ mResolutionComboBox->setValue("[i1200,i630]"); // hardcoded defaults ftw!
+ mResolutionComboBox->setCommitCallback(boost::bind(&LLFacebookPhotoPanel::updateResolution, this, TRUE));
+ mFilterComboBox = getChild<LLUICtrl>("filters_combobox");
+ mFilterComboBox->setCommitCallback(boost::bind(&LLFacebookPhotoPanel::updateResolution, this, TRUE));
mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
+ mBtnPreview = getChild<LLButton>("big_preview_btn");
mWorkingLabel = getChild<LLUICtrl>("working_lbl");
mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
mCaptionTextBox = getChild<LLUICtrl>("photo_caption");
mPostButton = getChild<LLUICtrl>("post_photo_btn");
mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
+ mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+
+ // Update filter list
+ std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
+ LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
+ for (U32 i = 0; i < filter_list.size(); i++)
+ {
+ filterbox->add(filter_list[i]);
+ }
return LLPanel::postBuild();
}
-void LLSocialPhotoPanel::draw()
+// virtual
+S32 LLFacebookPhotoPanel::notify(const LLSD& info)
+{
+ if (info.has("snapshot-updating"))
+ {
+ // Disable the Post button and whatever else while the snapshot is not updated
+ // updateControls();
+ return 1;
+ }
+
+ if (info.has("snapshot-updated"))
+ {
+ // Enable the send/post/save buttons.
+ updateControls();
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. after snapshot is taken
+ LLUICtrl * refresh_button = getRefreshBtn();
+ if (!refresh_button->getVisible())
+ {
+ refresh_button->setVisible(true);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+void LLFacebookPhotoPanel::draw()
{
LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
@@ -210,9 +276,21 @@ void LLSocialPhotoPanel::draw()
mCancelButton->setEnabled(no_ongoing_connection);
mCaptionTextBox->setEnabled(no_ongoing_connection);
mResolutionComboBox->setEnabled(no_ongoing_connection);
+ mFilterComboBox->setEnabled(no_ongoing_connection);
mRefreshBtn->setEnabled(no_ongoing_connection);
+ mBtnPreview->setEnabled(no_ongoing_connection);
+
+ // Reassign the preview floater if we have the focus and the preview exists
+ if (hasFocus() && isPreviewVisible())
+ {
+ attachPreview();
+ }
+
+ // Toggle the button state as appropriate
+ bool preview_active = (isPreviewVisible() && mBigPreviewFloater->isFloaterOwner(getParentByType<LLFloater>()));
+ mBtnPreview->setToggleState(preview_active);
- // Display the preview if one is available
+ // Display the thumbnail if one is available
if (previewp && previewp->getThumbnailImage())
{
const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
@@ -239,8 +317,6 @@ void LLSocialPhotoPanel::draw()
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
@@ -253,15 +329,14 @@ void LLSocialPhotoPanel::draw()
LLPanel::draw();
}
-LLSnapshotLivePreview* LLSocialPhotoPanel::getPreviewView()
+LLSnapshotLivePreview* LLFacebookPhotoPanel::getPreviewView()
{
LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
return previewp;
}
-void LLSocialPhotoPanel::onVisibilityChanged(const LLSD& new_visibility)
+void LLFacebookPhotoPanel::onVisibilityChange(BOOL visible)
{
- bool visible = new_visibility.asBoolean();
if (visible)
{
if (mPreviewHandle.get())
@@ -269,7 +344,7 @@ void LLSocialPhotoPanel::onVisibilityChanged(const LLSD& new_visibility)
LLSnapshotLivePreview* preview = getPreviewView();
if(preview)
{
- LL_DEBUGS() << "opened, updating snapshot" << LL_ENDL;
+ lldebugs << "opened, updating snapshot" << llendl;
preview->updateSnapshot(TRUE);
}
}
@@ -280,10 +355,15 @@ void LLSocialPhotoPanel::onVisibilityChanged(const LLSD& new_visibility)
p.rect(full_screen_rect);
LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
mPreviewHandle = previewp->getHandle();
+ mQuality = MAX_QUALITY;
+ previewp->setContainer(this);
previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
- //previewp->setSnapshotQuality(98);
+ previewp->setSnapshotQuality(mQuality, false);
+ previewp->setThumbnailSubsampled(TRUE); // We want the preview to reflect the *saved* image
+ previewp->setAllowRenderUI(FALSE); // We do not want the rendered UI in our snapshots
+ previewp->setAllowFullScreenPreview(FALSE); // No full screen preview in SL Share mode
previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
updateControls();
@@ -291,21 +371,48 @@ void LLSocialPhotoPanel::onVisibilityChanged(const LLSD& new_visibility)
}
}
-void LLSocialPhotoPanel::onClickNewSnapshot()
+void LLFacebookPhotoPanel::onClickNewSnapshot()
{
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp)
{
- //setStatus(Impl::STATUS_READY);
- LL_DEBUGS() << "updating snapshot" << LL_ENDL;
previewp->updateSnapshot(TRUE);
}
}
-void LLSocialPhotoPanel::onSend()
+void LLFacebookPhotoPanel::onClickBigPreview()
+{
+ // Toggle the preview
+ if (isPreviewVisible())
+ {
+ LLFloaterReg::hideInstance("big_preview");
+ }
+ else
+ {
+ attachPreview();
+ LLFloaterReg::showInstance("big_preview");
+ }
+}
+
+bool LLFacebookPhotoPanel::isPreviewVisible()
+{
+ return (mBigPreviewFloater && mBigPreviewFloater->getVisible());
+}
+
+void LLFacebookPhotoPanel::attachPreview()
+{
+ if (mBigPreviewFloater)
+ {
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ mBigPreviewFloater->setPreview(previewp);
+ mBigPreviewFloater->setFloaterOwner(getParentByType<LLFloater>());
+ }
+}
+
+void LLFacebookPhotoPanel::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));
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookPhotoPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookPhotoPanel", boost::bind(&LLFacebookPhotoPanel::onFacebookConnectStateChange, this, _1));
// Connect to Facebook if necessary and then post
if (LLFacebookConnect::instance().isConnected())
@@ -318,7 +425,7 @@ void LLSocialPhotoPanel::onSend()
}
}
-bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data)
+bool LLFacebookPhotoPanel::onFacebookConnectStateChange(const LLSD& data)
{
switch (data.get("enum").asInteger())
{
@@ -327,7 +434,7 @@ bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data)
break;
case LLFacebookConnect::FB_POSTED:
- LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialPhotoPanel");
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookPhotoPanel");
clearAndClose();
break;
}
@@ -335,7 +442,7 @@ bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data)
return false;
}
-void LLSocialPhotoPanel::sendPhoto()
+void LLFacebookPhotoPanel::sendPhoto()
{
// Get the caption
std::string caption = mCaptionTextBox->getValue().asString();
@@ -349,7 +456,7 @@ void LLSocialPhotoPanel::sendPhoto()
updateControls();
}
-void LLSocialPhotoPanel::clearAndClose()
+void LLFacebookPhotoPanel::clearAndClose()
{
mCaptionTextBox->setValue("");
@@ -357,39 +464,28 @@ void LLSocialPhotoPanel::clearAndClose()
if (floater)
{
floater->closeFloater();
+ if (mBigPreviewFloater)
+ {
+ mBigPreviewFloater->closeOnFloaterOwnerClosing(floater);
+ }
}
}
-void LLSocialPhotoPanel::updateControls()
+void LLFacebookPhotoPanel::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
- LL_DEBUGS() << "Is snapshot up-to-date? " << got_snap << LL_ENDL;
-
- 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" ));
-
+ lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
+
updateResolution(FALSE);
}
-void LLSocialPhotoPanel::updateResolution(BOOL do_update)
+void LLFacebookPhotoPanel::updateResolution(BOOL do_update)
{
LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
+ LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
std::string sdstring = combobox->getSelectedValue();
LLSD sdres;
@@ -399,6 +495,9 @@ void LLSocialPhotoPanel::updateResolution(BOOL do_update)
S32 width = sdres[0];
S32 height = sdres[1];
+ // Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
+ std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
+
LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
if (previewp && combobox->getCurrentIndex() >= 0)
{
@@ -408,40 +507,48 @@ void LLSocialPhotoPanel::updateResolution(BOOL do_update)
if (width == 0 || height == 0)
{
// take resolution from current window size
- LL_DEBUGS() << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << LL_ENDL;
+ 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
- LL_DEBUGS() << "Setting preview res selected from combo: " << width << "x" << height << LL_ENDL;
+ lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl;
previewp->setSize(width, height);
}
checkAspectRatio(width);
previewp->getSize(width, height);
+
+ // Recompute quality setting
+ mQuality = compute_jpeg_quality(width, height);
+ previewp->setSnapshotQuality(mQuality, false);
- if(original_width != width || original_height != height)
+ if (original_width != width || original_height != height)
{
previewp->setSize(width, height);
-
- // hide old preview as the aspect ratio could be wrong
- LL_DEBUGS() << "updating thumbnail" << LL_ENDL;
-
- previewp->updateSnapshot(FALSE, TRUE);
- if(do_update)
+ if (do_update)
{
- LL_DEBUGS() << "Will update controls" << LL_ENDL;
+ previewp->updateSnapshot(TRUE);
+ updateControls();
+ }
+ }
+ // Get the old filter, compare to the current one "filter_name" and set if changed
+ std::string original_filter = previewp->getFilter();
+ if (original_filter != filter_name)
+ {
+ previewp->setFilter(filter_name);
+ if (do_update)
+ {
+ previewp->updateSnapshot(FALSE, TRUE);
updateControls();
- LLSocialPhotoPanel::onClickNewSnapshot();
}
}
-
}
}
-void LLSocialPhotoPanel::checkAspectRatio(S32 index)
+void LLFacebookPhotoPanel::checkAspectRatio(S32 index)
{
LLSnapshotLivePreview *previewp = getPreviewView() ;
@@ -462,23 +569,23 @@ void LLSocialPhotoPanel::checkAspectRatio(S32 index)
}
}
-LLUICtrl* LLSocialPhotoPanel::getRefreshBtn()
+LLUICtrl* LLFacebookPhotoPanel::getRefreshBtn()
{
return mRefreshBtn;
}
////////////////////////
-//LLSocialCheckinPanel//
+//LLFacebookCheckinPanel//
////////////////////////
-LLSocialCheckinPanel::LLSocialCheckinPanel() :
+LLFacebookCheckinPanel::LLFacebookCheckinPanel() :
mMapUrl(""),
mReloadingMapTexture(false)
{
- mCommitCallbackRegistrar.add("SocialSharing.SendCheckin", boost::bind(&LLSocialCheckinPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.SendCheckin", boost::bind(&LLFacebookCheckinPanel::onSend, this));
}
-BOOL LLSocialCheckinPanel::postBuild()
+BOOL LLFacebookCheckinPanel::postBuild()
{
// Keep pointers to widgets so we don't traverse the UI hierarchy too often
mPostButton = getChild<LLUICtrl>("post_place_btn");
@@ -492,7 +599,7 @@ BOOL LLSocialCheckinPanel::postBuild()
return LLPanel::postBuild();
}
-void LLSocialCheckinPanel::draw()
+void LLFacebookCheckinPanel::draw()
{
bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing());
mPostButton->setEnabled(no_ongoing_connection);
@@ -533,10 +640,10 @@ void LLSocialCheckinPanel::draw()
LLPanel::draw();
}
-void LLSocialCheckinPanel::onSend()
+void LLFacebookCheckinPanel::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));
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookCheckinPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookCheckinPanel", boost::bind(&LLFacebookCheckinPanel::onFacebookConnectStateChange, this, _1));
// Connect to Facebook if necessary and then post
if (LLFacebookConnect::instance().isConnected())
@@ -549,7 +656,7 @@ void LLSocialCheckinPanel::onSend()
}
}
-bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data)
+bool LLFacebookCheckinPanel::onFacebookConnectStateChange(const LLSD& data)
{
switch (data.get("enum").asInteger())
{
@@ -558,7 +665,7 @@ bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data)
break;
case LLFacebookConnect::FB_POSTED:
- LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialCheckinPanel");
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookCheckinPanel");
clearAndClose();
break;
}
@@ -566,7 +673,7 @@ bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data)
return false;
}
-void LLSocialCheckinPanel::sendCheckin()
+void LLFacebookCheckinPanel::sendCheckin()
{
// Get the location SLURL
LLSLURL slurl;
@@ -584,7 +691,12 @@ void LLSocialCheckinPanel::sendCheckin()
slurl_string += DEFAULT_CHECKIN_QUERY_PARAMETERS;
// Get the region name
- std::string region_name = gAgent.getRegion()->getName();
+ std::string region_name("");
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ region_name = regionp->getName();
+ }
// Get the region description
std::string description;
@@ -601,7 +713,7 @@ void LLSocialCheckinPanel::sendCheckin()
LLFacebookConnect::instance().postCheckin(slurl_string, region_name, description, map_url, caption);
}
-void LLSocialCheckinPanel::clearAndClose()
+void LLFacebookCheckinPanel::clearAndClose()
{
mMessageTextEditor->setValue("");
@@ -613,23 +725,186 @@ void LLSocialCheckinPanel::clearAndClose()
}
///////////////////////////
-//LLSocialAccountPanel//////
+//LLFacebookFriendsPanel//////
///////////////////////////
-LLSocialAccountPanel::LLSocialAccountPanel() :
+LLFacebookFriendsPanel::LLFacebookFriendsPanel() :
+mFriendsStatusCaption(NULL),
+mSecondLifeFriends(NULL),
+mSuggestedFriends(NULL)
+{
+}
+
+LLFacebookFriendsPanel::~LLFacebookFriendsPanel()
+{
+ LLAvatarTracker::instance().removeObserver(this);
+}
+
+BOOL LLFacebookFriendsPanel::postBuild()
+{
+ mFriendsStatusCaption = getChild<LLTextBox>("facebook_friends_status");
+
+ mSecondLifeFriends = getChild<LLAvatarList>("second_life_friends");
+ mSecondLifeFriends->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
+
+ mSuggestedFriends = getChild<LLAvatarList>("suggested_friends");
+ mSuggestedFriends->setContextMenu(&LLPanelPeopleMenus::gSuggestedFriendsContextMenu);
+
+ setVisibleCallback(boost::bind(&LLFacebookFriendsPanel::updateFacebookList, this, _2));
+
+ LLAvatarTracker::instance().addObserver(this);
+
+ return LLPanel::postBuild();
+}
+
+bool LLFacebookFriendsPanel::updateSuggestedFriendList()
+{
+ const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ uuid_vec_t& second_life_friends = mSecondLifeFriends->getIDs();
+ second_life_friends.clear();
+ 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();
+ if (agent_id.notNull())
+ {
+ bool second_life_buddy = av_tracker.isBuddy(agent_id);
+ if (second_life_buddy)
+ {
+ second_life_friends.push_back(agent_id);
+ }
+ else
+ {
+ //FB+SL but not SL friend
+ suggested_friends.push_back(agent_id);
+ }
+ }
+ }
+
+ //Force a refresh when there aren't any filter matches (prevent displaying content that shouldn't display)
+ mSecondLifeFriends->setDirty(true, !mSecondLifeFriends->filterHasMatches());
+ mSuggestedFriends->setDirty(true, !mSuggestedFriends->filterHasMatches());
+ showFriendsAccordionsIfNeeded();
+
+ return false;
+}
+
+void LLFacebookFriendsPanel::showFriendsAccordionsIfNeeded()
+{
+ // Show / hide the status text : needs to be done *before* showing / hidding the accordions
+ if (!mSecondLifeFriends->filterHasMatches() && !mSuggestedFriends->filterHasMatches())
+ {
+ // Show some explanation text if the lists are empty...
+ mFriendsStatusCaption->setVisible(true);
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ //...you're connected to FB but have no friends :(
+ mFriendsStatusCaption->setText(getString("facebook_friends_empty"));
+ }
+ else
+ {
+ //...you're not connected to FB
+ mFriendsStatusCaption->setText(getString("facebook_friends_no_connected"));
+ }
+ // Hide the lists
+ getChild<LLAccordionCtrl>("friends_accordion")->setVisible(false);
+ getChild<LLAccordionCtrlTab>("tab_second_life_friends")->setVisible(false);
+ getChild<LLAccordionCtrlTab>("tab_suggested_friends")->setVisible(false);
+ }
+ else
+ {
+ // We have something in the lists, hide the explanatory text
+ mFriendsStatusCaption->setVisible(false);
+
+ // Show the lists
+ LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("friends_accordion");
+ accordion->setVisible(true);
+
+ // Expand and show accordions if needed, else - hide them
+ getChild<LLAccordionCtrlTab>("tab_second_life_friends")->setVisible(mSecondLifeFriends->filterHasMatches());
+ getChild<LLAccordionCtrlTab>("tab_suggested_friends")->setVisible(mSuggestedFriends->filterHasMatches());
+
+ // Rearrange accordions
+ accordion->arrange();
+ }
+}
+
+void LLFacebookFriendsPanel::changed(U32 mask)
+{
+ if (mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE))
+ {
+ LLFacebookConnect::instance().loadFacebookFriends();
+ updateFacebookList(true);
+ }
+}
+
+
+void LLFacebookFriendsPanel::updateFacebookList(bool visible)
+{
+ if (visible)
+ {
+ // We want this to be called to fetch the friends list once a connection is established
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookFriendsPanel");
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookFriendsPanel", boost::bind(&LLFacebookFriendsPanel::onConnectedToFacebook, this, _1));
+
+ // We then want this to be called to update the displayed lists once the list of friends is received
+ LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLFacebookFriendsPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FacebookConnectContent").listen("LLFacebookFriendsPanel", boost::bind(&LLFacebookFriendsPanel::updateSuggestedFriendList, this));
+
+ // Try to connect to Facebook
+ if ((LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_NOT_CONNECTED) ||
+ (LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_CONNECTION_FAILED))
+ {
+ LLFacebookConnect::instance().checkConnectionToFacebook();
+ }
+ // Loads FB friends
+ if (LLFacebookConnect::instance().isConnected())
+ {
+ LLFacebookConnect::instance().loadFacebookFriends();
+ }
+ // Sort the FB friends and update the lists
+ updateSuggestedFriendList();
+ }
+}
+
+bool LLFacebookFriendsPanel::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;
+}
+
+///////////////////////////
+//LLFacebookAccountPanel//////
+///////////////////////////
+
+LLFacebookAccountPanel::LLFacebookAccountPanel() :
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));
+ mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLFacebookAccountPanel::onConnect, this));
+ mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLFacebookAccountPanel::onDisconnect, this));
- setVisibleCallback(boost::bind(&LLSocialAccountPanel::onVisibilityChanged, this, _2));
+ setVisibleCallback(boost::bind(&LLFacebookAccountPanel::onVisibilityChange, this, _2));
}
-BOOL LLSocialAccountPanel::postBuild()
+BOOL LLFacebookAccountPanel::postBuild()
{
mAccountCaptionLabel = getChild<LLTextBox>("account_caption_label");
mAccountNameLabel = getChild<LLTextBox>("account_name_label");
@@ -640,7 +915,7 @@ BOOL LLSocialAccountPanel::postBuild()
return LLPanel::postBuild();
}
-void LLSocialAccountPanel::draw()
+void LLFacebookAccountPanel::draw()
{
LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState();
@@ -655,17 +930,15 @@ void LLSocialAccountPanel::draw()
LLPanel::draw();
}
-void LLSocialAccountPanel::onVisibilityChanged(const LLSD& new_visibility)
+void LLFacebookAccountPanel::onVisibilityChange(BOOL visible)
{
- 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("FacebookConnectState").stopListening("LLFacebookAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookAccountPanel", boost::bind(&LLFacebookAccountPanel::onFacebookConnectStateChange, this, _1));
- LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLSocialAccountPanel");
- LLEventPumps::instance().obtain("FacebookConnectInfo").listen("LLSocialAccountPanel", boost::bind(&LLSocialAccountPanel::onFacebookConnectInfoChange, this));
+ LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLFacebookAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectInfo").listen("LLFacebookAccountPanel", boost::bind(&LLFacebookAccountPanel::onFacebookConnectInfoChange, this));
//Connected
if(LLFacebookConnect::instance().isConnected())
@@ -685,12 +958,12 @@ void LLSocialAccountPanel::onVisibilityChanged(const LLSD& new_visibility)
}
else
{
- LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialAccountPanel");
- LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLSocialAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookAccountPanel");
+ LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLFacebookAccountPanel");
}
}
-bool LLSocialAccountPanel::onFacebookConnectStateChange(const LLSD& data)
+bool LLFacebookAccountPanel::onFacebookConnectStateChange(const LLSD& data)
{
if(LLFacebookConnect::instance().isConnected())
{
@@ -708,7 +981,7 @@ bool LLSocialAccountPanel::onFacebookConnectStateChange(const LLSD& data)
return false;
}
-bool LLSocialAccountPanel::onFacebookConnectInfoChange()
+bool LLFacebookAccountPanel::onFacebookConnectInfoChange()
{
LLSD info = LLFacebookConnect::instance().getInfo();
std::string clickable_name;
@@ -724,7 +997,7 @@ bool LLSocialAccountPanel::onFacebookConnectInfoChange()
return false;
}
-void LLSocialAccountPanel::showConnectButton()
+void LLFacebookAccountPanel::showConnectButton()
{
if(!mConnectButton->getVisible())
{
@@ -733,7 +1006,7 @@ void LLSocialAccountPanel::showConnectButton()
}
}
-void LLSocialAccountPanel::hideConnectButton()
+void LLFacebookAccountPanel::hideConnectButton()
{
if(mConnectButton->getVisible())
{
@@ -742,14 +1015,14 @@ void LLSocialAccountPanel::hideConnectButton()
}
}
-void LLSocialAccountPanel::showDisconnectedLayout()
+void LLFacebookAccountPanel::showDisconnectedLayout()
{
mAccountCaptionLabel->setText(getString("facebook_disconnected"));
mAccountNameLabel->setText(std::string(""));
showConnectButton();
}
-void LLSocialAccountPanel::showConnectedLayout()
+void LLFacebookAccountPanel::showConnectedLayout()
{
LLFacebookConnect::instance().loadFacebookInfo();
@@ -757,7 +1030,7 @@ void LLSocialAccountPanel::showConnectedLayout()
hideConnectButton();
}
-void LLSocialAccountPanel::onConnect()
+void LLFacebookAccountPanel::onConnect()
{
LLFacebookConnect::instance().checkConnectionToFacebook(true);
@@ -765,7 +1038,7 @@ void LLSocialAccountPanel::onConnect()
LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com");
}
-void LLSocialAccountPanel::onDisconnect()
+void LLFacebookAccountPanel::onDisconnect()
{
LLFacebookConnect::instance().disconnectFromFacebook();
@@ -773,27 +1046,42 @@ void LLSocialAccountPanel::onDisconnect()
}
////////////////////////
-//LLFloaterSocial///////
+//LLFloaterFacebook///////
////////////////////////
-LLFloaterSocial::LLFloaterSocial(const LLSD& key) : LLFloater(key),
- mSocialPhotoPanel(NULL),
+LLFloaterFacebook::LLFloaterFacebook(const LLSD& key) : LLFloater(key),
+ mFacebookPhotoPanel(NULL),
mStatusErrorText(NULL),
mStatusLoadingText(NULL),
mStatusLoadingIndicator(NULL)
{
- mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterSocial::onCancel, this));
+ mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterFacebook::onCancel, this));
}
-void LLFloaterSocial::onCancel()
+void LLFloaterFacebook::onClose(bool app_quitting)
{
+ LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+ if (big_preview_floater)
+ {
+ big_preview_floater->closeOnFloaterOwnerClosing(this);
+ }
+ LLFloater::onClose(app_quitting);
+}
+
+void LLFloaterFacebook::onCancel()
+{
+ LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+ if (big_preview_floater)
+ {
+ big_preview_floater->closeOnFloaterOwnerClosing(this);
+ }
closeFloater();
}
-BOOL LLFloaterSocial::postBuild()
+BOOL LLFloaterFacebook::postBuild()
{
// Keep tab of the Photo Panel
- mSocialPhotoPanel = static_cast<LLSocialPhotoPanel*>(getChild<LLUICtrl>("panel_social_photo"));
+ mFacebookPhotoPanel = static_cast<LLFacebookPhotoPanel*>(getChild<LLUICtrl>("panel_facebook_photo"));
// Connection status widgets
mStatusErrorText = getChild<LLTextBox>("connection_error_text");
mStatusLoadingText = getChild<LLTextBox>("connection_loading_text");
@@ -801,39 +1089,19 @@ BOOL LLFloaterSocial::postBuild()
return LLFloater::postBuild();
}
-// static
-void LLFloaterSocial::preUpdate()
+void LLFloaterFacebook::showPhotoPanel()
{
- LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
- if (instance)
+ LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mFacebookPhotoPanel->getParent());
+ if (!parent)
{
- //Will set file size text to 'unknown'
- instance->mSocialPhotoPanel->updateControls();
+ llwarns << "Cannot find panel container" << llendl;
+ return;
}
-}
-
-// 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);
- }
-
- }
+ parent->selectTabPanel(mFacebookPhotoPanel);
}
-void LLFloaterSocial::draw()
+void LLFloaterFacebook::draw()
{
if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
{
diff --git a/indra/newview/llfloatersocial.h b/indra/newview/llfloaterfacebook.h
index 585b265d9f..34356412d6 100644
--- a/indra/newview/llfloatersocial.h
+++ b/indra/newview/llfloaterfacebook.h
@@ -1,6 +1,6 @@
/**
-* @file llfloatersocial.h
-* @brief Header file for llfloatersocial
+* @file llfloaterfacebook.h
+* @brief Header file for llfloaterfacebook
* @author Gilbert@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
@@ -24,9 +24,10 @@
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-#ifndef LL_LLFLOATERSOCIAL_H
-#define LL_LLFLOATERSOCIAL_H
+#ifndef LL_LLFLOATERFACEBOOK_H
+#define LL_LLFLOATERFACEBOOK_H
+#include "llcallingcard.h"
#include "llfloater.h"
#include "lltextbox.h"
#include "llviewertexture.h"
@@ -34,11 +35,13 @@
class LLIconCtrl;
class LLCheckBoxCtrl;
class LLSnapshotLivePreview;
+class LLAvatarList;
+class LLFloaterBigPreview;
-class LLSocialStatusPanel : public LLPanel
+class LLFacebookStatusPanel : public LLPanel
{
public:
- LLSocialStatusPanel();
+ LLFacebookStatusPanel();
BOOL postBuild();
void draw();
void onSend();
@@ -53,19 +56,21 @@ private:
LLUICtrl* mCancelButton;
};
-class LLSocialPhotoPanel : public LLPanel
+class LLFacebookPhotoPanel : public LLPanel
{
public:
- LLSocialPhotoPanel();
- ~LLSocialPhotoPanel();
+ LLFacebookPhotoPanel();
+ ~LLFacebookPhotoPanel();
BOOL postBuild();
void draw();
LLSnapshotLivePreview* getPreviewView();
- void onVisibilityChanged(const LLSD& new_visibility);
+ void onVisibilityChange(BOOL new_visibility);
+ void onClickBigPreview();
void onClickNewSnapshot();
void onSend();
+ S32 notify(const LLSD& info);
bool onFacebookConnectStateChange(const LLSD& data);
void sendPhoto();
@@ -77,22 +82,31 @@ public:
LLUICtrl* getRefreshBtn();
private:
+ bool isPreviewVisible();
+ void attachPreview();
+
LLHandle<LLView> mPreviewHandle;
LLUICtrl * mSnapshotPanel;
LLUICtrl * mResolutionComboBox;
+ LLUICtrl * mFilterComboBox;
LLUICtrl * mRefreshBtn;
LLUICtrl * mWorkingLabel;
LLUICtrl * mThumbnailPlaceholder;
LLUICtrl * mCaptionTextBox;
LLUICtrl * mPostButton;
- LLUICtrl* mCancelButton;
+ LLUICtrl * mCancelButton;
+ LLButton * mBtnPreview;
+
+ LLFloaterBigPreview * mBigPreviewFloater;
+
+ S32 mQuality; // Compression quality
};
-class LLSocialCheckinPanel : public LLPanel
+class LLFacebookCheckinPanel : public LLPanel
{
public:
- LLSocialCheckinPanel();
+ LLFacebookCheckinPanel();
BOOL postBuild();
void draw();
void onSend();
@@ -114,15 +128,34 @@ private:
bool mReloadingMapTexture;
};
-class LLSocialAccountPanel : public LLPanel
+class LLFacebookFriendsPanel : public LLPanel, public LLFriendObserver
+{
+public:
+ LLFacebookFriendsPanel();
+ ~LLFacebookFriendsPanel();
+ BOOL postBuild();
+ virtual void changed(U32 mask);
+
+private:
+ bool updateSuggestedFriendList();
+ void showFriendsAccordionsIfNeeded();
+ void updateFacebookList(bool visible);
+ bool onConnectedToFacebook(const LLSD& data);
+
+ LLTextBox * mFriendsStatusCaption;
+ LLAvatarList* mSecondLifeFriends;
+ LLAvatarList* mSuggestedFriends;
+};
+
+class LLFacebookAccountPanel : public LLPanel
{
public:
- LLSocialAccountPanel();
+ LLFacebookAccountPanel();
BOOL postBuild();
void draw();
private:
- void onVisibilityChanged(const LLSD& new_visibility);
+ void onVisibilityChange(BOOL new_visibility);
bool onFacebookConnectStateChange(const LLSD& data);
bool onFacebookConnectInfoChange();
void onConnect();
@@ -141,24 +174,23 @@ private:
LLUICtrl * mDisconnectButton;
};
-
-class LLFloaterSocial : public LLFloater
+class LLFloaterFacebook : public LLFloater
{
public:
- LLFloaterSocial(const LLSD& key);
+ LLFloaterFacebook(const LLSD& key);
BOOL postBuild();
void draw();
+ void onClose(bool app_quitting);
void onCancel();
-
- static void preUpdate();
- static void postUpdate();
+
+ void showPhotoPanel();
private:
- LLSocialPhotoPanel* mSocialPhotoPanel;
+ LLFacebookPhotoPanel* mFacebookPhotoPanel;
LLTextBox* mStatusErrorText;
LLTextBox* mStatusLoadingText;
LLUICtrl* mStatusLoadingIndicator;
};
-#endif // LL_LLFLOATERSOCIAL_H
+#endif // LL_LLFLOATERFACEBOOK_H
diff --git a/indra/newview/llfloaterflickr.cpp b/indra/newview/llfloaterflickr.cpp
new file mode 100644
index 0000000000..4e6d98ecfa
--- /dev/null
+++ b/indra/newview/llfloaterflickr.cpp
@@ -0,0 +1,798 @@
+/**
+* @file llfloaterflickr.cpp
+* @brief Implementation of llfloaterflickr
+* @author cho@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 "llfloaterflickr.h"
+
+#include "llagent.h"
+#include "llagentui.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llflickrconnect.h"
+#include "llfloaterreg.h"
+#include "lliconctrl.h"
+#include "llimagefiltersmanager.h"
+#include "llresmgr.h" // LLLocale
+#include "llsdserialize.h"
+#include "llloadingindicator.h"
+#include "llplugincookiestore.h"
+#include "llslurl.h"
+#include "lltrans.h"
+#include "llsnapshotlivepreview.h"
+#include "llfloaterbigpreview.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+#include "llviewermedia.h"
+#include "lltabcontainer.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+
+static LLPanelInjector<LLFlickrPhotoPanel> t_panel_photo("llflickrphotopanel");
+static LLPanelInjector<LLFlickrAccountPanel> t_panel_account("llflickraccountpanel");
+
+const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
+const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=flickr&utm_medium=photo&utm_campaign=slshare";
+const std::string DEFAULT_TAG_TEXT = "secondlife ";
+const std::string FLICKR_MACHINE_TAGS_NAMESPACE = "secondlife";
+
+///////////////////////////
+//LLFlickrPhotoPanel///////
+///////////////////////////
+
+LLFlickrPhotoPanel::LLFlickrPhotoPanel() :
+mSnapshotPanel(NULL),
+mResolutionComboBox(NULL),
+mRefreshBtn(NULL),
+mBtnPreview(NULL),
+mWorkingLabel(NULL),
+mThumbnailPlaceholder(NULL),
+mTitleTextBox(NULL),
+mDescriptionTextBox(NULL),
+mLocationCheckbox(NULL),
+mTagsTextBox(NULL),
+mRatingComboBox(NULL),
+mBigPreviewFloater(NULL),
+mPostButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLFlickrPhotoPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLFlickrPhotoPanel::onClickNewSnapshot, this));
+ mCommitCallbackRegistrar.add("SocialSharing.BigPreview", boost::bind(&LLFlickrPhotoPanel::onClickBigPreview, this));
+}
+
+LLFlickrPhotoPanel::~LLFlickrPhotoPanel()
+{
+ if(mPreviewHandle.get())
+ {
+ mPreviewHandle.get()->die();
+ }
+}
+
+BOOL LLFlickrPhotoPanel::postBuild()
+{
+ setVisibleCallback(boost::bind(&LLFlickrPhotoPanel::onVisibilityChange, this, _2));
+
+ mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
+ mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
+ mResolutionComboBox->setCommitCallback(boost::bind(&LLFlickrPhotoPanel::updateResolution, this, TRUE));
+ mFilterComboBox = getChild<LLUICtrl>("filters_combobox");
+ mFilterComboBox->setCommitCallback(boost::bind(&LLFlickrPhotoPanel::updateResolution, this, TRUE));
+ mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
+ mBtnPreview = getChild<LLButton>("big_preview_btn");
+ mWorkingLabel = getChild<LLUICtrl>("working_lbl");
+ mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
+ mTitleTextBox = getChild<LLUICtrl>("photo_title");
+ mDescriptionTextBox = getChild<LLUICtrl>("photo_description");
+ mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
+ mTagsTextBox = getChild<LLUICtrl>("photo_tags");
+ mTagsTextBox->setValue(DEFAULT_TAG_TEXT);
+ mRatingComboBox = getChild<LLUICtrl>("rating_combobox");
+ mPostButton = getChild<LLUICtrl>("post_photo_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
+ mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+
+ // Update filter list
+ std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
+ LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
+ for (U32 i = 0; i < filter_list.size(); i++)
+ {
+ filterbox->add(filter_list[i]);
+ }
+
+ return LLPanel::postBuild();
+}
+
+// virtual
+S32 LLFlickrPhotoPanel::notify(const LLSD& info)
+{
+ if (info.has("snapshot-updating"))
+ {
+ // Disable the Post button and whatever else while the snapshot is not updated
+ // updateControls();
+ return 1;
+ }
+
+ if (info.has("snapshot-updated"))
+ {
+ // Enable the send/post/save buttons.
+ updateControls();
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. after snapshot is taken
+ LLUICtrl * refresh_button = getRefreshBtn();
+ if (!refresh_button->getVisible())
+ {
+ refresh_button->setVisible(true);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+void LLFlickrPhotoPanel::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 = !(LLFlickrConnect::instance().isTransactionOngoing());
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mTitleTextBox->setEnabled(no_ongoing_connection);
+ mDescriptionTextBox->setEnabled(no_ongoing_connection);
+ mTagsTextBox->setEnabled(no_ongoing_connection);
+ mRatingComboBox->setEnabled(no_ongoing_connection);
+ mResolutionComboBox->setEnabled(no_ongoing_connection);
+ mFilterComboBox->setEnabled(no_ongoing_connection);
+ mRefreshBtn->setEnabled(no_ongoing_connection);
+ mBtnPreview->setEnabled(no_ongoing_connection);
+ mLocationCheckbox->setEnabled(no_ongoing_connection);
+
+ // Reassign the preview floater if we have the focus and the preview exists
+ if (hasFocus() && isPreviewVisible())
+ {
+ attachPreview();
+ }
+
+ // Toggle the button state as appropriate
+ bool preview_active = (isPreviewVisible() && mBigPreviewFloater->isFloaterOwner(getParentByType<LLFloater>()));
+ mBtnPreview->setToggleState(preview_active);
+
+ // 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);
+ }
+
+ // 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* LLFlickrPhotoPanel::getPreviewView()
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
+ return previewp;
+}
+
+void LLFlickrPhotoPanel::onVisibilityChange(BOOL visible)
+{
+ 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->setContainer(this);
+ previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
+ previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG);
+ previewp->setThumbnailSubsampled(TRUE); // We want the preview to reflect the *saved* image
+ previewp->setAllowRenderUI(FALSE); // We do not want the rendered UI in our snapshots
+ previewp->setAllowFullScreenPreview(FALSE); // No full screen preview in SL Share mode
+ previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
+
+ updateControls();
+ }
+ }
+}
+
+void LLFlickrPhotoPanel::onClickNewSnapshot()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (previewp)
+ {
+ previewp->updateSnapshot(TRUE);
+ }
+}
+
+void LLFlickrPhotoPanel::onClickBigPreview()
+{
+ // Toggle the preview
+ if (isPreviewVisible())
+ {
+ LLFloaterReg::hideInstance("big_preview");
+ }
+ else
+ {
+ attachPreview();
+ LLFloaterReg::showInstance("big_preview");
+ }
+}
+
+bool LLFlickrPhotoPanel::isPreviewVisible()
+{
+ return (mBigPreviewFloater && mBigPreviewFloater->getVisible());
+}
+
+void LLFlickrPhotoPanel::attachPreview()
+{
+ if (mBigPreviewFloater)
+ {
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ mBigPreviewFloater->setPreview(previewp);
+ mBigPreviewFloater->setFloaterOwner(getParentByType<LLFloater>());
+ }
+}
+
+void LLFlickrPhotoPanel::onSend()
+{
+ LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrPhotoPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FlickrConnectState").listen("LLFlickrPhotoPanel", boost::bind(&LLFlickrPhotoPanel::onFlickrConnectStateChange, this, _1));
+
+ // Connect to Flickr if necessary and then post
+ if (LLFlickrConnect::instance().isConnected())
+ {
+ sendPhoto();
+ }
+ else
+ {
+ LLFlickrConnect::instance().checkConnectionToFlickr(true);
+ }
+}
+
+bool LLFlickrPhotoPanel::onFlickrConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLFlickrConnect::FLICKR_CONNECTED:
+ sendPhoto();
+ break;
+
+ case LLFlickrConnect::FLICKR_POSTED:
+ LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrPhotoPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLFlickrPhotoPanel::sendPhoto()
+{
+ // Get the title, description, and tags
+ std::string title = mTitleTextBox->getValue().asString();
+ std::string description = mDescriptionTextBox->getValue().asString();
+ std::string tags = mTagsTextBox->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;
+
+ std::string photo_link_text = "Visit this location";// at [] in Second Life";
+ std::string parcel_name = LLViewerParcelMgr::getInstance()->getAgentParcelName();
+ if (!parcel_name.empty())
+ {
+ photo_link_text += " at " + parcel_name;
+ }
+ photo_link_text += " in Second Life";
+
+ slurl_string = "<a href=\"" + slurl_string + "\">" + photo_link_text + "</a>";
+
+ // Add it to the description (pretty crude, but we don't have a better option with photos)
+ if (description.empty())
+ description = slurl_string;
+ else
+ description = description + "\n\n" + slurl_string;
+
+ // Also add special "machine tags" with location metadata
+ const LLVector3& agent_pos_region = gAgent.getPositionAgent();
+ LLViewerRegion* region = gAgent.getRegion();
+ LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
+ if (region && parcel)
+ {
+ S32 pos_x = S32(agent_pos_region.mV[VX]);
+ S32 pos_y = S32(agent_pos_region.mV[VY]);
+ S32 pos_z = S32(agent_pos_region.mV[VZ]);
+
+ std::string parcel_name = LLViewerParcelMgr::getInstance()->getAgentParcelName();
+ std::string region_name = region->getName();
+
+ if (!region_name.empty())
+ {
+ tags += llformat(" \"%s:region=%s\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), region_name.c_str());
+ }
+ if (!parcel_name.empty())
+ {
+ tags += llformat(" \"%s:parcel=%s\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), parcel_name.c_str());
+ }
+ tags += llformat(" \"%s:x=%d\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), pos_x);
+ tags += llformat(" \"%s:y=%d\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), pos_y);
+ tags += llformat(" \"%s:z=%d\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), pos_z);
+ }
+ }
+
+ // Get the content rating
+ int content_rating = mRatingComboBox->getValue().asInteger();
+
+ // Get the image
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ // Post to Flickr
+ LLFlickrConnect::instance().uploadPhoto(previewp->getFormattedImage(), title, description, tags, content_rating);
+
+ updateControls();
+}
+
+void LLFlickrPhotoPanel::clearAndClose()
+{
+ mTitleTextBox->setValue("");
+ mDescriptionTextBox->setValue("");
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ if (mBigPreviewFloater)
+ {
+ mBigPreviewFloater->closeOnFloaterOwnerClosing(floater);
+ }
+ }
+}
+
+void LLFlickrPhotoPanel::updateControls()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
+
+ // *TODO: Separate maximum size for Web images from postcards
+ lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
+
+ updateResolution(FALSE);
+}
+
+void LLFlickrPhotoPanel::updateResolution(BOOL do_update)
+{
+ LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
+ LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
+
+ 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];
+
+ // Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
+ std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
+
+ 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);
+ if (do_update)
+ {
+ previewp->updateSnapshot(TRUE);
+ updateControls();
+ }
+ }
+ // Get the old filter, compare to the current one "filter_name" and set if changed
+ std::string original_filter = previewp->getFilter();
+ if (original_filter != filter_name)
+ {
+ previewp->setFilter(filter_name);
+ if (do_update)
+ {
+ previewp->updateSnapshot(FALSE, TRUE);
+ updateControls();
+ }
+ }
+ }
+}
+
+void LLFlickrPhotoPanel::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* LLFlickrPhotoPanel::getRefreshBtn()
+{
+ return mRefreshBtn;
+}
+
+///////////////////////////
+//LLFlickrAccountPanel//////
+///////////////////////////
+
+LLFlickrAccountPanel::LLFlickrAccountPanel() :
+mAccountCaptionLabel(NULL),
+mAccountNameLabel(NULL),
+mPanelButtons(NULL),
+mConnectButton(NULL),
+mDisconnectButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLFlickrAccountPanel::onConnect, this));
+ mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLFlickrAccountPanel::onDisconnect, this));
+
+ setVisibleCallback(boost::bind(&LLFlickrAccountPanel::onVisibilityChange, this, _2));
+}
+
+BOOL LLFlickrAccountPanel::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 LLFlickrAccountPanel::draw()
+{
+ LLFlickrConnect::EConnectionState connection_state = LLFlickrConnect::instance().getConnectionState();
+
+ //Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress
+ bool disconnecting = connection_state == LLFlickrConnect::FLICKR_DISCONNECTING;
+ mDisconnectButton->setEnabled(!disconnecting);
+
+ //Disable the 'connect' button when a connection is in progress
+ bool connecting = connection_state == LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS;
+ mConnectButton->setEnabled(!connecting);
+
+ LLPanel::draw();
+}
+
+void LLFlickrAccountPanel::onVisibilityChange(BOOL visible)
+{
+ if(visible)
+ {
+ LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrAccountPanel");
+ LLEventPumps::instance().obtain("FlickrConnectState").listen("LLFlickrAccountPanel", boost::bind(&LLFlickrAccountPanel::onFlickrConnectStateChange, this, _1));
+
+ LLEventPumps::instance().obtain("FlickrConnectInfo").stopListening("LLFlickrAccountPanel");
+ LLEventPumps::instance().obtain("FlickrConnectInfo").listen("LLFlickrAccountPanel", boost::bind(&LLFlickrAccountPanel::onFlickrConnectInfoChange, this));
+
+ //Connected
+ if(LLFlickrConnect::instance().isConnected())
+ {
+ showConnectedLayout();
+ }
+ //Check if connected (show disconnected layout in meantime)
+ else
+ {
+ showDisconnectedLayout();
+ }
+ if ((LLFlickrConnect::instance().getConnectionState() == LLFlickrConnect::FLICKR_NOT_CONNECTED) ||
+ (LLFlickrConnect::instance().getConnectionState() == LLFlickrConnect::FLICKR_CONNECTION_FAILED))
+ {
+ LLFlickrConnect::instance().checkConnectionToFlickr();
+ }
+ }
+ else
+ {
+ LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrAccountPanel");
+ LLEventPumps::instance().obtain("FlickrConnectInfo").stopListening("LLFlickrAccountPanel");
+ }
+}
+
+bool LLFlickrAccountPanel::onFlickrConnectStateChange(const LLSD& data)
+{
+ if(LLFlickrConnect::instance().isConnected())
+ {
+ //In process of disconnecting so leave the layout as is
+ if(data.get("enum").asInteger() != LLFlickrConnect::FLICKR_DISCONNECTING)
+ {
+ showConnectedLayout();
+ }
+ }
+ else
+ {
+ showDisconnectedLayout();
+ }
+
+ return false;
+}
+
+bool LLFlickrAccountPanel::onFlickrConnectInfoChange()
+{
+ LLSD info = LLFlickrConnect::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 LLFlickrAccountPanel::showConnectButton()
+{
+ if(!mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(TRUE);
+ mDisconnectButton->setVisible(FALSE);
+ }
+}
+
+void LLFlickrAccountPanel::hideConnectButton()
+{
+ if(mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(FALSE);
+ mDisconnectButton->setVisible(TRUE);
+ }
+}
+
+void LLFlickrAccountPanel::showDisconnectedLayout()
+{
+ mAccountCaptionLabel->setText(getString("flickr_disconnected"));
+ mAccountNameLabel->setText(std::string(""));
+ showConnectButton();
+}
+
+void LLFlickrAccountPanel::showConnectedLayout()
+{
+ LLFlickrConnect::instance().loadFlickrInfo();
+
+ mAccountCaptionLabel->setText(getString("flickr_connected"));
+ hideConnectButton();
+}
+
+void LLFlickrAccountPanel::onConnect()
+{
+ LLFlickrConnect::instance().checkConnectionToFlickr(true);
+
+ //Clear only the flickr browser cookies so that the flickr login screen appears
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".flickr.com");
+}
+
+void LLFlickrAccountPanel::onDisconnect()
+{
+ LLFlickrConnect::instance().disconnectFromFlickr();
+
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".flickr.com");
+}
+
+////////////////////////
+//LLFloaterFlickr///////
+////////////////////////
+
+LLFloaterFlickr::LLFloaterFlickr(const LLSD& key) : LLFloater(key),
+ mFlickrPhotoPanel(NULL),
+ mStatusErrorText(NULL),
+ mStatusLoadingText(NULL),
+ mStatusLoadingIndicator(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterFlickr::onCancel, this));
+}
+
+void LLFloaterFlickr::onClose(bool app_quitting)
+{
+ LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+ if (big_preview_floater)
+ {
+ big_preview_floater->closeOnFloaterOwnerClosing(this);
+ }
+ LLFloater::onClose(app_quitting);
+}
+
+void LLFloaterFlickr::onCancel()
+{
+ LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+ if (big_preview_floater)
+ {
+ big_preview_floater->closeOnFloaterOwnerClosing(this);
+ }
+ closeFloater();
+}
+
+BOOL LLFloaterFlickr::postBuild()
+{
+ // Keep tab of the Photo Panel
+ mFlickrPhotoPanel = static_cast<LLFlickrPhotoPanel*>(getChild<LLUICtrl>("panel_flickr_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();
+}
+
+void LLFloaterFlickr::showPhotoPanel()
+{
+ LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mFlickrPhotoPanel->getParent());
+ if (!parent)
+ {
+ llwarns << "Cannot find panel container" << llendl;
+ return;
+ }
+
+ parent->selectTabPanel(mFlickrPhotoPanel);
+}
+
+void LLFloaterFlickr::draw()
+{
+ if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
+ {
+ mStatusErrorText->setVisible(false);
+ mStatusLoadingText->setVisible(false);
+ mStatusLoadingIndicator->setVisible(false);
+ LLFlickrConnect::EConnectionState connection_state = LLFlickrConnect::instance().getConnectionState();
+ std::string status_text;
+
+ switch (connection_state)
+ {
+ case LLFlickrConnect::FLICKR_NOT_CONNECTED:
+ // No status displayed when first opening the panel and no connection done
+ case LLFlickrConnect::FLICKR_CONNECTED:
+ // When successfully connected, no message is displayed
+ case LLFlickrConnect::FLICKR_POSTED:
+ // No success message to show since we actually close the floater after successful posting completion
+ break;
+ case LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS:
+ // Connection loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrConnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFlickrConnect::FLICKR_POSTING:
+ // Posting indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrPosting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFlickrConnect::FLICKR_CONNECTION_FAILED:
+ // Error connecting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrErrorConnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLFlickrConnect::FLICKR_POST_FAILED:
+ // Error posting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrErrorPosting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLFlickrConnect::FLICKR_DISCONNECTING:
+ // Disconnecting loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrDisconnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFlickrConnect::FLICKR_DISCONNECT_FAILED:
+ // Error disconnecting from the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrErrorDisconnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ }
+ }
+ LLFloater::draw();
+}
+
diff --git a/indra/newview/llfloaterflickr.h b/indra/newview/llfloaterflickr.h
new file mode 100644
index 0000000000..ba27c9a3d8
--- /dev/null
+++ b/indra/newview/llfloaterflickr.h
@@ -0,0 +1,135 @@
+/**
+* @file llfloaterflickr.h
+* @brief Header file for llfloaterflickr
+* @author cho@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_LLFLOATERFLICKR_H
+#define LL_LLFLOATERFLICKR_H
+
+#include "llfloater.h"
+#include "lltextbox.h"
+#include "llviewertexture.h"
+
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLSnapshotLivePreview;
+class LLFloaterBigPreview;
+
+class LLFlickrPhotoPanel : public LLPanel
+{
+public:
+ LLFlickrPhotoPanel();
+ ~LLFlickrPhotoPanel();
+
+ BOOL postBuild();
+ S32 notify(const LLSD& info);
+ void draw();
+
+ LLSnapshotLivePreview* getPreviewView();
+ void onVisibilityChange(BOOL new_visibility);
+ void onClickNewSnapshot();
+ void onClickBigPreview();
+ void onSend();
+ bool onFlickrConnectStateChange(const LLSD& data);
+
+ void sendPhoto();
+ void clearAndClose();
+
+ void updateControls();
+ void updateResolution(BOOL do_update);
+ void checkAspectRatio(S32 index);
+ LLUICtrl* getRefreshBtn();
+
+private:
+ bool isPreviewVisible();
+ void attachPreview();
+
+ LLHandle<LLView> mPreviewHandle;
+
+ LLUICtrl * mSnapshotPanel;
+ LLUICtrl * mResolutionComboBox;
+ LLUICtrl * mFilterComboBox;
+ LLUICtrl * mRefreshBtn;
+ LLUICtrl * mWorkingLabel;
+ LLUICtrl * mThumbnailPlaceholder;
+ LLUICtrl * mTitleTextBox;
+ LLUICtrl * mDescriptionTextBox;
+ LLUICtrl * mLocationCheckbox;
+ LLUICtrl * mTagsTextBox;
+ LLUICtrl * mRatingComboBox;
+ LLUICtrl * mPostButton;
+ LLUICtrl * mCancelButton;
+ LLButton * mBtnPreview;
+
+ LLFloaterBigPreview * mBigPreviewFloater;
+};
+
+class LLFlickrAccountPanel : public LLPanel
+{
+public:
+ LLFlickrAccountPanel();
+ BOOL postBuild();
+ void draw();
+
+private:
+ void onVisibilityChange(BOOL new_visibility);
+ bool onFlickrConnectStateChange(const LLSD& data);
+ bool onFlickrConnectInfoChange();
+ 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 LLFloaterFlickr : public LLFloater
+{
+public:
+ LLFloaterFlickr(const LLSD& key);
+ BOOL postBuild();
+ void draw();
+ void onClose(bool app_quitting);
+ void onCancel();
+
+ void showPhotoPanel();
+
+private:
+ LLFlickrPhotoPanel* mFlickrPhotoPanel;
+ LLTextBox* mStatusErrorText;
+ LLTextBox* mStatusLoadingText;
+ LLUICtrl* mStatusLoadingIndicator;
+};
+
+#endif // LL_LLFLOATERFLICKR_H
+
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index 3d5b297fbe..960d3f35dd 100755
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -31,7 +31,10 @@
#include "llagent.h"
#include "llfacebookconnect.h"
#include "llfloaterreg.h"
-#include "llfloatersocial.h"
+#include "llfloaterfacebook.h"
+#include "llfloaterflickr.h"
+#include "llfloatertwitter.h"
+#include "llimagefiltersmanager.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llpostcard.h"
@@ -91,6 +94,7 @@ public:
}
static void onClickNewSnapshot(void* data);
static void onClickAutoSnap(LLUICtrl *ctrl, void* data);
+ static void onClickFilter(LLUICtrl *ctrl, void* data);
//static void onClickAdvanceSnap(LLUICtrl *ctrl, void* data);
static void onClickMore(void* data) ;
static void onClickUICheck(LLUICtrl *ctrl, void* data);
@@ -429,9 +433,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
image_res_tb->setVisible(got_snap);
if (got_snap)
{
- LLPointer<LLImageRaw> img = previewp->getEncodedImage();
- image_res_tb->setTextArg("[WIDTH]", llformat("%d", img->getWidth()));
- image_res_tb->setTextArg("[HEIGHT]", llformat("%d", img->getHeight()));
+ image_res_tb->setTextArg("[WIDTH]", llformat("%d", previewp->getEncodedImageWidth()));
+ image_res_tb->setTextArg("[HEIGHT]", llformat("%d", previewp->getEncodedImageHeight()));
}
floater->getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : floater->getString("unknown"));
@@ -464,8 +467,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
default:
break;
}
-
- if (previewp)
+
+ if (previewp)
{
previewp->setSnapshotType(shot_type);
previewp->setSnapshotFormat(shot_format);
@@ -558,6 +561,26 @@ void LLFloaterSnapshot::Impl::onClickAutoSnap(LLUICtrl *ctrl, void* data)
}
}
+// static
+void LLFloaterSnapshot::Impl::onClickFilter(LLUICtrl *ctrl, void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ updateControls(view);
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+ if (previewp)
+ {
+ checkAutoSnapshot(previewp);
+ // Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
+ LLComboBox* filterbox = static_cast<LLComboBox *>(view->getChild<LLComboBox>("filters_combobox"));
+ std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
+ previewp->setFilter(filter_name);
+ previewp->updateSnapshot(FALSE, TRUE);
+ }
+ }
+}
+
void LLFloaterSnapshot::Impl::onClickMore(void* data)
{
BOOL visible = gSavedSettings.getBOOL("AdvanceSnapshot");
@@ -618,7 +641,7 @@ void LLFloaterSnapshot::Impl::applyKeepAspectCheck(LLFloaterSnapshot* view, BOOL
LL_DEBUGS() << "updating thumbnail" << LL_ENDL;
previewp->setSize(w, h) ;
- previewp->updateSnapshot(FALSE, TRUE);
+ previewp->updateSnapshot(TRUE);
checkAutoSnapshot(previewp, TRUE);
}
}
@@ -853,7 +876,6 @@ void LLFloaterSnapshot::Impl::onImageQualityChange(LLFloaterSnapshot* view, S32
{
previewp->setSnapshotQuality(quality_val);
}
- checkAutoSnapshot(previewp, TRUE);
}
// static
@@ -1052,7 +1074,26 @@ BOOL LLFloaterSnapshot::postBuild()
getChild<LLUICtrl>("auto_snapshot_check")->setValue(gSavedSettings.getBOOL("AutoSnapshot"));
childSetCommitCallback("auto_snapshot_check", Impl::onClickAutoSnap, this);
-
+
+ // Filters
+ LLComboBox* filterbox = getChild<LLComboBox>("filters_combobox");
+ if (gSavedSettings.getBOOL("SnapshotFiltersEnabled"))
+ {
+ // Update filter list if setting is on (experimental)
+ std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
+ for (U32 i = 0; i < filter_list.size(); i++)
+ {
+ filterbox->add(filter_list[i]);
+ }
+ childSetCommitCallback("filters_combobox", Impl::onClickFilter, this);
+ }
+ else
+ {
+ // Hide Filter UI if setting is off (default)
+ getChild<LLUICtrl>("filter_list_label")->setVisible(FALSE);
+ filterbox->setVisible(FALSE);
+ }
+
LLWebProfile::setImageUploadResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSnapshotUploadFinished, _1));
LLPostCard::setPostResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSendingPostcardFinished, _1));
@@ -1082,6 +1123,7 @@ BOOL LLFloaterSnapshot::postBuild()
getChild<LLComboBox>("local_format_combo")->selectNthItem(0);
impl.mPreviewHandle = previewp->getHandle();
+ previewp->setContainer(this);
impl.updateControls(this);
impl.updateLayout(this);
@@ -1246,6 +1288,32 @@ S32 LLFloaterSnapshot::notify(const LLSD& info)
impl.setStatus(Impl::STATUS_FINISHED, data["ok"].asBoolean(), data["msg"].asString());
return 1;
}
+
+ if (info.has("snapshot-updating"))
+ {
+ // Disable the send/post/save buttons until snapshot is ready.
+ impl.updateControls(this);
+ // Force hiding the "Refresh to save" hint because we know we've just started refresh.
+ impl.setNeedRefresh(this, false);
+ return 1;
+ }
+
+ if (info.has("snapshot-updated"))
+ {
+ // Enable the send/post/save buttons.
+ impl.updateControls(this);
+ // We've just done refresh.
+ impl.setNeedRefresh(this, false);
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. when preview appears.
+ if (!mRefreshBtn->getVisible())
+ {
+ mRefreshBtn->setVisible(true);
+ }
+ return 1;
+ }
+
return 0;
}
@@ -1253,9 +1321,11 @@ S32 LLFloaterSnapshot::notify(const LLSD& info)
void LLFloaterSnapshot::update()
{
LLFloaterSnapshot* inst = findInstance();
- LLFloaterSocial* floater_social = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+ LLFloaterFacebook* floater_facebook = LLFloaterReg::findTypedInstance<LLFloaterFacebook>("facebook");
+ LLFloaterFlickr* floater_flickr = LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr");
+ LLFloaterTwitter* floater_twitter = LLFloaterReg::findTypedInstance<LLFloaterTwitter>("twitter");
- if (!inst && !floater_social)
+ if (!inst && !floater_facebook && !floater_flickr && !floater_twitter)
return;
BOOL changed = FALSE;
@@ -1329,43 +1399,6 @@ BOOL LLFloaterSnapshot::saveLocal()
}
// static
-void LLFloaterSnapshot::preUpdate()
-{
- // FIXME: duplicated code
- LLFloaterSnapshot* instance = findInstance();
- if (instance)
- {
- // Disable the send/post/save buttons until snapshot is ready.
- Impl::updateControls(instance);
-
- // Force hiding the "Refresh to save" hint because we know we've just started refresh.
- Impl::setNeedRefresh(instance, false);
- }
-}
-
-// static
-void LLFloaterSnapshot::postUpdate()
-{
- // FIXME: duplicated code
- LLFloaterSnapshot* instance = findInstance();
- if (instance)
- {
- // Enable the send/post/save buttons.
- Impl::updateControls(instance);
-
- // We've just done refresh.
- Impl::setNeedRefresh(instance, false);
-
- // The refresh button is initially hidden. We show it after the first update,
- // i.e. when preview appears.
- if (!instance->mRefreshBtn->getVisible())
- {
- instance->mRefreshBtn->setVisible(true);
- }
- }
-}
-
-// static
void LLFloaterSnapshot::postSave()
{
LLFloaterSnapshot* instance = findInstance();
diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h
index c757bf21c2..0bb9474bb5 100755
--- a/indra/newview/llfloatersnapshot.h
+++ b/indra/newview/llfloatersnapshot.h
@@ -59,8 +59,6 @@ public:
static LLFloaterSnapshot* findInstance();
static void saveTexture();
static BOOL saveLocal();
- static void preUpdate();
- static void postUpdate();
static void postSave();
static void postPanelSwitch();
static LLPointer<LLImageFormatted> getImageData();
diff --git a/indra/newview/llfloatertwitter.cpp b/indra/newview/llfloatertwitter.cpp
new file mode 100644
index 0000000000..78e9259919
--- /dev/null
+++ b/indra/newview/llfloatertwitter.cpp
@@ -0,0 +1,827 @@
+/**
+* @file llfloatertwitter.cpp
+* @brief Implementation of llfloatertwitter
+* @author cho@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 "llfloatertwitter.h"
+
+#include "llagent.h"
+#include "llagentui.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lltwitterconnect.h"
+#include "llfloaterbigpreview.h"
+#include "llfloaterreg.h"
+#include "lliconctrl.h"
+#include "llimagefiltersmanager.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"
+#include "lltabcontainer.h"
+#include "lltexteditor.h"
+
+static LLPanelInjector<LLTwitterPhotoPanel> t_panel_photo("lltwitterphotopanel");
+static LLPanelInjector<LLTwitterAccountPanel> t_panel_account("lltwitteraccountpanel");
+
+const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
+const std::string DEFAULT_PHOTO_LOCATION_URL = "http://maps.secondlife.com/";
+const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=twitter&utm_medium=photo&utm_campaign=slshare";
+const std::string DEFAULT_STATUS_TEXT = " #SecondLife";
+
+///////////////////////////
+//LLTwitterPhotoPanel///////
+///////////////////////////
+
+LLTwitterPhotoPanel::LLTwitterPhotoPanel() :
+mSnapshotPanel(NULL),
+mResolutionComboBox(NULL),
+mRefreshBtn(NULL),
+mBtnPreview(NULL),
+mWorkingLabel(NULL),
+mThumbnailPlaceholder(NULL),
+mStatusCounterLabel(NULL),
+mStatusTextBox(NULL),
+mLocationCheckbox(NULL),
+mPhotoCheckbox(NULL),
+mBigPreviewFloater(NULL),
+mPostButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLTwitterPhotoPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLTwitterPhotoPanel::onClickNewSnapshot, this));
+ mCommitCallbackRegistrar.add("SocialSharing.BigPreview", boost::bind(&LLTwitterPhotoPanel::onClickBigPreview, this));
+}
+
+LLTwitterPhotoPanel::~LLTwitterPhotoPanel()
+{
+ if(mPreviewHandle.get())
+ {
+ mPreviewHandle.get()->die();
+ }
+}
+
+BOOL LLTwitterPhotoPanel::postBuild()
+{
+ setVisibleCallback(boost::bind(&LLTwitterPhotoPanel::onVisibilityChange, this, _2));
+
+ mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
+ mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
+ mResolutionComboBox->setValue("[i800,i600]"); // hardcoded defaults ftw!
+ mResolutionComboBox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::updateResolution, this, TRUE));
+ mFilterComboBox = getChild<LLUICtrl>("filters_combobox");
+ mFilterComboBox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::updateResolution, this, TRUE));
+ mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
+ mBtnPreview = getChild<LLButton>("big_preview_btn");
+ mWorkingLabel = getChild<LLUICtrl>("working_lbl");
+ mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
+ mStatusCounterLabel = getChild<LLUICtrl>("status_counter_label");
+ mStatusTextBox = getChild<LLUICtrl>("photo_status");
+ mStatusTextBox->setValue(DEFAULT_STATUS_TEXT);
+ mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
+ mLocationCheckbox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::onAddLocationToggled, this));
+ mPhotoCheckbox = getChild<LLUICtrl>("add_photo_cb");
+ mPhotoCheckbox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::onAddPhotoToggled, this));
+ mPostButton = getChild<LLUICtrl>("post_photo_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
+ mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+
+ // Update filter list
+ std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
+ LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
+ for (U32 i = 0; i < filter_list.size(); i++)
+ {
+ filterbox->add(filter_list[i]);
+ }
+
+ return LLPanel::postBuild();
+}
+
+// virtual
+S32 LLTwitterPhotoPanel::notify(const LLSD& info)
+{
+ if (info.has("snapshot-updating"))
+ {
+ // Disable the Post button and whatever else while the snapshot is not updated
+ // updateControls();
+ return 1;
+ }
+
+ if (info.has("snapshot-updated"))
+ {
+ // Enable the send/post/save buttons.
+ updateControls();
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. after snapshot is taken
+ LLUICtrl * refresh_button = getRefreshBtn();
+ if (!refresh_button->getVisible())
+ {
+ refresh_button->setVisible(true);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+void LLTwitterPhotoPanel::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 = !(LLTwitterConnect::instance().isTransactionOngoing());
+ bool photo_checked = mPhotoCheckbox->getValue().asBoolean();
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mStatusTextBox->setEnabled(no_ongoing_connection);
+ mResolutionComboBox->setEnabled(no_ongoing_connection && photo_checked);
+ mFilterComboBox->setEnabled(no_ongoing_connection && photo_checked);
+ mRefreshBtn->setEnabled(no_ongoing_connection && photo_checked);
+ mBtnPreview->setEnabled(no_ongoing_connection);
+ mLocationCheckbox->setEnabled(no_ongoing_connection);
+ mPhotoCheckbox->setEnabled(no_ongoing_connection);
+
+ bool add_location = mLocationCheckbox->getValue().asBoolean();
+ bool add_photo = mPhotoCheckbox->getValue().asBoolean();
+ updateStatusTextLength(false);
+
+ // Reassign the preview floater if we have the focus and the preview exists
+ if (hasFocus() && isPreviewVisible())
+ {
+ attachPreview();
+ }
+
+ // Toggle the button state as appropriate
+ bool preview_active = (isPreviewVisible() && mBigPreviewFloater->isFloaterOwner(getParentByType<LLFloater>()));
+ mBtnPreview->setToggleState(preview_active);
+
+ // 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 = (add_photo ? (getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency()) : 0.5f);
+ LLColor4 color = LLColor4::white;
+ gl_draw_scaled_image(offset_x, offset_y,
+ thumbnail_w, thumbnail_h,
+ previewp->getThumbnailImage(), color % alpha);
+ }
+
+ // 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()) && (add_photo || add_location || !mStatusTextBox->getValue().asString().empty()));
+
+ // Draw the rest of the panel on top of it
+ LLPanel::draw();
+}
+
+LLSnapshotLivePreview* LLTwitterPhotoPanel::getPreviewView()
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
+ return previewp;
+}
+
+void LLTwitterPhotoPanel::onVisibilityChange(BOOL visible)
+{
+ 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->setContainer(this);
+ previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
+ previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
+ previewp->setThumbnailSubsampled(TRUE); // We want the preview to reflect the *saved* image
+ previewp->setAllowRenderUI(FALSE); // We do not want the rendered UI in our snapshots
+ previewp->setAllowFullScreenPreview(FALSE); // No full screen preview in SL Share mode
+ previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
+
+ updateControls();
+ }
+ }
+}
+
+void LLTwitterPhotoPanel::onAddLocationToggled()
+{
+ bool add_location = mLocationCheckbox->getValue().asBoolean();
+ updateStatusTextLength(!add_location);
+}
+
+void LLTwitterPhotoPanel::onAddPhotoToggled()
+{
+ bool add_photo = mPhotoCheckbox->getValue().asBoolean();
+ updateStatusTextLength(!add_photo);
+}
+
+void LLTwitterPhotoPanel::onClickNewSnapshot()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (previewp)
+ {
+ previewp->updateSnapshot(TRUE);
+ }
+}
+
+void LLTwitterPhotoPanel::onClickBigPreview()
+{
+ // Toggle the preview
+ if (isPreviewVisible())
+ {
+ LLFloaterReg::hideInstance("big_preview");
+ }
+ else
+ {
+ attachPreview();
+ LLFloaterReg::showInstance("big_preview");
+ }
+}
+
+bool LLTwitterPhotoPanel::isPreviewVisible()
+{
+ return (mBigPreviewFloater && mBigPreviewFloater->getVisible());
+}
+
+void LLTwitterPhotoPanel::attachPreview()
+{
+ if (mBigPreviewFloater)
+ {
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ mBigPreviewFloater->setPreview(previewp);
+ mBigPreviewFloater->setFloaterOwner(getParentByType<LLFloater>());
+ }
+}
+
+void LLTwitterPhotoPanel::onSend()
+{
+ LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterPhotoPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("TwitterConnectState").listen("LLTwitterPhotoPanel", boost::bind(&LLTwitterPhotoPanel::onTwitterConnectStateChange, this, _1));
+
+ // Connect to Twitter if necessary and then post
+ if (LLTwitterConnect::instance().isConnected())
+ {
+ sendPhoto();
+ }
+ else
+ {
+ LLTwitterConnect::instance().checkConnectionToTwitter(true);
+ }
+}
+
+bool LLTwitterPhotoPanel::onTwitterConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLTwitterConnect::TWITTER_CONNECTED:
+ sendPhoto();
+ break;
+
+ case LLTwitterConnect::TWITTER_POSTED:
+ LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterPhotoPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLTwitterPhotoPanel::sendPhoto()
+{
+ // Get the status text
+ std::string status = mStatusTextBox->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();
+
+ // 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_PHOTO_LOCATION_URL;
+ }
+
+ // Add query parameters so Google Analytics can track incoming clicks!
+ slurl_string += DEFAULT_PHOTO_QUERY_PARAMETERS;
+
+ // Add it to the status (pretty crude, but we don't have a better option with photos)
+ if (status.empty())
+ status = slurl_string;
+ else
+ status = status + " " + slurl_string;
+ }
+
+ // Add the photo if required
+ bool add_photo = mPhotoCheckbox->getValue().asBoolean();
+ if (add_photo)
+ {
+ // Get the image
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ // Post to Twitter
+ LLTwitterConnect::instance().uploadPhoto(previewp->getFormattedImage(), status);
+ }
+ else
+ {
+ // Just post the status to Twitter
+ LLTwitterConnect::instance().updateStatus(status);
+ }
+
+ updateControls();
+}
+
+void LLTwitterPhotoPanel::clearAndClose()
+{
+ mStatusTextBox->setValue(DEFAULT_STATUS_TEXT);
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ if (mBigPreviewFloater)
+ {
+ mBigPreviewFloater->closeOnFloaterOwnerClosing(floater);
+ }
+ }
+}
+
+void LLTwitterPhotoPanel::updateStatusTextLength(BOOL restore_old_status_text)
+{
+ bool add_location = mLocationCheckbox->getValue().asBoolean();
+ bool add_photo = mPhotoCheckbox->getValue().asBoolean();
+
+ // Restrict the status text length to Twitter's character limit
+ LLTextEditor* status_text_box = dynamic_cast<LLTextEditor*>(mStatusTextBox);
+ if (status_text_box)
+ {
+ int max_status_length = 140 - (add_location ? 40 : 0) - (add_photo ? 40 : 0);
+ status_text_box->setMaxTextLength(max_status_length);
+ if (restore_old_status_text)
+ {
+ if (mOldStatusText.length() > status_text_box->getText().length() && status_text_box->getText() == mOldStatusText.substr(0, status_text_box->getText().length()))
+ {
+ status_text_box->setText(mOldStatusText);
+ }
+ if (mOldStatusText.length() <= max_status_length)
+ {
+ mOldStatusText = "";
+ }
+ }
+ if (status_text_box->getText().length() > max_status_length)
+ {
+ if (mOldStatusText.length() < status_text_box->getText().length() || status_text_box->getText() != mOldStatusText.substr(0, status_text_box->getText().length()))
+ {
+ mOldStatusText = status_text_box->getText();
+ }
+ status_text_box->setText(mOldStatusText.substr(0, max_status_length));
+ }
+
+ // Update the status character counter
+ int characters_remaining = max_status_length - status_text_box->getText().length();
+ mStatusCounterLabel->setValue(characters_remaining);
+ }
+
+}
+
+void LLTwitterPhotoPanel::updateControls()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
+
+ // *TODO: Separate maximum size for Web images from postcards
+ lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
+
+ updateResolution(FALSE);
+}
+
+void LLTwitterPhotoPanel::updateResolution(BOOL do_update)
+{
+ LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
+ LLComboBox* filterbox = static_cast<LLComboBox *>(mFilterComboBox);
+
+ 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];
+
+ // Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
+ std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
+
+ 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);
+ if (do_update)
+ {
+ previewp->updateSnapshot(TRUE);
+ updateControls();
+ }
+ }
+ // Get the old filter, compare to the current one "filter_name" and set if changed
+ std::string original_filter = previewp->getFilter();
+ if (original_filter != filter_name)
+ {
+ previewp->setFilter(filter_name);
+ if (do_update)
+ {
+ previewp->updateSnapshot(FALSE, TRUE);
+ updateControls();
+ }
+ }
+ }
+}
+
+void LLTwitterPhotoPanel::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* LLTwitterPhotoPanel::getRefreshBtn()
+{
+ return mRefreshBtn;
+}
+
+///////////////////////////
+//LLTwitterAccountPanel//////
+///////////////////////////
+
+LLTwitterAccountPanel::LLTwitterAccountPanel() :
+mAccountCaptionLabel(NULL),
+mAccountNameLabel(NULL),
+mPanelButtons(NULL),
+mConnectButton(NULL),
+mDisconnectButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLTwitterAccountPanel::onConnect, this));
+ mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLTwitterAccountPanel::onDisconnect, this));
+
+ setVisibleCallback(boost::bind(&LLTwitterAccountPanel::onVisibilityChange, this, _2));
+}
+
+BOOL LLTwitterAccountPanel::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 LLTwitterAccountPanel::draw()
+{
+ LLTwitterConnect::EConnectionState connection_state = LLTwitterConnect::instance().getConnectionState();
+
+ //Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress
+ bool disconnecting = connection_state == LLTwitterConnect::TWITTER_DISCONNECTING;
+ mDisconnectButton->setEnabled(!disconnecting);
+
+ //Disable the 'connect' button when a connection is in progress
+ bool connecting = connection_state == LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS;
+ mConnectButton->setEnabled(!connecting);
+
+ LLPanel::draw();
+}
+
+void LLTwitterAccountPanel::onVisibilityChange(BOOL visible)
+{
+ if(visible)
+ {
+ LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterAccountPanel");
+ LLEventPumps::instance().obtain("TwitterConnectState").listen("LLTwitterAccountPanel", boost::bind(&LLTwitterAccountPanel::onTwitterConnectStateChange, this, _1));
+
+ LLEventPumps::instance().obtain("TwitterConnectInfo").stopListening("LLTwitterAccountPanel");
+ LLEventPumps::instance().obtain("TwitterConnectInfo").listen("LLTwitterAccountPanel", boost::bind(&LLTwitterAccountPanel::onTwitterConnectInfoChange, this));
+
+ //Connected
+ if(LLTwitterConnect::instance().isConnected())
+ {
+ showConnectedLayout();
+ }
+ //Check if connected (show disconnected layout in meantime)
+ else
+ {
+ showDisconnectedLayout();
+ }
+ if ((LLTwitterConnect::instance().getConnectionState() == LLTwitterConnect::TWITTER_NOT_CONNECTED) ||
+ (LLTwitterConnect::instance().getConnectionState() == LLTwitterConnect::TWITTER_CONNECTION_FAILED))
+ {
+ LLTwitterConnect::instance().checkConnectionToTwitter();
+ }
+ }
+ else
+ {
+ LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterAccountPanel");
+ LLEventPumps::instance().obtain("TwitterConnectInfo").stopListening("LLTwitterAccountPanel");
+ }
+}
+
+bool LLTwitterAccountPanel::onTwitterConnectStateChange(const LLSD& data)
+{
+ if(LLTwitterConnect::instance().isConnected())
+ {
+ //In process of disconnecting so leave the layout as is
+ if(data.get("enum").asInteger() != LLTwitterConnect::TWITTER_DISCONNECTING)
+ {
+ showConnectedLayout();
+ }
+ }
+ else
+ {
+ showDisconnectedLayout();
+ }
+
+ return false;
+}
+
+bool LLTwitterAccountPanel::onTwitterConnectInfoChange()
+{
+ LLSD info = LLTwitterConnect::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 LLTwitterAccountPanel::showConnectButton()
+{
+ if(!mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(TRUE);
+ mDisconnectButton->setVisible(FALSE);
+ }
+}
+
+void LLTwitterAccountPanel::hideConnectButton()
+{
+ if(mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(FALSE);
+ mDisconnectButton->setVisible(TRUE);
+ }
+}
+
+void LLTwitterAccountPanel::showDisconnectedLayout()
+{
+ mAccountCaptionLabel->setText(getString("twitter_disconnected"));
+ mAccountNameLabel->setText(std::string(""));
+ showConnectButton();
+}
+
+void LLTwitterAccountPanel::showConnectedLayout()
+{
+ LLTwitterConnect::instance().loadTwitterInfo();
+
+ mAccountCaptionLabel->setText(getString("twitter_connected"));
+ hideConnectButton();
+}
+
+void LLTwitterAccountPanel::onConnect()
+{
+ LLTwitterConnect::instance().checkConnectionToTwitter(true);
+
+ //Clear only the twitter browser cookies so that the twitter login screen appears
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".twitter.com");
+}
+
+void LLTwitterAccountPanel::onDisconnect()
+{
+ LLTwitterConnect::instance().disconnectFromTwitter();
+
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".twitter.com");
+}
+
+////////////////////////
+//LLFloaterTwitter///////
+////////////////////////
+
+LLFloaterTwitter::LLFloaterTwitter(const LLSD& key) : LLFloater(key),
+ mTwitterPhotoPanel(NULL),
+ mStatusErrorText(NULL),
+ mStatusLoadingText(NULL),
+ mStatusLoadingIndicator(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterTwitter::onCancel, this));
+}
+
+void LLFloaterTwitter::onClose(bool app_quitting)
+{
+ LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+ if (big_preview_floater)
+ {
+ big_preview_floater->closeOnFloaterOwnerClosing(this);
+ }
+ LLFloater::onClose(app_quitting);
+}
+
+void LLFloaterTwitter::onCancel()
+{
+ LLFloaterBigPreview* big_preview_floater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
+ if (big_preview_floater)
+ {
+ big_preview_floater->closeOnFloaterOwnerClosing(this);
+ }
+ closeFloater();
+}
+
+BOOL LLFloaterTwitter::postBuild()
+{
+ // Keep tab of the Photo Panel
+ mTwitterPhotoPanel = static_cast<LLTwitterPhotoPanel*>(getChild<LLUICtrl>("panel_twitter_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();
+}
+
+void LLFloaterTwitter::showPhotoPanel()
+{
+ LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mTwitterPhotoPanel->getParent());
+ if (!parent)
+ {
+ llwarns << "Cannot find panel container" << llendl;
+ return;
+ }
+
+ parent->selectTabPanel(mTwitterPhotoPanel);
+}
+
+void LLFloaterTwitter::draw()
+{
+ if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
+ {
+ mStatusErrorText->setVisible(false);
+ mStatusLoadingText->setVisible(false);
+ mStatusLoadingIndicator->setVisible(false);
+ LLTwitterConnect::EConnectionState connection_state = LLTwitterConnect::instance().getConnectionState();
+ std::string status_text;
+
+ switch (connection_state)
+ {
+ case LLTwitterConnect::TWITTER_NOT_CONNECTED:
+ // No status displayed when first opening the panel and no connection done
+ case LLTwitterConnect::TWITTER_CONNECTED:
+ // When successfully connected, no message is displayed
+ case LLTwitterConnect::TWITTER_POSTED:
+ // No success message to show since we actually close the floater after successful posting completion
+ break;
+ case LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS:
+ // Connection loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterConnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLTwitterConnect::TWITTER_POSTING:
+ // Posting indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterPosting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLTwitterConnect::TWITTER_CONNECTION_FAILED:
+ // Error connecting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterErrorConnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLTwitterConnect::TWITTER_POST_FAILED:
+ // Error posting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterErrorPosting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLTwitterConnect::TWITTER_DISCONNECTING:
+ // Disconnecting loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterDisconnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLTwitterConnect::TWITTER_DISCONNECT_FAILED:
+ // Error disconnecting from the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterErrorDisconnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ }
+ }
+ LLFloater::draw();
+}
+
diff --git a/indra/newview/llfloatertwitter.h b/indra/newview/llfloatertwitter.h
new file mode 100644
index 0000000000..f07ec2ca2f
--- /dev/null
+++ b/indra/newview/llfloatertwitter.h
@@ -0,0 +1,139 @@
+/**
+* @file llfloatertwitter.h
+* @brief Header file for llfloatertwitter
+* @author cho@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_LLFLOATERTWITTER_H
+#define LL_LLFLOATERTWITTER_H
+
+#include "llfloater.h"
+#include "lltextbox.h"
+#include "llviewertexture.h"
+
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLSnapshotLivePreview;
+class LLFloaterBigPreview;
+
+class LLTwitterPhotoPanel : public LLPanel
+{
+public:
+ LLTwitterPhotoPanel();
+ ~LLTwitterPhotoPanel();
+
+ BOOL postBuild();
+ void draw();
+
+ LLSnapshotLivePreview* getPreviewView();
+ void onVisibilityChange(BOOL new_visibility);
+ void onAddLocationToggled();
+ void onAddPhotoToggled();
+ void onClickBigPreview();
+ void onClickNewSnapshot();
+ void onSend();
+ S32 notify(const LLSD& info);
+ bool onTwitterConnectStateChange(const LLSD& data);
+
+ void sendPhoto();
+ void clearAndClose();
+
+ void updateStatusTextLength(BOOL restore_old_status_text);
+ void updateControls();
+ void updateResolution(BOOL do_update);
+ void checkAspectRatio(S32 index);
+ LLUICtrl* getRefreshBtn();
+
+private:
+ bool isPreviewVisible();
+ void attachPreview();
+
+ LLHandle<LLView> mPreviewHandle;
+
+ LLUICtrl * mSnapshotPanel;
+ LLUICtrl * mResolutionComboBox;
+ LLUICtrl * mFilterComboBox;
+ LLUICtrl * mRefreshBtn;
+ LLUICtrl * mWorkingLabel;
+ LLUICtrl * mThumbnailPlaceholder;
+ LLUICtrl * mStatusCounterLabel;
+ LLUICtrl * mStatusTextBox;
+ LLUICtrl * mLocationCheckbox;
+ LLUICtrl * mPhotoCheckbox;
+ LLUICtrl * mPostButton;
+ LLUICtrl * mCancelButton;
+ LLButton * mBtnPreview;
+
+ LLFloaterBigPreview * mBigPreviewFloater;
+
+ std::string mOldStatusText;
+};
+
+class LLTwitterAccountPanel : public LLPanel
+{
+public:
+ LLTwitterAccountPanel();
+ BOOL postBuild();
+ void draw();
+
+private:
+ void onVisibilityChange(BOOL new_visibility);
+ bool onTwitterConnectStateChange(const LLSD& data);
+ bool onTwitterConnectInfoChange();
+ 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 LLFloaterTwitter : public LLFloater
+{
+public:
+ LLFloaterTwitter(const LLSD& key);
+ BOOL postBuild();
+ void draw();
+ void onClose(bool app_quitting);
+ void onCancel();
+
+ void showPhotoPanel();
+
+private:
+ LLTwitterPhotoPanel* mTwitterPhotoPanel;
+ LLTextBox* mStatusErrorText;
+ LLTextBox* mStatusLoadingText;
+ LLUICtrl* mStatusLoadingIndicator;
+};
+
+#endif // LL_LLFLOATERTWITTER_H
+
diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp
index 96a70d9345..bfc36a6bfb 100755
--- a/indra/newview/llfloateruipreview.cpp
+++ b/indra/newview/llfloateruipreview.cpp
@@ -1020,12 +1020,11 @@ void LLFloaterUIPreview::onClickEditFloater()
// Respond to button click to browse for an executable with which to edit XML files
void LLFloaterUIPreview::onClickBrowseForEditor()
{
- // create load dialog box
- LLFilePicker::ELoadFilter type = (LLFilePicker::ELoadFilter)((intptr_t)((void*)LLFilePicker::FFLOAD_ALL)); // nothing for *.exe so just use all
+ // Let the user choose an executable through the file picker dialog box
LLFilePicker& picker = LLFilePicker::instance();
- if (!picker.getOpenFile(type)) // user cancelled -- do nothing
+ if (!picker.getOpenFile(LLFilePicker::FFLOAD_EXE))
{
- return;
+ return; // user cancelled -- do nothing
}
// put the selected path into text field
diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp
index f72adbb880..bbfd855bc8 100755
--- a/indra/newview/llfloaterwebcontent.cpp
+++ b/indra/newview/llfloaterwebcontent.cpp
@@ -31,6 +31,8 @@
#include "llfloaterreg.h"
#include "llhttpconstants.h"
#include "llfacebookconnect.h"
+#include "llflickrconnect.h"
+#include "lltwitterconnect.h"
#include "lllayoutstack.h"
#include "llpluginclassmedia.h"
#include "llprogressbar.h"
@@ -52,7 +54,8 @@ LLFloaterWebContent::_Params::_Params()
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)
+ show_page_title("show_page_title", true),
+ clean_browser("clean_browser", false)
{}
LLFloaterWebContent::LLFloaterWebContent( const Params& params )
@@ -243,7 +246,7 @@ void LLFloaterWebContent::open_media(const Params& p)
LLViewerMedia::proxyWindowOpened(p.target(), p.id());
mWebBrowser->setHomePageUrl(p.url, HTTP_CONTENT_TEXT_HTML);
mWebBrowser->setTarget(p.target);
- mWebBrowser->navigateTo(p.url, HTTP_CONTENT_TEXT_HTML);
+ mWebBrowser->navigateTo(p.url, HTTP_CONTENT_TEXT_HTML, p.clean_browser);
set_current_url(p.url);
@@ -254,11 +257,6 @@ void LLFloaterWebContent::open_media(const Params& p)
getChildView("address")->setEnabled(address_entry_enabled);
getChildView("popexternal")->setEnabled(address_entry_enabled);
- if (!address_entry_enabled)
- {
- mWebBrowser->setFocus(TRUE);
- }
-
if (!p.show_chrome)
{
setResizeLimits(100, 100);
@@ -304,7 +302,24 @@ void LLFloaterWebContent::onClose(bool app_quitting)
LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
}
}
-
+ // Same with Flickr
+ LLFloater* flickr_web = LLFloaterReg::getInstance("flickr_web");
+ if (flickr_web == this)
+ {
+ if (!LLFlickrConnect::instance().isConnected())
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
+ }
+ }
+ // And Twitter
+ LLFloater* twitter_web = LLFloaterReg::getInstance("twitter_web");
+ if (twitter_web == this)
+ {
+ if (!LLTwitterConnect::instance().isConnected())
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
+ }
+ }
LLViewerMedia::proxyWindowClosed(mUUID);
destroy();
}
@@ -440,9 +455,6 @@ void LLFloaterWebContent::set_current_url(const std::string& url)
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);
}
}
diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h
index ad56eec859..f5faa39e3d 100755
--- a/indra/newview/llfloaterwebcontent.h
+++ b/indra/newview/llfloaterwebcontent.h
@@ -57,7 +57,8 @@ public:
allow_address_entry,
allow_back_forward_navigation,
trusted_content,
- show_page_title;
+ show_page_title,
+ clean_browser;
Optional<LLRect> preferred_media_size;
_Params();
diff --git a/indra/newview/llimagefiltersmanager.cpp b/indra/newview/llimagefiltersmanager.cpp
new file mode 100644
index 0000000000..ee6b39efac
--- /dev/null
+++ b/indra/newview/llimagefiltersmanager.cpp
@@ -0,0 +1,115 @@
+/**
+ * @file llimagefiltersmanager.cpp
+ * @brief Load image filters list and retrieve their path. Mostly used for Flickr UI at the moment.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, 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 "llimagefiltersmanager.h"
+
+#include "lldiriterator.h"
+#include "lltrans.h"
+
+std::string get_sys_dir()
+{
+ return gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "filters", "");
+}
+
+//---------------------------------------------------------------------------
+// LLImageFiltersManager
+//---------------------------------------------------------------------------
+
+LLImageFiltersManager::LLImageFiltersManager()
+{
+}
+
+LLImageFiltersManager::~LLImageFiltersManager()
+{
+}
+
+// virtual static
+void LLImageFiltersManager::initSingleton()
+{
+ loadAllFilters();
+}
+
+void LLImageFiltersManager::loadAllFilters()
+{
+ // Load system (coming out of the box) filters
+ loadFiltersFromDir(get_sys_dir());
+}
+
+void LLImageFiltersManager::loadFiltersFromDir(const std::string& dir)
+{
+ mFiltersList.clear();
+
+ LLDirIterator dir_iter(dir, "*.xml");
+ while (1)
+ {
+ std::string file_name;
+ if (!dir_iter.next(file_name))
+ {
+ break; // no more files
+ }
+
+ // Get the ".xml" out of the file name to get the filter name. That's the one known in strings.xml
+ std::string filter_name_untranslated = file_name.substr(0,file_name.length()-4);
+
+ // Get the localized name for the filter
+ std::string filter_name_translated;
+ bool translated = LLTrans::findString(filter_name_translated, filter_name_untranslated);
+ std::string filter_name = (translated ? filter_name_translated: filter_name_untranslated);
+
+ // Store the filter in the list with its associated file name
+ mFiltersList[filter_name] = file_name;
+ }
+}
+
+// Note : That method is a bit heavy handed but the list of filters is always small (10 or so)
+// and that method is typically called only once when building UI widgets.
+const std::vector<std::string> LLImageFiltersManager::getFiltersList() const
+{
+ std::vector<std::string> filter_list;
+ for (std::map<std::string,std::string>::const_iterator it = mFiltersList.begin(); it != mFiltersList.end(); ++it)
+ {
+ filter_list.push_back(it->first);
+ }
+ return filter_list;
+}
+
+std::string LLImageFiltersManager::getFilterPath(const std::string& filter_name)
+{
+ std::string path = "";
+ std::map<std::string,std::string>::const_iterator it = mFiltersList.find(filter_name);
+ if (it != mFiltersList.end())
+ {
+ // Get the file name for that filter and build the complete path
+ std::string file = it->second;
+ std::string dir = get_sys_dir();
+ path = gDirUtilp->add(dir, file);
+ }
+ return path;
+}
+
+//============================================================================
diff --git a/indra/newview/llimagefiltersmanager.h b/indra/newview/llimagefiltersmanager.h
new file mode 100644
index 0000000000..4751933065
--- /dev/null
+++ b/indra/newview/llimagefiltersmanager.h
@@ -0,0 +1,55 @@
+/**
+ * @file llimagefiltersmanager.h
+ * @brief Load image filters list and retrieve their path. Mostly used for Flickr UI at the moment.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, 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_LLIMAGEFILTERSMANAGER_H
+#define LL_LLIMAGEFILTERSMANAGER_H
+
+#include "llsingleton.h"
+
+//============================================================================
+// LLImageFiltersManager class
+
+class LLImageFiltersManager : public LLSingleton<LLImageFiltersManager>
+{
+ LOG_CLASS(LLImageFiltersManager);
+public:
+ const std::vector<std::string> getFiltersList() const;
+ std::string getFilterPath(const std::string& filter_name);
+
+private:
+ void loadAllFilters();
+ void loadFiltersFromDir(const std::string& dir);
+
+ friend class LLSingleton<LLImageFiltersManager>;
+ /*virtual*/ void initSingleton();
+ LLImageFiltersManager();
+ ~LLImageFiltersManager();
+
+ // List of filters : first is the user friendly localized name, second is the xml file name
+ std::map<std::string,std::string> mFiltersList;
+};
+
+#endif // LL_LLIMAGEFILTERSMANAGER_H
diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp
index 0178512cb5..086bc1c186 100755
--- a/indra/newview/llmediactrl.cpp
+++ b/indra/newview/llmediactrl.cpp
@@ -544,7 +544,7 @@ void LLMediaCtrl::clearCache()
////////////////////////////////////////////////////////////////////////////////
//
-void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type)
+void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type, bool clean_browser)
{
// don't browse to anything that starts with secondlife:// or sl://
const std::string protocol1 = "secondlife://";
@@ -561,7 +561,7 @@ void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type)
{
mCurrentNavUrl = url_in;
mMediaSource->setSize(mTextureWidth, mTextureHeight);
- mMediaSource->navigateTo(url_in, mime_type, mime_type.empty());
+ mMediaSource->navigateTo(url_in, mime_type, mime_type.empty(), false, clean_browser);
}
}
diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h
index b07eb356ae..b6ed0f3fab 100755
--- a/indra/newview/llmediactrl.h
+++ b/indra/newview/llmediactrl.h
@@ -95,7 +95,7 @@ public:
virtual BOOL handleToolTip(S32 x, S32 y, MASK mask);
// navigation
- void navigateTo( std::string url_in, std::string mime_type = "");
+ void navigateTo( std::string url_in, std::string mime_type = "", bool clean_browser = false);
void navigateBack();
void navigateHome();
void navigateForward();
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index 9d9fb4040d..5977d558d3 100755
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -505,7 +505,7 @@ public:
LLPanelPeople::LLPanelPeople()
: LLPanel(),
- mTryToConnectToFbc(true),
+ mTryToConnectToFacebook(true),
mTabContainer(NULL),
mOnlineFriendList(NULL),
mAllFriendList(NULL),
@@ -865,10 +865,10 @@ void LLPanelPeople::updateFacebookList(bool visible)
{
LLFacebookConnect::instance().loadFacebookFriends();
}
- else if(mTryToConnectToFbc)
+ else if(mTryToConnectToFacebook)
{
LLFacebookConnect::instance().checkConnectionToFacebook();
- mTryToConnectToFbc = false;
+ mTryToConnectToFacebook = false;
}
updateSuggestedFriendList();
diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h
index 34dc3d9ab7..55eaf74f74 100755
--- a/indra/newview/llpanelpeople.h
+++ b/indra/newview/llpanelpeople.h
@@ -57,7 +57,7 @@ public:
// when voice is available
/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
- bool mTryToConnectToFbc;
+ bool mTryToConnectToFacebook;
// internals
class Updater;
diff --git a/indra/newview/llpanelsnapshotoptions.cpp b/indra/newview/llpanelsnapshotoptions.cpp
index 21f95f38c0..743ef3e329 100755
--- a/indra/newview/llpanelsnapshotoptions.cpp
+++ b/indra/newview/llpanelsnapshotoptions.cpp
@@ -31,6 +31,10 @@
#include "llsidetraypanelcontainer.h"
#include "llfloatersnapshot.h" // FIXME: create a snapshot model
+#include "llfloaterreg.h"
+#include "llfloaterfacebook.h"
+#include "llfloaterflickr.h"
+#include "llfloatertwitter.h"
/**
* Provides several ways to save a snapshot.
@@ -44,6 +48,7 @@ class LLPanelSnapshotOptions
public:
LLPanelSnapshotOptions();
~LLPanelSnapshotOptions();
+ /*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void onEconomyDataChange() { updateUploadCost(); }
@@ -54,6 +59,9 @@ private:
void onSaveToEmail();
void onSaveToInventory();
void onSaveToComputer();
+ void onSendToFacebook();
+ void onSendToTwitter();
+ void onSendToFlickr();
};
static LLPanelInjector<LLPanelSnapshotOptions> panel_class("llpanelsnapshotoptions");
@@ -74,6 +82,19 @@ LLPanelSnapshotOptions::~LLPanelSnapshotOptions()
}
// virtual
+BOOL LLPanelSnapshotOptions::postBuild()
+{
+ LLTextBox* sendToFacebookTextBox = getChild<LLTextBox>("send_to_facebook_textbox");
+ sendToFacebookTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToFacebook, this));
+ LLTextBox* sendToTwitterTextBox = getChild<LLTextBox>("send_to_twitter_textbox");
+ sendToTwitterTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToTwitter, this));
+ LLTextBox* sendToFlickrTextBox = getChild<LLTextBox>("send_to_flickr_textbox");
+ sendToFlickrTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this));
+
+ return LLPanel::postBuild();
+}
+
+// virtual
void LLPanelSnapshotOptions::onOpen(const LLSD& key)
{
updateUploadCost();
@@ -118,3 +139,39 @@ void LLPanelSnapshotOptions::onSaveToComputer()
{
openPanel("panel_snapshot_local");
}
+
+void LLPanelSnapshotOptions::onSendToFacebook()
+{
+ LLFloaterReg::hideInstance("snapshot");
+
+ LLFloaterFacebook* facebook_floater = dynamic_cast<LLFloaterFacebook*>(LLFloaterReg::getInstance("facebook"));
+ if (facebook_floater)
+ {
+ facebook_floater->showPhotoPanel();
+ }
+ LLFloaterReg::showInstance("facebook");
+}
+
+void LLPanelSnapshotOptions::onSendToTwitter()
+{
+ LLFloaterReg::hideInstance("snapshot");
+
+ LLFloaterTwitter* twitter_floater = dynamic_cast<LLFloaterTwitter*>(LLFloaterReg::getInstance("twitter"));
+ if (twitter_floater)
+ {
+ twitter_floater->showPhotoPanel();
+ }
+ LLFloaterReg::showInstance("twitter");
+}
+
+void LLPanelSnapshotOptions::onSendToFlickr()
+{
+ LLFloaterReg::hideInstance("snapshot");
+
+ LLFloaterFlickr* flickr_floater = dynamic_cast<LLFloaterFlickr*>(LLFloaterReg::getInstance("flickr"));
+ if (flickr_floater)
+ {
+ flickr_floater->showPhotoPanel();
+ }
+ LLFloaterReg::showInstance("flickr");
+}
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
index d8ad071ba6..a1965af4a5 100644
--- a/indra/newview/llsnapshotlivepreview.cpp
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -34,7 +34,11 @@
#include "lleconomy.h"
#include "llfloaterperms.h"
#include "llfloaterreg.h"
-#include "llfloatersocial.h"
+#include "llfloaterfacebook.h"
+#include "llfloaterflickr.h"
+#include "llfloatertwitter.h"
+#include "llimagefilter.h"
+#include "llimagefiltersmanager.h"
#include "llimagebmp.h"
#include "llimagej2c.h"
#include "llimagejpeg.h"
@@ -70,9 +74,11 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param
mColor(1.f, 0.f, 0.f, 0.5f),
mCurImageIndex(0),
mPreviewImage(NULL),
- mThumbnailImage(NULL) ,
+ mThumbnailImage(NULL) ,
+ mBigThumbnailImage(NULL) ,
mThumbnailWidth(0),
mThumbnailHeight(0),
+ mThumbnailSubsampled(FALSE),
mPreviewImageEncoded(NULL),
mFormattedImage(NULL),
mShineCountdown(0),
@@ -86,7 +92,11 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param
mCameraPos(LLViewerCamera::getInstance()->getOrigin()),
mCameraRot(LLViewerCamera::getInstance()->getQuaternion()),
mSnapshotActive(FALSE),
- mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR)
+ mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR),
+ mFilterName(""),
+ mAllowRenderUI(TRUE),
+ mAllowFullScreenPreview(TRUE),
+ mViewContainer(NULL)
{
setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality"));
mSnapshotDelayTimer.setTimerExpirySec(0.0f);
@@ -105,6 +115,7 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param
mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;
mThumbnailUpdateLock = FALSE ;
mThumbnailUpToDate = FALSE ;
+ mBigThumbnailUpToDate = FALSE ;
}
LLSnapshotLivePreview::~LLSnapshotLivePreview()
@@ -120,14 +131,7 @@ LLSnapshotLivePreview::~LLSnapshotLivePreview()
void LLSnapshotLivePreview::setMaxImageSize(S32 size)
{
- if(size < MAX_SNAPSHOT_IMAGE_SIZE)
- {
- mMaxImageSize = size;
- }
- else
- {
- mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ;
- }
+ mMaxImageSize = llmin(size,(S32)(MAX_SNAPSHOT_IMAGE_SIZE));
}
LLViewerTexture* LLSnapshotLivePreview::getCurrentImage()
@@ -135,97 +139,92 @@ 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() ;
+ // mKeepAspectRatio) == gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ return (mKeepAspectRatio ? ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()) : ((F32)getWidth()) / ((F32)getHeight()));
}
-void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
+void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay)
{
- // Invalidate current image.
- LL_DEBUGS() << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << LL_ENDL;
- 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();
+ lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl;
// Update snapshot if requested.
if (new_snapshot)
{
+ 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();
mSnapshotDelayTimer.start();
mSnapshotDelayTimer.setTimerExpirySec(delay);
- LLFloaterSnapshot::preUpdate();
- LLFloaterSocial::preUpdate();
+
+ // Tell the floater container that the snapshot is in the process of updating itself
+ if (mViewContainer)
+ {
+ mViewContainer->notify(LLSD().with("snapshot-updating", true));
+ }
}
// Update thumbnail if requested.
- if(new_thumbnail)
+ if (new_thumbnail)
{
mThumbnailUpToDate = FALSE ;
+ mBigThumbnailUpToDate = FALSE;
}
}
-void LLSnapshotLivePreview::setSnapshotQuality(S32 quality)
+// Return true if the quality has been changed, false otherwise
+bool LLSnapshotLivePreview::setSnapshotQuality(S32 quality, bool set_by_user)
{
llclamp(quality, 0, 100);
if (quality != mSnapshotQuality)
{
mSnapshotQuality = quality;
- gSavedSettings.setS32("SnapshotQuality", quality);
- mSnapshotUpToDate = FALSE;
+ if (set_by_user)
+ {
+ gSavedSettings.setS32("SnapshotQuality", quality);
+ }
+ mFormattedImage = NULL; // Invalidate the already formatted image if any
+ return true;
}
+ return false;
}
void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y)
@@ -459,68 +458,57 @@ void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_pare
if (old_rect.getWidth() != width || old_rect.getHeight() != height)
{
LL_DEBUGS() << "window reshaped, updating thumbnail" << LL_ENDL;
- updateSnapshot(FALSE, TRUE);
+ updateSnapshot(TRUE);
}
}
BOOL LLSnapshotLivePreview::setThumbnailImageSize()
{
- if(getWidth() < 10 || getHeight() < 10)
+ if (getWidth() < 10 || getHeight() < 10)
{
return FALSE ;
}
- S32 window_width = gViewerWindow->getWindowWidthRaw() ;
- S32 window_height = gViewerWindow->getWindowHeightRaw() ;
+ S32 width = (mThumbnailSubsampled ? mPreviewImage->getWidth() : gViewerWindow->getWindowWidthRaw());
+ S32 height = (mThumbnailSubsampled ? mPreviewImage->getHeight() : gViewerWindow->getWindowHeightRaw()) ;
- F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height);
+ F32 aspect_ratio = ((F32)width) / ((F32)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();
+ S32 max_width = mThumbnailPlaceholderRect.getWidth();
+ S32 max_height = mThumbnailPlaceholderRect.getHeight();
- if (window_aspect_ratio > (F32)max_width / max_height)
+ if (aspect_ratio > (F32)max_width / (F32)max_height)
{
// image too wide, shrink to width
mThumbnailWidth = max_width;
- mThumbnailHeight = llround((F32)max_width / window_aspect_ratio);
+ mThumbnailHeight = llround((F32)max_width / aspect_ratio);
}
else
{
// image too tall, shrink to height
mThumbnailHeight = max_height;
- mThumbnailWidth = llround((F32)max_height * window_aspect_ratio);
+ mThumbnailWidth = llround((F32)max_height * aspect_ratio);
}
-
- if(mThumbnailWidth > window_width || mThumbnailHeight > window_height)
+
+ if (mThumbnailWidth > width || mThumbnailHeight > height)
{
return FALSE ;//if the window is too small, ignore thumbnail updating.
}
S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ;
- if(!mKeepAspectRatio)
+ 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) ;
- //}
+ F32 ratio_x = (F32)getWidth() / width ;
+ F32 ratio_y = (F32)getHeight() / height ;
+
+ if (ratio_x > ratio_y)
+ {
+ top = (S32)(top * ratio_y / ratio_x) ;
+ }
+ else
+ {
+ right = (S32)(right * ratio_x / ratio_y) ;
+ }
left = (S32)((mThumbnailWidth - right) * 0.5f) ;
bottom = (S32)((mThumbnailHeight - top) * 0.5f) ;
top += bottom ;
@@ -556,25 +544,62 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
return ;
}
+ // Invalidate the big thumbnail when we regenerate the small one
+ mBigThumbnailUpToDate = FALSE;
+
if(mThumbnailImage)
{
resetThumbnailImage() ;
}
LLPointer<LLImageRaw> raw = new LLImageRaw;
- if(!gViewerWindow->thumbnailSnapshot(raw,
- mThumbnailWidth, mThumbnailHeight,
- gSavedSettings.getBOOL("RenderUIInSnapshot"),
- FALSE,
- mSnapshotBufferType) )
+
+ if (mThumbnailSubsampled)
+ {
+ // The thumbnail is be a subsampled version of the preview (used in SL Share previews, i.e. Flickr, Twitter, Facebook)
+ raw->resize( mPreviewImage->getWidth(),
+ mPreviewImage->getHeight(),
+ mPreviewImage->getComponents());
+ raw->copy(mPreviewImage);
+ // Scale to the thumbnail size
+ if (!raw->scale(mThumbnailWidth, mThumbnailHeight))
+ {
+ raw = NULL ;
+ }
+ }
+ else
+ {
+ // The thumbnail is a screen view with screen grab positioning preview
+ if(!gViewerWindow->thumbnailSnapshot(raw,
+ mThumbnailWidth, mThumbnailHeight,
+ mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE,
+ mSnapshotBufferType) )
+ {
+ raw = NULL ;
+ }
+ }
+
+ if (raw)
{
- raw = NULL ;
- }
-
- if(raw)
- {
- raw->expandToPowerOfTwo();
- mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
+ // Filter the thumbnail
+ // Note: filtering needs to be done *before* the scaling to power of 2 or the effect is distorted
+ if (getFilter() != "")
+ {
+ std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter());
+ if (filter_path != "")
+ {
+ LLImageFilter filter(filter_path);
+ filter.executeFilter(raw);
+ }
+ else
+ {
+ llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
+ }
+ }
+ // Scale to a power of 2 so it can be mapped to a texture
+ raw->expandToPowerOfTwo();
+ mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
mThumbnailUpToDate = TRUE ;
}
@@ -582,6 +607,52 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update)
mThumbnailUpdateLock = FALSE ;
}
+LLViewerTexture* LLSnapshotLivePreview::getBigThumbnailImage()
+{
+ if (mThumbnailUpdateLock) //in the process of updating
+ {
+ return NULL;
+ }
+ if (mBigThumbnailUpToDate && mBigThumbnailImage)//already updated
+ {
+ return mBigThumbnailImage;
+ }
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+
+ if (raw)
+ {
+ // The big thumbnail is a new filtered version of the preview (used in SL Share previews, i.e. Flickr, Twitter, Facebook)
+ mBigThumbnailWidth = mPreviewImage->getWidth();
+ mBigThumbnailHeight = mPreviewImage->getHeight();
+ raw->resize( mBigThumbnailWidth,
+ mBigThumbnailHeight,
+ mPreviewImage->getComponents());
+ raw->copy(mPreviewImage);
+
+ // Filter
+ // Note: filtering needs to be done *before* the scaling to power of 2 or the effect is distorted
+ if (getFilter() != "")
+ {
+ std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter());
+ if (filter_path != "")
+ {
+ LLImageFilter filter(filter_path);
+ filter.executeFilter(raw);
+ }
+ else
+ {
+ llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
+ }
+ }
+ // Scale to a power of 2 so it can be mapped to a texture
+ raw->expandToPowerOfTwo();
+ mBigThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE);
+ mBigThumbnailUpToDate = TRUE ;
+ }
+
+ return mBigThumbnailImage ;
+}
// 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.
@@ -598,7 +669,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
// 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") &&
+ if (gSavedSettings.getBOOL("FreezeTime") && previewp->mAllowFullScreenPreview &&
(new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))
{
previewp->mCameraPos = new_camera_pos;
@@ -616,157 +687,256 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
previewp->mSnapshotActive =
(previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired())
&& !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active
- if ( ! previewp->mSnapshotActive)
+ if (!previewp->mSnapshotActive && previewp->getSnapshotUpToDate() && previewp->getThumbnailUpToDate())
{
return FALSE;
}
// time to produce a snapshot
- previewp->setThumbnailImageSize();
-
- LL_DEBUGS() << "producing snapshot" << LL_ENDL;
- if (!previewp->mPreviewImage)
+ if(!previewp->getSnapshotUpToDate())
+ {
+ lldebugs << "producing snapshot" << llendl;
+ if (!previewp->mPreviewImage)
+ {
+ previewp->mPreviewImage = new LLImageRaw;
+ }
+
+ previewp->setVisible(FALSE);
+ previewp->setEnabled(FALSE);
+
+ previewp->getWindow()->incBusyCount();
+ previewp->setImageScaled(FALSE);
+
+ // grab the raw image
+ if (gViewerWindow->rawSnapshot(
+ previewp->mPreviewImage,
+ previewp->getWidth(),
+ previewp->getHeight(),
+ previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"),
+ previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE,
+ previewp->mAllowRenderUI && gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE,
+ previewp->mSnapshotBufferType,
+ previewp->getMaxImageSize()))
+ {
+ // Invalidate/delete any existing encoded image
+ previewp->mPreviewImageEncoded = NULL;
+ // Invalidate/delete any existing formatted image
+ previewp->mFormattedImage = NULL;
+ // Update the data size
+ previewp->estimateDataSize();
+
+ // Full size preview is set: get the decoded image result and save it for animation
+ if (gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview)
+ {
+ // Get the decoded version of the formatted image
+ previewp->getEncodedImage();
+
+ // We need to scale that a bit for display...
+ 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);
+ curr_preview_image->setFilteringOption(previewp->getSnapshotType() == SNAPSHOT_TEXTURE ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT);
+ curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP);
+
+ previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal();
+ previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
+ }
+ }
+ // The snapshot is updated now...
+ previewp->mSnapshotUpToDate = TRUE;
+
+ // We need to update the thumbnail though
+ previewp->setThumbnailImageSize();
+ previewp->generateThumbnailImage(TRUE) ;
+ }
+ previewp->getWindow()->decBusyCount();
+ previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode
+ previewp->mSnapshotDelayTimer.stop();
+ previewp->mSnapshotActive = FALSE;
+ lldebugs << "done creating snapshot" << llendl;
+ }
+
+ if (!previewp->getThumbnailUpToDate())
{
- previewp->mPreviewImage = new LLImageRaw;
+ previewp->generateThumbnailImage() ;
}
+
+ // Tell the floater container that the snapshot is updated now
+ if (previewp->mViewContainer)
+ {
+ previewp->mViewContainer->notify(LLSD().with("snapshot-updated", true));
+ }
- if (!previewp->mPreviewImageEncoded)
- {
- previewp->mPreviewImageEncoded = new LLImageRaw;
- }
+ return TRUE;
+}
- 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());
+S32 LLSnapshotLivePreview::getEncodedImageWidth() const
+{
+ S32 width = getWidth();
+ if (getSnapshotType() == SNAPSHOT_TEXTURE)
+ {
+ width = LLImageRaw::biasedDimToPowerOfTwo(width,MAX_TEXTURE_SIZE);
+ }
+ return width;
+}
+S32 LLSnapshotLivePreview::getEncodedImageHeight() const
+{
+ S32 height = getHeight();
+ if (getSnapshotType() == SNAPSHOT_TEXTURE)
+ {
+ height = LLImageRaw::biasedDimToPowerOfTwo(height,MAX_TEXTURE_SIZE);
+ }
+ return height;
+}
- if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE)
+LLPointer<LLImageRaw> LLSnapshotLivePreview::getEncodedImage()
+{
+ if (!mPreviewImageEncoded)
+ {
+ mPreviewImageEncoded = new LLImageRaw;
+
+ mPreviewImageEncoded->resize(
+ mPreviewImage->getWidth(),
+ mPreviewImage->getHeight(),
+ mPreviewImage->getComponents());
+
+ if (getSnapshotType() == SNAPSHOT_TEXTURE)
{
+ // We don't store the intermediate formatted image in mFormattedImage in the J2C case
LL_DEBUGS() << "Encoding new image of format J2C" << LL_ENDL;
LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ // Copy the preview
LLPointer<LLImageRaw> scaled = new LLImageRaw(
- previewp->mPreviewImage->getData(),
- previewp->mPreviewImage->getWidth(),
- previewp->mPreviewImage->getHeight(),
- previewp->mPreviewImage->getComponents());
-
+ mPreviewImage->getData(),
+ mPreviewImage->getWidth(),
+ mPreviewImage->getHeight(),
+ mPreviewImage->getComponents());
+ // Scale it as required by J2C
scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE);
- previewp->setImageScaled(TRUE);
+ setImageScaled(TRUE);
+ // Compress to J2C
if (formatted->encode(scaled, 0.f))
{
- previewp->mDataSize = formatted->getDataSize();
- formatted->decode(previewp->mPreviewImageEncoded, 0);
+ // We can update the data size precisely at that point
+ mDataSize = formatted->getDataSize();
+ // Decompress back
+ formatted->decode(mPreviewImageEncoded, 0);
}
}
else
{
- // delete any existing image
- previewp->mFormattedImage = NULL;
- // now create the new one of the appropriate format.
- LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat();
- LL_DEBUGS() << "Encoding new image of format " << format << LL_ENDL;
-
- 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
+ // Update mFormattedImage if necessary
+ getFormattedImage();
+ if (getSnapshotFormat() == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP)
+ {
+ // BMP hack : copy instead of decode otherwise decode will crash.
+ mPreviewImageEncoded->copy(mPreviewImage);
+ }
+ else
+ {
+ // Decode back
+ mFormattedImage->decode(mPreviewImageEncoded, 0);
+ }
}
}
- previewp->getWindow()->decBusyCount();
- // only show fullscreen preview when in freeze frame mode
- previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame"));
- previewp->mSnapshotDelayTimer.stop();
- previewp->mSnapshotActive = FALSE;
+ return mPreviewImageEncoded;
+}
- if(!previewp->getThumbnailUpToDate())
- {
- previewp->generateThumbnailImage() ;
- }
- LL_DEBUGS() << "done creating snapshot" << LL_ENDL;
- LLFloaterSnapshot::postUpdate();
- LLFloaterSocial::postUpdate();
+// We actually estimate the data size so that we do not require actual compression when showing the preview
+// Note : whenever formatted image is computed, mDataSize will be updated to reflect the true size
+void LLSnapshotLivePreview::estimateDataSize()
+{
+ // Compression ratio
+ F32 ratio = 1.0;
+
+ if (getSnapshotType() == SNAPSHOT_TEXTURE)
+ {
+ ratio = 8.0; // This is what we shoot for when compressing to J2C
+ }
+ else
+ {
+ LLFloaterSnapshot::ESnapshotFormat format = getSnapshotFormat();
+ switch (format)
+ {
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
+ ratio = 3.0; // Average observed PNG compression ratio
+ break;
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
+ // Observed from JPG compression tests
+ ratio = (110 - mSnapshotQuality) / 2;
+ break;
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
+ ratio = 1.0; // No compression with BMP
+ break;
+ }
+ }
+ mDataSize = (S32)((F32)mPreviewImage->getDataSize() / ratio);
+}
- return TRUE;
+LLPointer<LLImageFormatted> LLSnapshotLivePreview::getFormattedImage()
+{
+ if (!mFormattedImage)
+ {
+ // Apply the filter to mPreviewImage
+ if (getFilter() != "")
+ {
+ std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter());
+ if (filter_path != "")
+ {
+ LLImageFilter filter(filter_path);
+ filter.executeFilter(mPreviewImage);
+ }
+ else
+ {
+ llwarns << "Couldn't find a path to the following filter : " << getFilter() << llendl;
+ }
+ }
+
+ // Create the new formatted image of the appropriate format.
+ LLFloaterSnapshot::ESnapshotFormat format = getSnapshotFormat();
+ lldebugs << "Encoding new image of format " << format << llendl;
+
+ switch (format)
+ {
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
+ mFormattedImage = new LLImagePNG();
+ break;
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
+ mFormattedImage = new LLImageJPEG(mSnapshotQuality);
+ break;
+ case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
+ mFormattedImage = new LLImageBMP();
+ break;
+ }
+ if (mFormattedImage->encode(mPreviewImage, 0))
+ {
+ // We can update the data size precisely at that point
+ mDataSize = mFormattedImage->getDataSize();
+ }
+ }
+ return mFormattedImage;
}
void LLSnapshotLivePreview::setSize(S32 w, S32 h)
@@ -776,6 +946,15 @@ void LLSnapshotLivePreview::setSize(S32 w, S32 h)
setHeight(h);
}
+void LLSnapshotLivePreview::setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat format)
+{
+ if (mSnapshotFormat != format)
+ {
+ mFormattedImage = NULL; // Invalidate the already formatted image if any
+ mSnapshotFormat = format;
+ }
+}
+
void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
{
w = getWidth();
@@ -836,6 +1015,10 @@ void LLSnapshotLivePreview::saveTexture()
BOOL LLSnapshotLivePreview::saveLocal()
{
+ // Update mFormattedImage if necessary
+ getFormattedImage();
+
+ // Save the formatted image
BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage);
if(success)
@@ -844,3 +1027,4 @@ BOOL LLSnapshotLivePreview::saveLocal()
}
return success;
}
+
diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h
index 0f09ef214a..e1937187a3 100644
--- a/indra/newview/llsnapshotlivepreview.h
+++ b/indra/newview/llsnapshotlivepreview.h
@@ -28,6 +28,7 @@
#define LL_LLSNAPSHOTLIVEPREVIEW_H
#include "llpanelsnapshot.h"
+#include "llviewertexture.h"
#include "llviewerwindow.h"
class LLImageJPEG;
@@ -61,6 +62,8 @@ public:
LLSnapshotLivePreview(const LLSnapshotLivePreview::Params& p);
~LLSnapshotLivePreview();
+ void setContainer(LLView* container) { mViewContainer = container; }
+
/*virtual*/ void draw();
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
@@ -70,6 +73,9 @@ public:
void getSize(S32& w, S32& h) const;
S32 getWidth() const { return mWidth[mCurImageIndex]; }
S32 getHeight() const { return mHeight[mCurImageIndex]; }
+ S32 getEncodedImageWidth() const;
+ S32 getEncodedImageHeight() const;
+ void estimateDataSize();
S32 getDataSize() const { return mDataSize; }
void setMaxImageSize(S32 size) ;
S32 getMaxImageSize() {return mMaxImageSize ;}
@@ -83,36 +89,48 @@ public:
S32 getThumbnailHeight() const { return mThumbnailHeight ; }
BOOL getThumbnailLock() const { return mThumbnailUpdateLock ; }
BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;}
+ void setThumbnailSubsampled(BOOL subsampled) { mThumbnailSubsampled = subsampled; }
+
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 setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat format);
+ bool setSnapshotQuality(S32 quality, bool set_by_user = true);
void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
+ void setAllowRenderUI(BOOL allow) { mAllowRenderUI = allow; }
+ void setAllowFullScreenPreview(BOOL allow) { mAllowFullScreenPreview = allow; }
+ void setFilter(std::string filter_name) { mFilterName = filter_name; }
+ std::string getFilter() const { return mFilterName; }
void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f);
void saveTexture();
BOOL saveLocal();
- LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; }
- LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; }
+ LLPointer<LLImageFormatted> getFormattedImage();
+ LLPointer<LLImageRaw> getEncodedImage();
- /// Sets size of preview thumbnail image and thhe surrounding rect.
+ /// Sets size of preview thumbnail image and the 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) ;
+
+ LLViewerTexture* getBigThumbnailImage();
+ S32 getBigThumbnailWidth() const { return mBigThumbnailWidth ; }
+ S32 getBigThumbnailHeight() const { return mBigThumbnailHeight ; }
+
// Returns TRUE when snapshot generated, FALSE otherwise.
static BOOL onIdle( void* snapshot_preview );
private:
+ LLView* mViewContainer;
+
LLColor4 mColor;
LLPointer<LLViewerTexture> mViewerImage[2]; //used to represent the scene when the frame is frozen.
LLRect mImageRect[2];
@@ -129,11 +147,20 @@ private:
BOOL mThumbnailUpdateLock ;
BOOL mThumbnailUpToDate ;
LLRect mThumbnailPlaceholderRect;
+ BOOL mThumbnailSubsampled; // TRUE if the thumbnail is a subsampled version of the mPreviewImage
+
+ LLPointer<LLViewerTexture> mBigThumbnailImage ;
+ S32 mBigThumbnailWidth;
+ S32 mBigThumbnailHeight;
+ BOOL mBigThumbnailUpToDate;
S32 mCurImageIndex;
+ // The logic is mPreviewImage (raw frame) -> mFormattedImage (formatted / filtered) -> mPreviewImageEncoded (decoded back, to show artifacts)
LLPointer<LLImageRaw> mPreviewImage;
LLPointer<LLImageRaw> mPreviewImageEncoded;
LLPointer<LLImageFormatted> mFormattedImage;
+ BOOL mAllowRenderUI;
+ BOOL mAllowFullScreenPreview;
LLFrameTimer mSnapshotDelayTimer;
S32 mShineCountdown;
LLFrameTimer mShineAnimTimer;
@@ -150,6 +177,7 @@ private:
LLQuaternion mCameraRot;
BOOL mSnapshotActive;
LLViewerWindow::ESnapshotType mSnapshotBufferType;
+ std::string mFilterName;
public:
static std::set<LLSnapshotLivePreview*> sList;
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 14a42dd8dc..9ea46cab68 100755
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -2593,7 +2593,8 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const
S32 desired_size;
std::string exten = gDirUtilp->getExtension(url);
- if (f_type == FTT_SERVER_BAKE)
+ //if (f_type == FTT_SERVER_BAKE)
+ if ((f_type == FTT_SERVER_BAKE) && !url.empty() && !exten.empty() && (LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C))
{
// SH-4030: This case should be redundant with the following one, just
// breaking it out here to clarify that it's intended behavior.
diff --git a/indra/newview/lltwitterconnect.cpp b/indra/newview/lltwitterconnect.cpp
new file mode 100644
index 0000000000..7088558b83
--- /dev/null
+++ b/indra/newview/lltwitterconnect.cpp
@@ -0,0 +1,503 @@
+/**
+ * @file lltwitterconnect.h
+ * @author Merov, Cho
+ * @brief Connection to Twitter 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 "lltwitterconnect.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> LLTwitterConnect::sStateWatcher(new LLEventStream("TwitterConnectState"));
+boost::scoped_ptr<LLEventPump> LLTwitterConnect::sInfoWatcher(new LLEventStream("TwitterConnectInfo"));
+boost::scoped_ptr<LLEventPump> LLTwitterConnect::sContentWatcher(new LLEventStream("TwitterConnectContent"));
+
+// Local functions
+void log_twitter_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("TwitterConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL;
+ }
+}
+
+void toast_user_for_twitter_success()
+{
+ LLSD args;
+ args["MESSAGE"] = LLTrans::getString("twitter_post_success");
+ LLNotificationsUtil::add("TwitterConnect", args);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterConnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterConnectResponder);
+public:
+
+ LLTwitterConnectResponder()
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS);
+ }
+
+ /* virtual */ void httpSuccess()
+ {
+ LL_DEBUGS("TwitterConnect") << "Connect successful. " << dumpResponse() << LL_ENDL;
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED);
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ if ( HTTP_FOUND == getStatus() )
+ {
+ const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
+ if (location.empty())
+ {
+ LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse()
+ << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
+ }
+ else
+ {
+ LLTwitterConnect::instance().openTwitterWeb(location);
+ }
+ }
+ else
+ {
+ LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
+ const LLSD& content = getContent();
+ log_twitter_connect_error("Connect", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterShareResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterShareResponder);
+public:
+
+ LLTwitterShareResponder()
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTING);
+ }
+
+ /* virtual */ void httpSuccess()
+ {
+ toast_user_for_twitter_success();
+ LL_DEBUGS("TwitterConnect") << "Post successful. " << dumpResponse() << LL_ENDL;
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTED);
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ if ( HTTP_FOUND == getStatus() )
+ {
+ const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
+ if (location.empty())
+ {
+ LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse()
+ << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
+ }
+ else
+ {
+ LLTwitterConnect::instance().openTwitterWeb(location);
+ }
+ }
+ else if ( HTTP_NOT_FOUND == getStatus() )
+ {
+ LLTwitterConnect::instance().connectToTwitter();
+ }
+ else
+ {
+ LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POST_FAILED);
+ const LLSD& content = getContent();
+ log_twitter_connect_error("Share", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterDisconnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterDisconnectResponder);
+public:
+
+ LLTwitterDisconnectResponder()
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECTING);
+ }
+
+ void setUserDisconnected()
+ {
+ // Clear data
+ LLTwitterConnect::instance().clearInfo();
+
+ //Notify state change
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED);
+ }
+
+ /* virtual */ void httpSuccess()
+ {
+ LL_DEBUGS("TwitterConnect") << "Disconnect successful. " << dumpResponse() << LL_ENDL;
+ setUserDisconnected();
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ //User not found so already disconnected
+ if ( HTTP_NOT_FOUND == getStatus() )
+ {
+ LL_DEBUGS("TwitterConnect") << "Already disconnected. " << dumpResponse() << LL_ENDL;
+ setUserDisconnected();
+ }
+ else
+ {
+ LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECT_FAILED);
+ const LLSD& content = getContent();
+ log_twitter_connect_error("Disconnect", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterConnectedResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterConnectedResponder);
+public:
+
+ LLTwitterConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect)
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS);
+ }
+
+ /* virtual */ void httpSuccess()
+ {
+ LL_DEBUGS("TwitterConnect") << "Connect successful. " << dumpResponse() << LL_ENDL;
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED);
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ // show the facebook login page if not connected yet
+ if ( HTTP_NOT_FOUND == getStatus() )
+ {
+ LL_DEBUGS("TwitterConnect") << "Not connected. " << dumpResponse() << LL_ENDL;
+ if (mAutoConnect)
+ {
+ LLTwitterConnect::instance().connectToTwitter();
+ }
+ else
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED);
+ }
+ }
+ else
+ {
+ LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
+ const LLSD& content = getContent();
+ log_twitter_connect_error("Connected", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+private:
+ bool mAutoConnect;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterInfoResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterInfoResponder);
+public:
+
+ /* virtual */ void httpSuccess()
+ {
+ LL_INFOS("TwitterConnect") << "Twitter: Info received" << LL_ENDL;
+ LL_DEBUGS("TwitterConnect") << "Getting Twitter info successful. " << dumpResponse() << LL_ENDL;
+ LLTwitterConnect::instance().storeInfo(getContent());
+ }
+
+ /* virtual */ void httpFailure()
+ {
+ if ( HTTP_FOUND == getStatus() )
+ {
+ const std::string& location = getResponseHeader(HTTP_IN_HEADER_LOCATION);
+ if (location.empty())
+ {
+ LL_WARNS("TwitterConnect") << "Missing Location header " << dumpResponse()
+ << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
+ }
+ else
+ {
+ LLTwitterConnect::instance().openTwitterWeb(location);
+ }
+ }
+ else
+ {
+ LL_WARNS("TwitterConnect") << dumpResponse() << LL_ENDL;
+ const LLSD& content = getContent();
+ log_twitter_connect_error("Info", getStatus(), getReason(),
+ content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLTwitterConnect::LLTwitterConnect()
+: mConnectionState(TWITTER_NOT_CONNECTED),
+ mConnected(false),
+ mInfo(),
+ mRefreshInfo(false),
+ mReadFromMaster(false)
+{
+}
+
+void LLTwitterConnect::openTwitterWeb(std::string url)
+{
+ // Open the URL in an internal browser window without navigation UI
+ LLFloaterWebContent::Params p;
+ p.url(url);
+ p.show_chrome(true);
+ p.allow_address_entry(false);
+ p.allow_back_forward_navigation(false);
+ p.trusted_content(true);
+ p.clean_browser(true);
+ LLFloater *floater = LLFloaterReg::showInstance("twitter_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 "twitter_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.
+ //twitter_web floater contains the "webbrowser" panel. JIRA: ACME-744
+ gFocusMgr.setKeyboardFocus( floater );
+
+ //LLUrlAction::openURLExternal(url);
+}
+
+std::string LLTwitterConnect::getTwitterConnectURL(const std::string& route, bool include_read_from_master)
+{
+ std::string url("");
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ //url = "http://pdp15.lindenlab.com/twitter/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO
+ url = regionp->getCapability("TwitterConnect");
+ url += route;
+
+ if (include_read_from_master && mReadFromMaster)
+ {
+ url += "?read_from_master=true";
+ }
+ }
+ return url;
+}
+
+void LLTwitterConnect::connectToTwitter(const std::string& request_token, const std::string& oauth_verifier)
+{
+ LLSD body;
+ if (!request_token.empty())
+ body["request_token"] = request_token;
+ if (!oauth_verifier.empty())
+ body["oauth_verifier"] = oauth_verifier;
+
+ LLHTTPClient::put(getTwitterConnectURL("/connection"), body, new LLTwitterConnectResponder());
+}
+
+void LLTwitterConnect::disconnectFromTwitter()
+{
+ LLHTTPClient::del(getTwitterConnectURL("/connection"), new LLTwitterDisconnectResponder());
+}
+
+void LLTwitterConnect::checkConnectionToTwitter(bool auto_connect)
+{
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getTwitterConnectURL("/connection", true), new LLTwitterConnectedResponder(auto_connect),
+ LLSD(), timeout, follow_redirects);
+}
+
+void LLTwitterConnect::loadTwitterInfo()
+{
+ if(mRefreshInfo)
+ {
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getTwitterConnectURL("/info", true), new LLTwitterInfoResponder(),
+ LLSD(), timeout, follow_redirects);
+ }
+}
+
+void LLTwitterConnect::uploadPhoto(const std::string& image_url, const std::string& status)
+{
+ LLSD body;
+ body["image"] = image_url;
+ body["status"] = status;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getTwitterConnectURL("/share/photo", true), body, new LLTwitterShareResponder());
+}
+
+void LLTwitterConnect::uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& status)
+{
+ 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=\"status\"\r\n\r\n"
+ << status << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"image\"; filename=\"Untitled." << 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(getTwitterConnectURL("/share/photo", true), data, size, new LLTwitterShareResponder(), headers);
+}
+
+void LLTwitterConnect::updateStatus(const std::string& status)
+{
+ LLSD body;
+ body["status"] = status;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getTwitterConnectURL("/share/status", true), body, new LLTwitterShareResponder());
+}
+
+void LLTwitterConnect::storeInfo(const LLSD& info)
+{
+ mInfo = info;
+ mRefreshInfo = false;
+
+ sInfoWatcher->post(info);
+}
+
+const LLSD& LLTwitterConnect::getInfo() const
+{
+ return mInfo;
+}
+
+void LLTwitterConnect::clearInfo()
+{
+ mInfo = LLSD();
+}
+
+void LLTwitterConnect::setDataDirty()
+{
+ mRefreshInfo = true;
+}
+
+void LLTwitterConnect::setConnectionState(LLTwitterConnect::EConnectionState connection_state)
+{
+ if(connection_state == TWITTER_CONNECTED)
+ {
+ mReadFromMaster = true;
+ setConnected(true);
+ setDataDirty();
+ }
+ else if(connection_state == TWITTER_NOT_CONNECTED)
+ {
+ setConnected(false);
+ }
+ else if(connection_state == TWITTER_POSTED)
+ {
+ mReadFromMaster = false;
+ }
+
+ if (mConnectionState != connection_state)
+ {
+ // set the connection state before notifying watchers
+ mConnectionState = connection_state;
+
+ LLSD state_info;
+ state_info["enum"] = connection_state;
+ sStateWatcher->post(state_info);
+ }
+}
+
+void LLTwitterConnect::setConnected(bool connected)
+{
+ mConnected = connected;
+}
diff --git a/indra/newview/lltwitterconnect.h b/indra/newview/lltwitterconnect.h
new file mode 100644
index 0000000000..c1df13f18c
--- /dev/null
+++ b/indra/newview/lltwitterconnect.h
@@ -0,0 +1,99 @@
+/**
+ * @file lltwitterconnect.h
+ * @author Merov, Cho
+ * @brief Connection to Twitter 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_LLTWITTERCONNECT_H
+#define LL_LLTWITTERCONNECT_H
+
+#include "llsingleton.h"
+#include "llimage.h"
+
+class LLEventPump;
+
+/**
+ * @class LLTwitterConnect
+ *
+ * Manages authentication to, and interaction with, a web service allowing the
+ * the viewer to post status updates and upload photos to Twitter.
+ */
+class LLTwitterConnect : public LLSingleton<LLTwitterConnect>
+{
+ LOG_CLASS(LLTwitterConnect);
+public:
+ enum EConnectionState
+ {
+ TWITTER_NOT_CONNECTED = 0,
+ TWITTER_CONNECTION_IN_PROGRESS = 1,
+ TWITTER_CONNECTED = 2,
+ TWITTER_CONNECTION_FAILED = 3,
+ TWITTER_POSTING = 4,
+ TWITTER_POSTED = 5,
+ TWITTER_POST_FAILED = 6,
+ TWITTER_DISCONNECTING = 7,
+ TWITTER_DISCONNECT_FAILED = 8
+ };
+
+ void connectToTwitter(const std::string& request_token = "", const std::string& oauth_verifier = ""); // Initiate the complete Twitter connection. Please use checkConnectionToTwitter() in normal use.
+ void disconnectFromTwitter(); // Disconnect from the Twitter service.
+ void checkConnectionToTwitter(bool auto_connect = false); // Check if an access token is available on the Twitter service. If not, call connectToTwitter().
+
+ void loadTwitterInfo();
+ void uploadPhoto(const std::string& image_url, const std::string& status);
+ void uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& status);
+ void updateStatus(const std::string& status);
+
+ void storeInfo(const LLSD& info);
+ const LLSD& getInfo() const;
+ void clearInfo();
+ void setDataDirty();
+
+ void setConnectionState(EConnectionState connection_state);
+ void setConnected(bool connected);
+ bool isConnected() { return mConnected; }
+ bool isTransactionOngoing() { return ((mConnectionState == TWITTER_CONNECTION_IN_PROGRESS) || (mConnectionState == TWITTER_POSTING) || (mConnectionState == TWITTER_DISCONNECTING)); }
+ EConnectionState getConnectionState() { return mConnectionState; }
+
+ void openTwitterWeb(std::string url);
+
+private:
+ friend class LLSingleton<LLTwitterConnect>;
+
+ LLTwitterConnect();
+ ~LLTwitterConnect() {};
+ std::string getTwitterConnectURL(const std::string& route = "", bool include_read_from_master = false);
+
+ EConnectionState mConnectionState;
+ BOOL mConnected;
+ LLSD mInfo;
+ bool mRefreshInfo;
+ bool mReadFromMaster;
+
+ static boost::scoped_ptr<LLEventPump> sStateWatcher;
+ static boost::scoped_ptr<LLEventPump> sInfoWatcher;
+ static boost::scoped_ptr<LLEventPump> sContentWatcher;
+};
+
+#endif // LL_LLTWITTERCONNECT_H
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index c2abf0080d..a84564c41b 100755
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -39,6 +39,7 @@
#include "llfloateravatar.h"
#include "llfloateravatarpicker.h"
#include "llfloateravatartextures.h"
+#include "llfloaterbigpreview.h"
#include "llfloaterbeacons.h"
#include "llfloaterbuildoptions.h"
#include "llfloaterbulkpermission.h"
@@ -64,6 +65,8 @@
#include "llfloaterexperiences.h"
#include "llfloaterexperiencepicker.h"
#include "llfloaterevent.h"
+#include "llfloaterfacebook.h"
+#include "llfloaterflickr.h"
#include "llfloaterfonttest.h"
#include "llfloatergesture.h"
#include "llfloatergodtools.h"
@@ -107,7 +110,6 @@
#include "llfloatersettingsdebug.h"
#include "llfloatersidepanelcontainer.h"
#include "llfloatersnapshot.h"
-#include "llfloatersocial.h"
#include "llfloatersounddevices.h"
#include "llfloaterspellchecksettings.h"
#include "llfloatertelehub.h"
@@ -119,6 +121,7 @@
#include "llfloatertos.h"
#include "llfloatertoybox.h"
#include "llfloatertranslationsettings.h"
+#include "llfloatertwitter.h"
#include "llfloateruipreview.h"
#include "llfloatervoiceeffect.h"
#include "llfloaterwebcontent.h"
@@ -311,7 +314,6 @@ 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("scene_load_stats", "floater_scene_load_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSceneLoadStats>);
@@ -320,8 +322,16 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearch>);
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("how_to", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
+
LLFloaterReg::add("fbc_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
+ LLFloaterReg::add("flickr_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
+ LLFloaterReg::add("twitter_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
+
+ LLFloaterReg::add("facebook", "floater_facebook.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFacebook>);
+ LLFloaterReg::add("flickr", "floater_flickr.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFlickr>);
+ LLFloaterReg::add("twitter", "floater_twitter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTwitter>);
+ LLFloaterReg::add("big_preview", "floater_big_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBigPreview>);
LLFloaterUIPreviewUtil::registerFloater();
LLFloaterReg::add("upload_anim_bvh", "floater_animation_bvh_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBvhPreview>, "upload");
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index ed26842035..bdda37cc5f 100755
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -1524,7 +1524,7 @@ void LLViewerMedia::createSpareBrowserMediaSource()
// popping up at the moment we start a media plugin.
if (!sSpareBrowserMediaSource && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))
{
- // The null owner will keep the browser plugin from fully initializing
+ // The null owner will keep the browser plugin from fully initializing
// (specifically, it keeps LLPluginClassMedia from negotiating a size change,
// which keeps MediaPluginWebkit::initBrowserWindow from doing anything until we have some necessary data, like the background color)
sSpareBrowserMediaSource = LLViewerMediaImpl::newSourceFromMediaType(HTTP_CONTENT_TEXT_HTML, NULL, 0, 0);
@@ -1533,7 +1533,7 @@ void LLViewerMedia::createSpareBrowserMediaSource()
/////////////////////////////////////////////////////////////////////////////////////////
// static
-LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource()
+LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource()
{
LLPluginClassMedia* result = sSpareBrowserMediaSource;
sSpareBrowserMediaSource = NULL;
@@ -1582,7 +1582,7 @@ std::string LLViewerMedia::getParcelAudioURL()
// static
void LLViewerMedia::initClass()
{
- gIdleCallbacks.addFunction(LLViewerMedia::updateMedia, NULL);
+ gIdleCallbacks.addFunction(LLViewerMedia::updateMedia, NULL);
sTeleportFinishConnection = LLViewerParcelMgr::getInstance()->
setTeleportFinishedCallback(boost::bind(&LLViewerMedia::onTeleportFinished));
}
@@ -1670,7 +1670,8 @@ LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id,
mNavigateSuspendedDeferred(false),
mIsUpdated(false),
mTrustedBrowser(false),
- mZoomFactor(1.0)
+ mZoomFactor(1.0),
+ mCleanBrowser(false)
{
// Set up the mute list observer if it hasn't been set up already.
@@ -1794,14 +1795,16 @@ void LLViewerMediaImpl::setMediaType(const std::string& media_type)
//////////////////////////////////////////////////////////////////////////////////////////
/*static*/
-LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target)
+LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target, bool clean_browser)
{
std::string plugin_basename = LLMIMETypes::implType(media_type);
LLPluginClassMedia* media_source = NULL;
// HACK: we always try to keep a spare running webkit plugin around to improve launch times.
// If a spare was already created before PluginAttachDebuggerToPlugins was set, don't use it.
- if(plugin_basename == "media_plugin_webkit" && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))
+ // Do not use a spare if launching with full viewer control (e.g. Facebook, Twitter and few others)
+ if ((plugin_basename == "media_plugin_webkit") &&
+ !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins") && !clean_browser)
{
media_source = LLViewerMedia::getSpareBrowserMediaSource();
if(media_source)
@@ -1813,7 +1816,6 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
return media_source;
}
}
-
if(plugin_basename.empty())
{
LL_WARNS_ONCE("Media") << "Couldn't find plugin for media type " << media_type << LL_ENDL;
@@ -1857,18 +1859,18 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
// collect 'cookies enabled' setting from prefs and send to embedded browser
bool cookies_enabled = gSavedSettings.getBOOL( "CookiesEnabled" );
- media_source->enable_cookies( cookies_enabled );
+ media_source->enable_cookies( cookies_enabled || clean_browser);
// collect 'plugins enabled' setting from prefs and send to embedded browser
bool plugins_enabled = gSavedSettings.getBOOL( "BrowserPluginsEnabled" );
- media_source->setPluginsEnabled( plugins_enabled );
+ media_source->setPluginsEnabled( plugins_enabled || clean_browser);
// collect 'javascript enabled' setting from prefs and send to embedded browser
bool javascript_enabled = gSavedSettings.getBOOL( "BrowserJavascriptEnabled" );
- media_source->setJavascriptEnabled( javascript_enabled );
+ media_source->setJavascriptEnabled( javascript_enabled || clean_browser);
bool media_plugin_debugging_enabled = gSavedSettings.getBOOL("MediaPluginDebugging");
- media_source->enableMediaPluginDebugging( media_plugin_debugging_enabled );
+ media_source->enableMediaPluginDebugging( media_plugin_debugging_enabled || clean_browser);
media_source->setTarget(target);
@@ -1923,7 +1925,7 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type)
// Save the MIME type that really caused the plugin to load
mCurrentMimeType = mMimeType;
- LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight, mTarget);
+ LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight, mTarget, mCleanBrowser);
if (media_source)
{
@@ -2544,7 +2546,7 @@ void LLViewerMediaImpl::unload()
}
//////////////////////////////////////////////////////////////////////////////////////////
-void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request)
+void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request, bool clean_browser)
{
cancelMimeTypeProbe();
@@ -2557,6 +2559,7 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi
// Always set the current URL and MIME type.
mMediaURL = url;
mMimeType = mime_type;
+ mCleanBrowser = clean_browser;
// Clear the current media URL, since it will no longer be correct.
mCurrentMediaURL.clear();
diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h
index fff5b3fc08..6803adfaa2 100755
--- a/indra/newview/llviewermedia.h
+++ b/indra/newview/llviewermedia.h
@@ -234,7 +234,7 @@ public:
void navigateReload();
void navigateHome();
void unload();
- void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false);
+ void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false, bool clean_browser = false);
void navigateInternal();
void navigateStop();
bool handleKeyHere(KEY key, MASK mask);
@@ -289,7 +289,7 @@ public:
void setTarget(const std::string& target) { mTarget = target; }
// utility function to create a ready-to-use media instance from a desired media type.
- static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target = LLStringUtil::null);
+ static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target = LLStringUtil::null, bool clean_browser = false);
// Internally set our desired browser user agent string, including
// the Second Life version and skin name. Used because we can
@@ -464,6 +464,7 @@ private:
bool mTrustedBrowser;
std::string mTarget;
LLNotificationPtr mNotification;
+ bool mCleanBrowser; // force the creation of a clean browsing target with full options enabled
private:
BOOL mIsUpdated ;
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 04697f3472..9199e7fd97 100755
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -215,7 +215,8 @@ std::string build_extensions_string(LLFilePicker::ELoadFilter filter)
#endif
case LLFilePicker::FFLOAD_XML:
return XML_EXTENSIONS;
- case LLFilePicker::FFLOAD_ALL:
+ case LLFilePicker::FFLOAD_ALL:
+ case LLFilePicker::FFLOAD_EXE:
return ALL_FILE_EXTENSIONS;
#endif
default:
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index cffce5efc8..bcf6bc19ba 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -2658,7 +2658,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("EstateChangeInfo");
capabilityNames.append("EventQueueGet");
capabilityNames.append("FacebookConnect");
- //capabilityNames.append("FacebookRedirect");
+ capabilityNames.append("FlickrConnect");
+ capabilityNames.append("TwitterConnect");
if (gSavedSettings.getBOOL("UseHTTPInventory"))
{
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 286711d4af..85981c1c41 100755
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -133,6 +133,8 @@ with the same filename but different name
<texture name="Command_Chat_Icon" file_name="toolbar_icons/chat.png" preload="true" />
<texture name="Command_Compass_Icon" file_name="toolbar_icons/land.png" preload="true" />
<texture name="Command_Destinations_Icon" file_name="toolbar_icons/destinations.png" preload="true" />
+ <texture name="Command_Facebook_Icon" file_name="toolbar_icons/facebook.png" preload="true" />
+ <texture name="Command_Flickr_Icon" file_name="toolbar_icons/flickr.png" preload="true" />
<texture name="Command_Gestures_Icon" file_name="toolbar_icons/gestures.png" preload="true" />
<texture name="Command_HowTo_Icon" file_name="toolbar_icons/howto.png" preload="true" />
<texture name="Command_Inventory_Icon" file_name="toolbar_icons/inventory.png" preload="true" />
@@ -148,9 +150,9 @@ 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_Twitter_Icon" file_name="toolbar_icons/twitter.png" preload="true" />
<texture name="Command_View_Icon" file_name="toolbar_icons/view.png" preload="true" />
<texture name="Command_Voice_Icon" file_name="toolbar_icons/nearbyvoice.png" preload="true" />
<texture name="Caret_Bottom_Icon" file_name="toolbar_icons/caret_bottom.png" preload="true" scale.left="1" scale.top="23" scale.right="15" scale.bottom="1" />
diff --git a/indra/newview/skins/default/textures/toolbar_icons/flickr.png b/indra/newview/skins/default/textures/toolbar_icons/flickr.png
new file mode 100644
index 0000000000..7fce9f0df2
--- /dev/null
+++ b/indra/newview/skins/default/textures/toolbar_icons/flickr.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/toolbar_icons/twitter.png b/indra/newview/skins/default/textures/toolbar_icons/twitter.png
new file mode 100644
index 0000000000..a99c490887
--- /dev/null
+++ b/indra/newview/skins/default/textures/toolbar_icons/twitter.png
Binary files differ
diff --git a/indra/newview/skins/default/xui/en/floater_big_preview.xml b/indra/newview/skins/default/xui/en/floater_big_preview.xml
new file mode 100644
index 0000000000..c0bdd3d9bd
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_big_preview.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<floater
+ positioning="cascading"
+ can_close="true"
+ can_resize="true"
+ can_minimize="false"
+ help_topic="floater_big_preview"
+ layout="topleft"
+ name="floater_big_preview"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="PREVIEW"
+ height="465"
+ width="770">
+ <panel
+ height="450"
+ width="750"
+ visible="true"
+ name="big_preview_placeholder"
+ top="5"
+ follows="all"
+ left="10">
+ </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_facebook.xml b/indra/newview/skins/default/xui/en/floater_facebook.xml
new file mode 100644
index 0000000000..4535b9084e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_facebook.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<floater
+ positioning="cascading"
+ can_close="true"
+ can_resize="true"
+ help_topic="floater_facebook"
+ layout="topleft"
+ name="floater_facebook"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="POST TO FACEBOOK"
+ min_height="501"
+ min_width="304"
+ 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"
+ follows="all"
+ halign="center"
+ use_highlighting_on_hover="true">
+ <panel
+ filename="panel_facebook_status.xml"
+ class="llfacebookstatuspanel"
+ follows="all"
+ label="STATUS"
+ name="panel_facebook_status"/>
+ <panel
+ filename="panel_facebook_photo.xml"
+ class="llfacebookphotopanel"
+ follows="all"
+ label="PHOTO"
+ name="panel_facebook_photo"/>
+ <panel
+ filename="panel_facebook_place.xml"
+ class="llfacebookcheckinpanel"
+ follows="all"
+ label="CHECK IN"
+ name="panel_facebook_place"/>
+ <panel
+ filename="panel_facebook_friends.xml"
+ class="llfacebookfriendspanel"
+ follows="all"
+ label="FRIENDS"
+ name="panel_facebook_friends"/>
+ <panel
+ filename="panel_facebook_account.xml"
+ class="llfacebookaccountpanel"
+ follows="all"
+ label="ACCOUNT"
+ name="panel_facebook_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"
+ 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_social.xml b/indra/newview/skins/default/xui/en/floater_flickr.xml
index b7ff374d5f..1a9ffd0489 100644
--- a/indra/newview/skins/default/xui/en/floater_social.xml
+++ b/indra/newview/skins/default/xui/en/floater_flickr.xml
@@ -3,17 +3,17 @@
positioning="cascading"
can_close="true"
can_resize="false"
- help_topic="floater_social"
+ help_topic="floater_flickr"
layout="topleft"
- name="floater_social"
+ name="floater_flickr"
save_rect="true"
single_instance="true"
reuse_instance="true"
- title="POST TO FACEBOOK"
- height="482"
+ title="UPLOAD TO FLICKR"
+ height="622"
width="304">
<panel
- height="482"
+ height="622"
width="304"
visible="true"
name="background"
@@ -27,32 +27,21 @@
tab_height="30"
tab_position="top"
top="7"
- height="437"
- halign="center">
+ height="577"
+ halign="center"
+ use_highlighting_on_hover="true">
<panel
- filename="panel_social_status.xml"
- class="llsocialstatuspanel"
- follows="all"
- label="STATUS"
- name="panel_social_status"/>
- <panel
- filename="panel_social_photo.xml"
- class="llsocialphotopanel"
+ filename="panel_flickr_photo.xml"
+ class="llflickrphotopanel"
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"/>
+ name="panel_flickr_photo"/>
<panel
- filename="panel_social_account.xml"
- class="llsocialaccountpanel"
+ filename="panel_flickr_account.xml"
+ class="llflickraccountpanel"
follows="all"
label="ACCOUNT"
- name="panel_social_account"/>
+ name="panel_flickr_account"/>
</tab_container>
<panel
name="connection_status_panel"
diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml
index 853c209bca..771035b40d 100755
--- a/indra/newview/skins/default/xui/en/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml
@@ -10,7 +10,7 @@
help_topic="snapshot"
save_rect="true"
save_visibility="false"
- title="SNAPSHOT PREVIEW"
+ title="SNAPSHOT"
width="470">
<floater.string
name="unknown">
@@ -381,5 +381,31 @@
top_pad="8"
width="180"
name="auto_snapshot_check" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="13"
+ layout="topleft"
+ left="10"
+ name="filter_list_label"
+ top_pad="10"
+ width="50">
+ Filter:
+ </text>
+ <combo_box
+ control_name="PhotoFilters"
+ follows="left|right|top"
+ name="filters_combobox"
+ tool_tip="Image filters"
+ top_pad="8"
+ left="30"
+ height="21"
+ width="135">
+ <combo_box.item
+ label="No Filter"
+ name="NoFilter"
+ value="NoFilter" />
+ </combo_box>
</panel>
</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_twitter.xml b/indra/newview/skins/default/xui/en/floater_twitter.xml
new file mode 100644
index 0000000000..aa5bfce2e9
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_twitter.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<floater
+ positioning="cascading"
+ can_close="true"
+ can_resize="false"
+ help_topic="floater_twitter"
+ layout="topleft"
+ name="floater_twitter"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="TWITTER"
+ height="502"
+ width="304">
+ <panel
+ height="502"
+ 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="457"
+ halign="center"
+ use_highlighting_on_hover="true">
+ <panel
+ filename="panel_twitter_photo.xml"
+ class="lltwitterphotopanel"
+ follows="all"
+ label="COMPOSE"
+ name="panel_twitter_photo"/>
+ <panel
+ filename="panel_twitter_account.xml"
+ class="lltwitteraccountpanel"
+ follows="all"
+ label="ACCOUNT"
+ name="panel_twitter_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/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 4ade5e7aa7..01759336cd 100755
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -16,14 +16,6 @@
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
@@ -295,6 +287,28 @@
parameter="conversation" />
</menu_item_check>
<menu_item_separator/>
+ <menu_item_call
+ label="Facebook..."
+ name="Facebook">
+ <menu_item_call.on_click
+ function="Floater.Toggle"
+ parameter="facebook"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Twitter..."
+ name="Twitter">
+ <menu_item_call.on_click
+ function="Floater.Toggle"
+ parameter="twitter"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Flickr..."
+ name="Flickr">
+ <menu_item_call.on_click
+ function="Floater.Toggle"
+ parameter="flickr"/>
+ </menu_item_call>
+ <menu_item_separator/>
<menu
label="Voice morphing"
name="VoiceMorphing"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index b207051ec8..14c5dfcea6 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -6149,7 +6149,21 @@ Please select at least one type of content to search (General, Moderate, or Adul
type="notifytip">
[MESSAGE]
</notification>
-
+
+ <notification
+ icon="notify.tga"
+ name="FlickrConnect"
+ type="notifytip">
+ [MESSAGE]
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="TwitterConnect"
+ type="notifytip">
+ [MESSAGE]
+ </notification>
+
<notification
icon="notify.tga"
name="PaymentReceived"
diff --git a/indra/newview/skins/default/xui/en/panel_social_account.xml b/indra/newview/skins/default/xui/en/panel_facebook_account.xml
index d7235396fe..122cbfb717 100644
--- a/indra/newview/skins/default/xui/en/panel_social_account.xml
+++ b/indra/newview/skins/default/xui/en/panel_facebook_account.xml
@@ -2,7 +2,8 @@
height="400"
width="304"
layout="topleft"
- name="panel_social_account">
+ follows="all"
+ name="panel_facebook_account">
<string
name="facebook_connected"
value="You are connected to Facebook as:" />
@@ -34,6 +35,7 @@
type="string"/>
<panel
layout="topleft"
+ follows="left|top"
name="panel_buttons"
height="345"
left="9">
@@ -69,7 +71,7 @@
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]
+ [http://community.secondlife.com/t5/English-Knowledge-Base/Second-Life-Share-Facebook/ta-p/2149711 Learn about posting to Facebook]
</text>
</panel>
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_facebook_friends.xml b/indra/newview/skins/default/xui/en/panel_facebook_friends.xml
new file mode 100644
index 0000000000..9d21a3a293
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_facebook_friends.xml
@@ -0,0 +1,72 @@
+<panel
+ height="400"
+ width="304"
+ layout="topleft"
+ follows="all"
+ name="panel_facebook_friends">
+ <string
+ name="facebook_friends_empty"
+ value="You currently do not have any Facebook friends who are also Second Life residents. Ask your Facebook friends to join Second Life today!" />
+ <string
+ name="facebook_friends_no_connected"
+ value="You're currently not connected to Facebook. Please go to the Account tab to connect and enable this feature." />
+ <accordion
+ background_visible="true"
+ bg_alpha_color="DkGray2"
+ bg_opaque_color="DkGray2"
+ follows="all"
+ height="408"
+ layout="topleft"
+ left="3"
+ name="friends_accordion"
+ right="-2"
+ top_pad="2">
+ <accordion_tab
+ layout="topleft"
+ height="173"
+ name="tab_second_life_friends"
+ title="SL friends">
+ <avatar_list
+ ignore_online_status="true"
+ allow_select="true"
+ follows="all"
+ height="173"
+ layout="topleft"
+ left="0"
+ name="second_life_friends"
+ show_permissions_granted="true"
+ top="0"
+ width="307" />
+ </accordion_tab>
+ <accordion_tab
+ layout="topleft"
+ height="173"
+ name="tab_suggested_friends"
+ title="Add these people as SL friends">
+ <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
+ layout="topleft"
+ word_wrap="true"
+ height="64"
+ width="290"
+ follows="top|left|right"
+ font="SansSerif"
+ left="9"
+ name="facebook_friends_status"
+ top="21"
+ type="string">
+ Not connected to Facebook.
+ </text>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_social_photo.xml b/indra/newview/skins/default/xui/en/panel_facebook_photo.xml
index c79a246d9d..b5b6dee004 100644
--- a/indra/newview/skins/default/xui/en/panel_social_photo.xml
+++ b/indra/newview/skins/default/xui/en/panel_facebook_photo.xml
@@ -2,7 +2,8 @@
height="400"
width="304"
layout="topleft"
- name="panel_social_photo">
+ follows="all"
+ name="panel_facebook_photo">
<layout_stack
layout="topleft"
border_size="0"
@@ -15,7 +16,7 @@
name="snapshot_panel"
height="367">
<combo_box
- control_name="SocialPhotoResolution"
+ control_name="FacebookPhotoResolution"
follows="left|top"
top="6"
left="9"
@@ -39,27 +40,32 @@
label="1024x768"
name="1024x768"
value="[i1024,i768]" />
+ <combo_box.item
+ label="1200x630"
+ name="1200x630"
+ value="[i1200,i630]" />
+ </combo_box>
+ <combo_box
+ control_name="FacebookPhotoFilters"
+ follows="right|top"
+ name="filters_combobox"
+ tool_tip="Image filters"
+ top="6"
+ left="165"
+ height="21"
+ width="135">
+ <combo_box.item
+ label="No Filter"
+ name="NoFilter"
+ value="NoFilter" />
</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"
+ follows="left|top|right"
left="9">
</panel>
<button
@@ -81,7 +87,7 @@
text_color="EmphasisColor"
height="14"
top_pad="-19"
- left_pad="-20"
+ left_pad="-30"
length="1"
halign="center"
name="working_lbl"
@@ -91,6 +97,20 @@
width="150">
Refreshing...
</text>
+ <button
+ follows="right|top"
+ height="23"
+ label="Preview"
+ left="200"
+ top_pad="-19"
+ name="big_preview_btn"
+ tool_tip="Click to toggle preview"
+ is_toggle="true"
+ visible="true"
+ width="100" >
+ <button.commit_callback
+ function="SocialSharing.BigPreview" />
+ </button>
<text
length="1"
follows="top|left|right"
@@ -103,7 +123,7 @@
Comment (optional):
</text>
<text_editor
- follows="left|top"
+ follows="left|top|right|bottom"
height="87"
width="250"
left="9"
@@ -118,7 +138,7 @@
name="photo_button_panel"
height="25">
<button
- follows="left|top"
+ follows="left|bottom"
top="0"
left="9"
height="23"
@@ -129,7 +149,7 @@
function="SocialSharing.SendPhoto" />
</button>
<button
- follows="left|top"
+ follows="left|bottom"
height="23"
label="Cancel"
name="cancel_photo_btn"
diff --git a/indra/newview/skins/default/xui/en/panel_social_place.xml b/indra/newview/skins/default/xui/en/panel_facebook_place.xml
index 13e94f6998..84c87df523 100644
--- a/indra/newview/skins/default/xui/en/panel_social_place.xml
+++ b/indra/newview/skins/default/xui/en/panel_facebook_place.xml
@@ -2,7 +2,8 @@
height="400"
width="304"
layout="topleft"
- name="panel_social_place">
+ follows="all"
+ name="panel_facebook_place">
<layout_stack
layout="topleft"
border_size="0"
@@ -26,7 +27,7 @@
Say something about where you are:
</text>
<text_editor
- follows="top|left"
+ follows="top|left|right"
height="150"
width="250"
left="9"
@@ -106,7 +107,7 @@
name="place_button_panel"
height="25">
<button
- follows="left|top"
+ follows="left|bottom"
top="0"
left="9"
height="23"
@@ -117,7 +118,7 @@
function="SocialSharing.SendCheckin" />
</button>
<button
- follows="left|top"
+ follows="left|bottom"
height="23"
label="Cancel"
name="cancel_place_btn"
diff --git a/indra/newview/skins/default/xui/en/panel_social_status.xml b/indra/newview/skins/default/xui/en/panel_facebook_status.xml
index 54cfa3f524..480abec558 100644
--- a/indra/newview/skins/default/xui/en/panel_social_status.xml
+++ b/indra/newview/skins/default/xui/en/panel_facebook_status.xml
@@ -1,8 +1,9 @@
<panel
height="400"
width="304"
+ follows="all"
layout="topleft"
- name="panel_social_status">
+ name="panel_facebook_status">
<layout_stack
layout="topleft"
border_size="0"
@@ -26,7 +27,7 @@
What's on your mind?
</text>
<text_editor
- follows="left|top"
+ follows="left|top|right"
height="150"
width="250"
left="9"
@@ -41,7 +42,7 @@
name="status_button_panel"
height="25">
<button
- follows="left|top"
+ follows="left|bottom"
top="0"
left="9"
height="23"
@@ -52,7 +53,7 @@
function="SocialSharing.SendStatus" />
</button>
<button
- follows="left|top"
+ follows="left|bottom"
height="23"
label="Cancel"
name="cancel_status_btn"
diff --git a/indra/newview/skins/default/xui/en/panel_flickr_account.xml b/indra/newview/skins/default/xui/en/panel_flickr_account.xml
new file mode 100644
index 0000000000..506d2e2f74
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_flickr_account.xml
@@ -0,0 +1,75 @@
+<panel
+ height="540"
+ width="304"
+ layout="topleft"
+ name="panel_flickr_account">
+ <string
+ name="flickr_connected"
+ value="You are connected to Flickr as:" />
+ <string
+ name="flickr_disconnected"
+ value="Not connected to Flickr" />
+ <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 Flickr.
+ </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-Flickr/ta-p/2435609 Learn about posting to Flickr]
+ </text>
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_flickr_photo.xml b/indra/newview/skins/default/xui/en/panel_flickr_photo.xml
new file mode 100644
index 0000000000..8d8ef45c0d
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_flickr_photo.xml
@@ -0,0 +1,245 @@
+ <panel
+ height="540"
+ width="304"
+ layout="topleft"
+ name="panel_flickr_photo">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="532"
+ follows="all"
+ orientation="vertical"
+ name="stack_photo"
+ top="8">
+ <layout_panel
+ name="snapshot_panel"
+ height="507">
+ <combo_box
+ control_name="FlickrPhotoResolution"
+ 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>
+ <combo_box
+ control_name="FlickrPhotoFilters"
+ follows="right|top"
+ name="filters_combobox"
+ tool_tip="Image filters"
+ top="6"
+ left="165"
+ height="21"
+ width="135">
+ <combo_box.item
+ label="No Filter"
+ name="NoFilter"
+ value="NoFilter" />
+ </combo_box>
+ <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="-30"
+ length="1"
+ halign="center"
+ name="working_lbl"
+ translate="false"
+ type="string"
+ visible="true"
+ width="150">
+ Refreshing...
+ </text>
+ <button
+ follows="right|top"
+ height="23"
+ label="Preview"
+ left="200"
+ top_pad="-19"
+ name="big_preview_btn"
+ tool_tip="Click to toggle preview"
+ is_toggle="true"
+ visible="true"
+ width="100" >
+ <button.commit_callback
+ function="SocialSharing.BigPreview" />
+ </button>
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="title_label"
+ top_pad="15"
+ type="string">
+ Title:
+ </text>
+ <line_editor
+ follows="left|top"
+ height="20"
+ width="250"
+ left="9"
+ length="1"
+ max_length="256"
+ name="photo_title"
+ type="string">
+ </line_editor>
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="description_label"
+ top_pad="10"
+ type="string">
+ Description:
+ </text>
+ <text_editor
+ follows="left|top"
+ height="50"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="photo_description"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <check_box
+ follows="left|top"
+ initial_value="true"
+ label="Include SL location at end of description"
+ name="add_location_cb"
+ left="9"
+ height="16"
+ top_pad="8"/>
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="tags_label"
+ top_pad="10"
+ type="string">
+ Tags:
+ </text>
+ <text
+ length="1"
+ follows="top|left"
+ font="SansSerifSmall"
+ text_color="White_50"
+ height="30"
+ name="tags_help_label"
+ left="50"
+ top_pad="-16"
+ type="string">
+Separate tags with spaces
+Use "" for multi-word tags
+ </text>
+ <text_editor
+ follows="left|top"
+ height="50"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="photo_tags"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <combo_box
+ control_name="FlickrPhotoRating"
+ follows="left|top"
+ top_pad="16"
+ left="9"
+ name="rating_combobox"
+ tool_tip="Flickr content rating"
+ height="21"
+ width="250">
+ <combo_box.item
+ label="Safe Flickr rating"
+ name="SafeRating"
+ value="1" />
+ <combo_box.item
+ label="Moderate Flickr rating"
+ name="ModerateRating"
+ value="2" />
+ <combo_box.item
+ label="Restricted Flickr rating"
+ name="RestrictedRating"
+ value="3" />
+ </combo_box>
+ </layout_panel>
+ <layout_panel
+ name="photo_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Upload"
+ 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_snapshot_options.xml b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
index 61c8c971c2..eff60f8228 100755
--- a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
@@ -81,4 +81,40 @@
<button.commit_callback
function="Snapshot.SaveToComputer" />
</button>
+ <text
+ font="SansSerif"
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="10"
+ name="send_to_facebook_textbox"
+ top_pad="10"
+ type="string">
+ Send to: [secondlife:/// Facebook]
+ </text>
+ <text
+ font="SansSerif"
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="140"
+ name="send_to_twitter_textbox"
+ top_pad="-16"
+ type="string">
+ [secondlife:/// Twitter]
+ </text>
+ <text
+ font="SansSerif"
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="190"
+ name="send_to_flickr_textbox"
+ top_pad="-16"
+ type="string">
+ [secondlife:/// Flickr]
+ </text>
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_twitter_account.xml b/indra/newview/skins/default/xui/en/panel_twitter_account.xml
new file mode 100644
index 0000000000..ee4f6396e1
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_twitter_account.xml
@@ -0,0 +1,75 @@
+<panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_twitter_account">
+ <string
+ name="twitter_connected"
+ value="You are connected to Twitter as:" />
+ <string
+ name="twitter_disconnected"
+ value="Not connected to Twitter" />
+ <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 Twitter.
+ </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-Twitter/ta-p/2435453 Learn about posting to Twitter]
+ </text>
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_twitter_photo.xml b/indra/newview/skins/default/xui/en/panel_twitter_photo.xml
new file mode 100644
index 0000000000..c2be56da21
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_twitter_photo.xml
@@ -0,0 +1,193 @@
+ <panel
+ height="420"
+ width="304"
+ layout="topleft"
+ name="panel_twitter_photo">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="412"
+ follows="all"
+ orientation="vertical"
+ name="stack_photo"
+ top="8">
+ <layout_panel
+ name="text_panel"
+ height="160">
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="status_label"
+ top="3"
+ type="string">
+ What's happening?
+ </text>
+ <text
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ text_color="EmphasisColor"
+ halign="right"
+ height="16"
+ width="30"
+ left="227"
+ name="status_counter_label"
+ top="3"
+ type="string">
+ 140
+ </text>
+ <text_editor
+ follows="left|top"
+ height="87"
+ width="250"
+ left="9"
+ length="1"
+ max_length="140"
+ name="photo_status"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <check_box
+ follows="left|top"
+ initial_value="true"
+ label="Include SL location"
+ name="add_location_cb"
+ left="9"
+ height="16"
+ top_pad="10"/>
+ <check_box
+ follows="left|top"
+ initial_value="true"
+ label="Include a photo"
+ name="add_photo_cb"
+ left="9"
+ height="16"
+ top_pad="10"/>
+ </layout_panel>
+ <layout_panel
+ name="snapshot_panel"
+ height="227">
+ <combo_box
+ control_name="TwitterPhotoResolution"
+ 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>
+ <combo_box
+ control_name="TwitterPhotoFilters"
+ follows="right|top"
+ name="filters_combobox"
+ tool_tip="Image filters"
+ top="6"
+ left="165"
+ height="21"
+ width="135">
+ <combo_box.item
+ label="No Filter"
+ name="NoFilter"
+ value="NoFilter" />
+ </combo_box>
+ <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="-30"
+ length="1"
+ halign="center"
+ name="working_lbl"
+ translate="false"
+ type="string"
+ visible="true"
+ width="150">
+ Refreshing...
+ </text>
+ <button
+ follows="right|top"
+ height="23"
+ label="Preview"
+ left="200"
+ top_pad="-19"
+ name="big_preview_btn"
+ tool_tip="Click to toggle preview"
+ is_toggle="true"
+ visible="true"
+ width="100" >
+ <button.commit_callback
+ function="SocialSharing.BigPreview" />
+ </button>
+ </layout_panel>
+ <layout_panel
+ name="photo_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Tweet"
+ 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/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 189dd1f427..f93b52d02c 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -184,15 +184,40 @@ 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>
+ <!-- SLShare: Facebook, Flickr, and Twitter -->
+ <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>
+ <string name="SocialFlickrConnecting">Connecting to Flickr...</string>
+ <string name="SocialFlickrPosting">Posting...</string>
+ <string name="SocialFlickrDisconnecting">Disconnecting from Flickr...</string>
+ <string name="SocialFlickrErrorConnecting">Problem connecting to Flickr</string>
+ <string name="SocialFlickrErrorPosting">Problem posting to Flickr</string>
+ <string name="SocialFlickrErrorDisconnecting">Problem disconnecting from Flickr</string>
+ <string name="SocialTwitterConnecting">Connecting to Twitter...</string>
+ <string name="SocialTwitterPosting">Posting...</string>
+ <string name="SocialTwitterDisconnecting">Disconnecting from Twitter...</string>
+ <string name="SocialTwitterErrorConnecting">Problem connecting to Twitter</string>
+ <string name="SocialTwitterErrorPosting">Problem posting to Twitter</string>
+ <string name="SocialTwitterErrorDisconnecting">Problem disconnecting from Twitter</string>
- <!-- Tooltip -->
+ <!-- SLShare: User Friendly Filter Names Translation -->
+ <string name="BlackAndWhite">Black &amp; White</string>
+ <string name="Colors1970">1970&apos;s Colors</string>
+ <string name="Intense">Intense</string>
+ <string name="Newspaper">Newsprint</string>
+ <string name="Sepia">Sepia</string>
+ <string name="Spotlight">Spotlight</string>
+ <string name="Video">Video</string>
+ <string name="Autocontrast">Autocontrast</string>
+ <string name="LensFlare">Lens Flare</string>
+ <string name="Miniature">Miniature</string>
+ <string name="Toycamera">Toy Camera</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 -->
<string name="TooltipOwner">Owner:</string> <!-- Owner name follows -->
@@ -3490,6 +3515,12 @@ If you continue to receive this message, contact the [SUPPORT_SITE].
<string name="facebook_post_success">
You posted to Facebook.
</string>
+ <string name="flickr_post_success">
+ You posted to Flickr.
+ </string>
+ <string name="twitter_post_success">
+ You posted to Twitter.
+ </string>
<string name="no_session_message">
(IM Session Doesn't Exist)
@@ -3907,6 +3938,8 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Conversations_Label">Conversations</string>
<string name="Command_Compass_Label">Compass</string>
<string name="Command_Destinations_Label">Destinations</string>
+ <string name="Command_Facebook_Label">Facebook</string>
+ <string name="Command_Flickr_Label">Flickr</string>
<string name="Command_Gestures_Label">Gestures</string>
<string name="Command_HowTo_Label">How to</string>
<string name="Command_Inventory_Label">Inventory</string>
@@ -3922,8 +3955,8 @@ 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_Twitter_Label">Twitter</string>
<string name="Command_View_Label">Camera controls</string>
<string name="Command_Voice_Label">Voice settings</string>
@@ -3935,6 +3968,8 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Conversations_Tooltip">Converse with everyone</string>
<string name="Command_Compass_Tooltip">Compass</string>
<string name="Command_Destinations_Tooltip">Destinations of interest</string>
+ <string name="Command_Facebook_Tooltip">Post to Facebook</string>
+ <string name="Command_Flickr_Tooltip">Upload to Flickr</string>
<string name="Command_Gestures_Tooltip">Gestures for your avatar</string>
<string name="Command_HowTo_Tooltip">How to do common tasks</string>
<string name="Command_Inventory_Tooltip">View and use your belongings</string>
@@ -3950,8 +3985,8 @@ 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_Twitter_Tooltip">Twitter</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/viewer_manifest.py b/indra/newview/viewer_manifest.py
index f7b3a45e8d..f079f31c81 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -98,6 +98,9 @@ class ViewerManifest(LLManifest):
# ... and the entire windlight directory
self.path("windlight")
+ # ... and the entire image filters directory
+ self.path("filters")
+
# ... and the included spell checking dictionaries
pkgdir = os.path.join(self.args['build'], os.pardir, 'packages')
if self.prefix(src=pkgdir,dst=""):