diff options
Diffstat (limited to 'indra')
120 files changed, 8510 insertions, 664 deletions
| 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 c8a05e1fae..18e08b94a6 100755 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -449,18 +449,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 );  } @@ -468,55 +458,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 2277afc585..c1ba1e3c21 100755 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -205,6 +205,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..0f1cbc3fb8 --- /dev/null +++ b/indra/llimage/llimagefilter.h @@ -0,0 +1,136 @@ +/**  + * @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 "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 17e340d136..6d8d6b75a2 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -199,6 +199,7 @@ set(viewer_SOURCE_FILES      llfilteredwearablelist.cpp      llfirstuse.cpp      llflexibleobject.cpp +    llflickrconnect.cpp      llfloaterabout.cpp      llfloaterbvhpreview.cpp      llfloaterauction.cpp @@ -207,6 +208,7 @@ set(viewer_SOURCE_FILES      llfloateravatarpicker.cpp      llfloateravatartextures.cpp      llfloaterbeacons.cpp +    llfloaterbigpreview.cpp      llfloaterbuildoptions.cpp      llfloaterbulkpermission.cpp      llfloaterbump.cpp @@ -228,6 +230,8 @@ set(viewer_SOURCE_FILES      llfloatereditwater.cpp      llfloaterenvironmentsettings.cpp      llfloaterevent.cpp +    llfloaterfacebook.cpp +    llfloaterflickr.cpp      llfloaterfonttest.cpp      llfloatergesture.cpp      llfloatergodtools.cpp @@ -275,7 +279,6 @@ set(viewer_SOURCE_FILES      llfloatersettingsdebug.cpp      llfloatersidepanelcontainer.cpp      llfloatersnapshot.cpp -    llfloatersocial.cpp      llfloatersounddevices.cpp      llfloaterspellchecksettings.cpp      llfloatertelehub.cpp @@ -287,6 +290,7 @@ set(viewer_SOURCE_FILES      llfloatertos.cpp      llfloatertoybox.cpp      llfloatertranslationsettings.cpp +    llfloatertwitter.cpp      llfloateruipreview.cpp      llfloaterurlentry.cpp      llfloatervoiceeffect.cpp @@ -326,6 +330,7 @@ set(viewer_SOURCE_FILES      llfloaterimsessiontab.cpp      llfloaterimsession.cpp      llfloaterimcontainer.cpp +    llimagefiltersmanager.cpp      llimhandler.cpp      llimview.cpp      llinspect.cpp @@ -568,6 +573,7 @@ set(viewer_SOURCE_FILES      lltransientdockablefloater.cpp      lltransientfloatermgr.cpp      lltranslate.cpp +    lltwitterconnect.cpp      lluilistener.cpp      lluploaddialog.cpp      lluploadfloaterobservers.cpp @@ -788,6 +794,7 @@ set(viewer_HEADER_FILES      llfilteredwearablelist.h      llfirstuse.h      llflexibleobject.h +    llflickrconnect.h      llfloaterabout.h      llfloaterbvhpreview.h      llfloaterauction.h @@ -796,6 +803,7 @@ set(viewer_HEADER_FILES      llfloateravatarpicker.h      llfloateravatartextures.h      llfloaterbeacons.h +    llfloaterbigpreview.h      llfloaterbuildoptions.h      llfloaterbulkpermission.h      llfloaterbump.h @@ -817,6 +825,8 @@ set(viewer_HEADER_FILES      llfloatereditwater.h      llfloaterenvironmentsettings.h      llfloaterevent.h +    llfloaterfacebook.h +    llfloaterflickr.h      llfloaterfonttest.h      llfloatergesture.h      llfloatergodtools.h @@ -864,7 +874,6 @@ set(viewer_HEADER_FILES      llfloatersettingsdebug.h      llfloatersidepanelcontainer.h      llfloatersnapshot.h -    llfloatersocial.h      llfloatersounddevices.h      llfloaterspellchecksettings.h      llfloatertelehub.h @@ -876,6 +885,7 @@ set(viewer_HEADER_FILES      llfloatertos.h      llfloatertoybox.h      llfloatertranslationsettings.h +    llfloatertwitter.h      llfloateruipreview.h      llfloaterurlentry.h      llfloatervoiceeffect.h @@ -914,6 +924,7 @@ set(viewer_HEADER_FILES      llfloaterimsessiontab.h      llfloaterimsession.h      llfloaterimcontainer.h +    llimagefiltersmanager.h      llimview.h      llinspect.h      llinspectavatar.h @@ -1147,6 +1158,7 @@ set(viewer_HEADER_FILES      lltransientdockablefloater.h      lltransientfloatermgr.h      lltranslate.h +    lltwitterconnect.h      lluiconstants.h      lluilistener.h      lluploaddialog.h 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 4c7b192ae5..b440dff095 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -11300,6 +11300,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> @@ -13162,7 +13173,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 9a20ce8f1b..ec7d0f7c50 100644 --- 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; @@ -150,7 +175,7 @@ public:  	{  		if (isGoodStatus(status))  		{ -            toast_user_for_success(); +            toast_user_for_facebook_success();  			LL_DEBUGS("FacebookConnect") << "Post successful. content: " << content << LL_ENDL;  			LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED); @@ -340,10 +365,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  @@ -360,6 +387,7 @@ std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route, b      LLViewerRegion *regionp = gAgent.getRegion();      if (regionp)      { +		//url = "http://pdp15.lindenlab.com/fbc/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO          url = regionp->getCapability("FacebookConnect");          url += route; @@ -375,9 +403,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());  } @@ -421,15 +453,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()); @@ -476,7 +518,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. @@ -568,12 +610,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 16eacc9392..f921dace84 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 \ @@ -579,6 +580,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; @@ -777,9 +782,9 @@ 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; +        mPickOptions |= F_NAV_SUPPORT;  	}  	if (blocking) diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index f0f82c51db..0b89e2716c 100755 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -86,7 +86,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..1898842478 --- /dev/null +++ b/indra/newview/llflickrconnect.cpp @@ -0,0 +1,480 @@ +/**  + * @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 completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			LL_DEBUGS("FlickrConnect") << "Connect successful. content: " << content << LL_ENDL; +			 +            LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED); +		} +		else if (status != 302) +		{ +            LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED); +            log_flickr_connect_error("Connect", status, reason, content.get("error_code"), content.get("error_description")); +		} +	} +     +    void completedHeader(U32 status, const std::string& reason, const LLSD& content) +    { +        if (status == 302) +        { +            LLFlickrConnect::instance().openFlickrWeb(content["location"]); +        } +    } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFlickrShareResponder : public LLHTTPClient::Responder +{ +	LOG_CLASS(LLFlickrShareResponder); +public: +     +	LLFlickrShareResponder() +	{ +		LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTING); +	} +	 +	virtual void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +            toast_user_for_flickr_success(); +			LL_DEBUGS("FlickrConnect") << "Post successful. content: " << content << LL_ENDL; +			 +			LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTED); +		} +		else if (status == 404) +		{ +			LLFlickrConnect::instance().connectToFlickr(); +		} +		else +		{ +            LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POST_FAILED); +            log_flickr_connect_error("Share", status, reason, content.get("error_code"), content.get("error_description")); +		} +	} +     +    void completedHeader(U32 status, const std::string& reason, const LLSD& content) +    { +        if (status == 302) +        { +            LLFlickrConnect::instance().openFlickrWeb(content["location"]); +        } +    } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +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 completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status))  +		{ +			LL_DEBUGS("FlickrConnect") << "Disconnect successful. content: " << content << LL_ENDL; +			setUserDisconnected(); + +		} +		//User not found so already disconnected +		else if(status == 404) +		{ +			LL_DEBUGS("FlickrConnect") << "Already disconnected. content: " << content << LL_ENDL; +			setUserDisconnected(); +		} +		else +		{ +			LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECT_FAILED); +            log_flickr_connect_error("Disconnect", status, reason, 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 completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			LL_DEBUGS("FlickrConnect") << "Connect successful. content: " << content << LL_ENDL; +             +            LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED); +		} +		else +		{ +			// show the flickr login page if not connected yet +			if (status == 404) +			{ +				if (mAutoConnect) +				{ +					LLFlickrConnect::instance().connectToFlickr(); +				} +				else +				{ +					LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED); +				} +			} +            else +            { +                LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED); +				log_flickr_connect_error("Connected", status, reason, content.get("error_code"), content.get("error_description")); +            } +		} +	} +     +private: +	bool mAutoConnect; +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFlickrInfoResponder : public LLHTTPClient::Responder +{ +	LOG_CLASS(LLFlickrInfoResponder); +public: + +	virtual void completed(U32 status, const std::string& reason, const LLSD& info) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << "Flickr: Info received" << llendl; +			LL_DEBUGS("FlickrConnect") << "Getting Flickr info successful. info: " << info << LL_ENDL; +			LLFlickrConnect::instance().storeInfo(info); +		} +		else +		{ +			log_flickr_connect_error("Info", status, reason, info.get("error_code"), info.get("error_description")); +		} +	} + +	void completedHeader(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (status == 302) +		{ +			LLFlickrConnect::instance().openFlickrWeb(content["location"]); +		} +	} +}; + +/////////////////////////////////////////////////////////////////////////////// +// +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 2a74c8e3ea..5589d4897d 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,15 @@  #include "llviewerregion.h"  #include "llviewercontrol.h"  #include "llviewermedia.h" +#include "lltabcontainer.h" +#include "llavatarlist.h" +#include "llpanelpeoplemenus.h" -static LLRegisterPanelClassWrapper<LLSocialStatusPanel> t_panel_status("llsocialstatuspanel"); -static LLRegisterPanelClassWrapper<LLSocialPhotoPanel> t_panel_photo("llsocialphotopanel"); -static LLRegisterPanelClassWrapper<LLSocialCheckinPanel> t_panel_checkin("llsocialcheckinpanel"); -static LLRegisterPanelClassWrapper<LLSocialAccountPanel> t_panel_account("llsocialaccountpanel"); +static LLRegisterPanelClassWrapper<LLFacebookStatusPanel> t_panel_status("llfacebookstatuspanel"); +static LLRegisterPanelClassWrapper<LLFacebookPhotoPanel> t_panel_photo("llfacebookphotopanel"); +static LLRegisterPanelClassWrapper<LLFacebookCheckinPanel> t_panel_checkin("llfacebookcheckinpanel"); +static LLRegisterPanelClassWrapper<LLFacebookFriendsPanel> t_panel_friends("llfacebookfriendspanel"); +static LLRegisterPanelClassWrapper<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 +64,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 +82,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 +111,7 @@ BOOL LLSocialStatusPanel::postBuild()  	return LLPanel::postBuild();  } -void LLSocialStatusPanel::draw() +void LLFacebookStatusPanel::draw()  {      if (mMessageTextEditor && mPostButton && mCancelButton)  	{ @@ -106,10 +125,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 +141,7 @@ void LLSocialStatusPanel::onSend()  	}  } -bool LLSocialStatusPanel::onFacebookConnectStateChange(const LLSD& data) +bool LLFacebookStatusPanel::onFacebookConnectStateChange(const LLSD& data)  {  	switch (data.get("enum").asInteger())  	{ @@ -131,7 +150,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 +158,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 +167,7 @@ void LLSocialStatusPanel::sendStatus()  	}  } -void LLSocialStatusPanel::clearAndClose() +void LLFacebookStatusPanel::clearAndClose()  {  	mMessageTextEditor->setValue(""); @@ -160,24 +179,28 @@ void LLSocialStatusPanel::clearAndClose()  }  /////////////////////////// -//LLSocialPhotoPanel/////// +//LLFacebookPhotoPanel///////  /////////////////////////// -LLSocialPhotoPanel::LLSocialPhotoPanel() : +LLFacebookPhotoPanel::LLFacebookPhotoPanel() :  mSnapshotPanel(NULL),  mResolutionComboBox(NULL),  mRefreshBtn(NULL), +mBtnPreview(NULL),  mWorkingLabel(NULL),  mThumbnailPlaceholder(NULL),  mCaptionTextBox(NULL),  mLocationCheckbox(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())  	{ @@ -185,25 +208,66 @@ LLSocialPhotoPanel::~LLSocialPhotoPanel()  	}  } -BOOL LLSocialPhotoPanel::postBuild() +BOOL LLFacebookPhotoPanel::postBuild()  { -	setVisibleCallback(boost::bind(&LLSocialPhotoPanel::onVisibilityChange, 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");  	mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");  	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()); @@ -212,10 +276,22 @@ 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);      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 +    // Display the thumbnail if one is available  	if (previewp && previewp->getThumbnailImage())  	{  		const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect(); @@ -242,8 +318,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 @@ -256,13 +330,13 @@ void LLSocialPhotoPanel::draw()  	LLPanel::draw();  } -LLSnapshotLivePreview* LLSocialPhotoPanel::getPreviewView() +LLSnapshotLivePreview* LLFacebookPhotoPanel::getPreviewView()  {  	LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();  	return previewp;  } -void LLSocialPhotoPanel::onVisibilityChange(const LLSD& new_visibility) +void LLFacebookPhotoPanel::onVisibilityChange(const LLSD& new_visibility)  {  	bool visible = new_visibility.asBoolean();  	if (visible) @@ -283,10 +357,15 @@ void LLSocialPhotoPanel::onVisibilityChange(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(); @@ -294,21 +373,48 @@ void LLSocialPhotoPanel::onVisibilityChange(const LLSD& new_visibility)  	}  } -void LLSocialPhotoPanel::onClickNewSnapshot() +void LLFacebookPhotoPanel::onClickNewSnapshot()  {  	LLSnapshotLivePreview* previewp = getPreviewView();  	if (previewp)  	{ -		//setStatus(Impl::STATUS_READY); -		lldebugs << "updating snapshot" << llendl;  		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()  { -	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)); +    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("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()) @@ -321,7 +427,7 @@ void LLSocialPhotoPanel::onSend()  	}  } -bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data) +bool LLFacebookPhotoPanel::onFacebookConnectStateChange(const LLSD& data)  {  	switch (data.get("enum").asInteger())  	{ @@ -330,7 +436,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;  	} @@ -338,7 +444,7 @@ bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data)  	return false;  } -void LLSocialPhotoPanel::sendPhoto() +void LLFacebookPhotoPanel::sendPhoto()  {  	// Get the caption  	std::string caption = mCaptionTextBox->getValue().asString(); @@ -371,7 +477,7 @@ void LLSocialPhotoPanel::sendPhoto()  	updateControls();  } -void LLSocialPhotoPanel::clearAndClose() +void LLFacebookPhotoPanel::clearAndClose()  {  	mCaptionTextBox->setValue(""); @@ -379,39 +485,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  	lldebugs << "Is snapshot up-to-date? " << got_snap << llendl; - -	LLLocale locale(LLLocale::USER_LOCALE); -	std::string bytes_string; -	if (got_snap) -	{ -		LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10 ); -	} - -	//getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : getString("unknown")); <---uses localized string -	getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : "unknown"); -	getChild<LLUICtrl>("file_size_label")->setColor( -		shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD  -		&& got_bytes -		&& previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLUIColor(LLColor4::red) : LLUIColorTable::instance().getColor( "LabelTextColor" )); - +      	updateResolution(FALSE);  } -void LLSocialPhotoPanel::updateResolution(BOOL do_update) +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; @@ -421,6 +516,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)  	{ @@ -443,27 +541,35 @@ void LLSocialPhotoPanel::updateResolution(BOOL do_update)  		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 -			lldebugs << "updating thumbnail" << llendl; -			 -			previewp->updateSnapshot(FALSE, TRUE); -			if(do_update) +			if (do_update)  			{ -				lldebugs << "Will update controls" << llendl; +                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() ; @@ -484,23 +590,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"); @@ -514,7 +620,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); @@ -555,10 +661,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()) @@ -571,7 +677,7 @@ void LLSocialCheckinPanel::onSend()  	}  } -bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data) +bool LLFacebookCheckinPanel::onFacebookConnectStateChange(const LLSD& data)  {  	switch (data.get("enum").asInteger())  	{ @@ -580,7 +686,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;  	} @@ -588,7 +694,7 @@ bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data)  	return false;  } -void LLSocialCheckinPanel::sendCheckin() +void LLFacebookCheckinPanel::sendCheckin()  {  	// Get the location SLURL  	LLSLURL slurl; @@ -606,7 +712,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; @@ -623,7 +734,7 @@ void LLSocialCheckinPanel::sendCheckin()  	LLFacebookConnect::instance().postCheckin(slurl_string, region_name, description, map_url, caption);  } -void LLSocialCheckinPanel::clearAndClose() +void LLFacebookCheckinPanel::clearAndClose()  {  	mMessageTextEditor->setValue(""); @@ -635,23 +746,119 @@ void LLSocialCheckinPanel::clearAndClose()  }  /////////////////////////// -//LLSocialAccountPanel////// +//LLFacebookFriendsPanel//////  /////////////////////////// -LLSocialAccountPanel::LLSocialAccountPanel() :  +LLFacebookFriendsPanel::LLFacebookFriendsPanel() :  +mSuggestedFriends(NULL) +{ +} + +BOOL LLFacebookFriendsPanel::postBuild() +{ +	mSuggestedFriends = getChild<LLAvatarList>("suggested_friends"); +	mSuggestedFriends->setContextMenu(&LLPanelPeopleMenus::gSuggestedFriendsContextMenu); +	 +	setVisibleCallback(boost::bind(&LLFacebookFriendsPanel::updateFacebookList, this, _2)); + +	return LLPanel::postBuild(); +} + +bool LLFacebookFriendsPanel::updateSuggestedFriendList() +{ +	const LLAvatarTracker& av_tracker = LLAvatarTracker::instance(); +	uuid_vec_t& suggested_friends = mSuggestedFriends->getIDs(); +	suggested_friends.clear(); + +	//Add suggested friends +	LLSD friends = LLFacebookConnect::instance().getContent(); +	for (LLSD::array_const_iterator i = friends.beginArray(); i != friends.endArray(); ++i) +	{ +		LLUUID agent_id = (*i).asUUID(); +		bool second_life_buddy = agent_id.notNull() ? av_tracker.isBuddy(agent_id) : false; + +		if(!second_life_buddy) +		{ +			//FB+SL but not SL friend +			if (agent_id.notNull()) +			{ +				suggested_friends.push_back(agent_id); +			} +		} +	} + +	//Force a refresh when there aren't any filter matches (prevent displaying content that shouldn't display) +	mSuggestedFriends->setDirty(true, !mSuggestedFriends->filterHasMatches()); +	//showFriendsAccordionsIfNeeded(); + +	return false; +} + +void LLFacebookFriendsPanel::updateFacebookList(bool visible) +{ +	if (visible) +	{ +		LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLPanelPeople"); // just in case it is already listening +		LLEventPumps::instance().obtain("FacebookConnectContent").listen("LLPanelPeople", boost::bind(&LLFacebookFriendsPanel::updateSuggestedFriendList, this)); + +		LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLPanelPeople"); // just in case it is already listening +		LLEventPumps::instance().obtain("FacebookConnectState").listen("LLPanelPeople", boost::bind(&LLFacebookFriendsPanel::onConnectedToFacebook, this, _1)); + +		//Connected +		if (LLFacebookConnect::instance().isConnected()) +		{ +			LLFacebookConnect::instance().loadFacebookFriends(); +		} +		//Check if connected +        if ((LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_NOT_CONNECTED) || +            (LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_CONNECTION_FAILED)) +        { +            LLFacebookConnect::instance().checkConnectionToFacebook(); +        } + +		updateSuggestedFriendList(); +	} +	else +	{ +		LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLPanelPeople"); +		LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLPanelPeople"); +	} +} + +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::onVisibilityChange, 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"); @@ -662,7 +869,7 @@ BOOL LLSocialAccountPanel::postBuild()  	return LLPanel::postBuild();  } -void LLSocialAccountPanel::draw() +void LLFacebookAccountPanel::draw()  {  	LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState(); @@ -677,17 +884,17 @@ void LLSocialAccountPanel::draw()  	LLPanel::draw();  } -void LLSocialAccountPanel::onVisibilityChange(const LLSD& new_visibility) +void LLFacebookAccountPanel::onVisibilityChange(const LLSD& new_visibility)  {  	bool visible = new_visibility.asBoolean();  	if(visible)  	{ -		LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialAccountPanel"); -		LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialAccountPanel", boost::bind(&LLSocialAccountPanel::onFacebookConnectStateChange, this, _1)); +		LLEventPumps::instance().obtain("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()) @@ -707,12 +914,12 @@ void LLSocialAccountPanel::onVisibilityChange(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())  	{ @@ -730,7 +937,7 @@ bool LLSocialAccountPanel::onFacebookConnectStateChange(const LLSD& data)  	return false;  } -bool LLSocialAccountPanel::onFacebookConnectInfoChange() +bool LLFacebookAccountPanel::onFacebookConnectInfoChange()  {  	LLSD info = LLFacebookConnect::instance().getInfo();  	std::string clickable_name; @@ -746,7 +953,7 @@ bool LLSocialAccountPanel::onFacebookConnectInfoChange()  	return false;  } -void LLSocialAccountPanel::showConnectButton() +void LLFacebookAccountPanel::showConnectButton()  {  	if(!mConnectButton->getVisible())  	{ @@ -755,7 +962,7 @@ void LLSocialAccountPanel::showConnectButton()  	}  } -void LLSocialAccountPanel::hideConnectButton() +void LLFacebookAccountPanel::hideConnectButton()  {  	if(mConnectButton->getVisible())  	{ @@ -764,14 +971,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(); @@ -779,7 +986,7 @@ void LLSocialAccountPanel::showConnectedLayout()  	hideConnectButton();  } -void LLSocialAccountPanel::onConnect() +void LLFacebookAccountPanel::onConnect()  {  	LLFacebookConnect::instance().checkConnectionToFacebook(true); @@ -787,7 +994,7 @@ void LLSocialAccountPanel::onConnect()  	LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com");   } -void LLSocialAccountPanel::onDisconnect() +void LLFacebookAccountPanel::onDisconnect()  {  	LLFacebookConnect::instance().disconnectFromFacebook(); @@ -795,27 +1002,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 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 LLFloaterSocial::onCancel() +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"); @@ -823,39 +1045,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 bbe07c9704..08c5f24e4d 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,8 +24,8 @@  * 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 "llfloater.h"  #include "lltextbox.h" @@ -34,11 +34,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 +55,21 @@ private:  	LLUICtrl* mCancelButton;  }; -class LLSocialPhotoPanel : public LLPanel +class LLFacebookPhotoPanel : public LLPanel  {  public: -	LLSocialPhotoPanel(); -	~LLSocialPhotoPanel(); +	LLFacebookPhotoPanel(); +	~LLFacebookPhotoPanel();  	BOOL postBuild();  	void draw();  	LLSnapshotLivePreview* getPreviewView();  	void onVisibilityChange(const LLSD& new_visibility); +    void onClickBigPreview();  	void onClickNewSnapshot();  	void onSend(); +	S32 notify(const LLSD& info);  	bool onFacebookConnectStateChange(const LLSD& data);  	void sendPhoto(); @@ -77,23 +81,32 @@ 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 * mLocationCheckbox;  	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(); @@ -115,10 +128,24 @@ private:      bool mReloadingMapTexture;  }; -class LLSocialAccountPanel : public LLPanel +class LLFacebookFriendsPanel : public LLPanel +{ +public: +	LLFacebookFriendsPanel(); +	BOOL postBuild(); + +private: +	bool updateSuggestedFriendList(); +	void updateFacebookList(bool visible); +	bool onConnectedToFacebook(const LLSD& data); +	 +	LLAvatarList* mSuggestedFriends; +}; + +class LLFacebookAccountPanel : public LLPanel  {  public: -	LLSocialAccountPanel(); +	LLFacebookAccountPanel();  	BOOL postBuild();  	void draw(); @@ -142,24 +169,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..8c4c36538b --- /dev/null +++ b/indra/newview/llfloaterflickr.cpp @@ -0,0 +1,795 @@ +/**  +* @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 LLRegisterPanelClassWrapper<LLFlickrPhotoPanel> t_panel_photo("llflickrphotopanel"); +static LLRegisterPanelClassWrapper<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(const LLSD& new_visibility) +{ +	bool visible = new_visibility.asBoolean(); +	if (visible) +	{ +		if (mPreviewHandle.get()) +		{ +			LLSnapshotLivePreview* preview = getPreviewView(); +			if(preview) +			{ +				lldebugs << "opened, updating snapshot" << llendl; +				preview->updateSnapshot(TRUE); +			} +		} +		else +		{ +			LLRect full_screen_rect = getRootView()->getRect(); +			LLSnapshotLivePreview::Params p; +			p.rect(full_screen_rect); +			LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); +			mPreviewHandle = previewp->getHandle(); + +            previewp->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(); +			 +			tags += llformat(" \"%s:region=%s\"", FLICKR_MACHINE_TAGS_NAMESPACE.c_str(), region_name.c_str()); +			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(const LLSD& new_visibility) +{ +	bool visible = new_visibility.asBoolean(); + +	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..7a5453d32a --- /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(const LLSD& 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(const LLSD& 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 ea385d7baf..8e48d35c1d 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" @@ -92,6 +95,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); @@ -434,9 +438,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")); @@ -469,8 +472,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)  	  default:  		break;  	} - -	if (previewp) +     +    if (previewp)  	{  		previewp->setSnapshotType(shot_type);  		previewp->setSnapshotFormat(shot_format); @@ -563,6 +566,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"); @@ -621,9 +644,9 @@ void LLFloaterSnapshot::Impl::applyKeepAspectCheck(LLFloaterSnapshot* view, BOOL  			previewp->getSize(w, h) ;  			updateSpinners(view, previewp, w, h, TRUE); // may change w and h -			lldebugs << "updating thumbnail" << llendl; +			lldebugs << "updating snapshot" << llendl;  			previewp->setSize(w, h) ; -			previewp->updateSnapshot(FALSE, TRUE); +			previewp->updateSnapshot(TRUE);  			checkAutoSnapshot(previewp, TRUE);  		}  	} @@ -820,8 +843,8 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, BOOL  			// hide old preview as the aspect ratio could be wrong  			checkAutoSnapshot(previewp, FALSE); -			lldebugs << "updating thumbnail" << llendl; -			getPreviewView(view)->updateSnapshot(FALSE, TRUE); +			lldebugs << "updating snapshot" << llendl; +			getPreviewView(view)->updateSnapshot(TRUE);  			if(do_update)  			{  				lldebugs << "Will update controls" << llendl; @@ -858,7 +881,6 @@ void LLFloaterSnapshot::Impl::onImageQualityChange(LLFloaterSnapshot* view, S32  	{  		previewp->setSnapshotQuality(quality_val);  	} -	checkAutoSnapshot(previewp, TRUE);  }  // static @@ -867,8 +889,6 @@ void LLFloaterSnapshot::Impl::onImageFormatChange(LLFloaterSnapshot* view)  	if (view)  	{  		gSavedSettings.setS32("SnapshotFormat", getImageFormat(view)); -		lldebugs << "image format changed, updating snapshot" << llendl; -		getPreviewView(view)->updateSnapshot(TRUE);  		updateControls(view);  		setNeedRefresh(view, false); // we're refreshing  	} @@ -968,8 +988,8 @@ void LLFloaterSnapshot::Impl::applyCustomResolution(LLFloaterSnapshot* view, S32  			previewp->setSize(w,h);  			checkAutoSnapshot(previewp, FALSE); -			lldebugs << "applied custom resolution, updating thumbnail" << llendl; -			previewp->updateSnapshot(FALSE, TRUE); +			lldebugs << "applied custom resolution, updating snapshot" << llendl; +			previewp->updateSnapshot(TRUE);  			comboSetCustom(view, "profile_size_combo");  			comboSetCustom(view, "postcard_size_combo");  			comboSetCustom(view, "texture_size_combo"); @@ -1063,7 +1083,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)); @@ -1093,6 +1132,7 @@ BOOL LLFloaterSnapshot::postBuild()  	getChild<LLComboBox>("local_format_combo")->selectNthItem(0);  	impl.mPreviewHandle = previewp->getHandle(); +    previewp->setContainer(this);  	impl.updateControls(this);  	impl.updateLayout(this); @@ -1257,6 +1297,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;  } @@ -1264,9 +1330,11 @@ S32 LLFloaterSnapshot::notify(const LLSD& info)  void LLFloaterSnapshot::update()  {  	LLFloaterSnapshot* inst = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot"); -	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; @@ -1334,43 +1402,6 @@ BOOL LLFloaterSnapshot::saveLocal()  }  // static -void LLFloaterSnapshot::preUpdate() -{ -	// FIXME: duplicated code -	LLFloaterSnapshot* instance = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot"); -	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 = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot"); -	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 = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot"); diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h index 82af8c7a9d..3a279c194d 100755 --- a/indra/newview/llfloatersnapshot.h +++ b/indra/newview/llfloatersnapshot.h @@ -58,8 +58,6 @@ public:  	static LLFloaterSnapshot* getInstance();  	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..0b2987a3c1 --- /dev/null +++ b/indra/newview/llfloatertwitter.cpp @@ -0,0 +1,830 @@ +/**  +* @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 LLRegisterPanelClassWrapper<LLTwitterPhotoPanel> t_panel_photo("lltwitterphotopanel"); +static LLRegisterPanelClassWrapper<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(const LLSD& new_visibility) +{ +	bool visible = new_visibility.asBoolean(); +	if (visible) +	{ +		if (mPreviewHandle.get()) +		{ +			LLSnapshotLivePreview* preview = getPreviewView(); +			if(preview) +			{ +				lldebugs << "opened, updating snapshot" << llendl; +				preview->updateSnapshot(TRUE); +			} +		} +		else +		{ +			LLRect full_screen_rect = getRootView()->getRect(); +			LLSnapshotLivePreview::Params p; +			p.rect(full_screen_rect); +			LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); +			mPreviewHandle = previewp->getHandle(); + +            previewp->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(const LLSD& new_visibility) +{ +	bool visible = new_visibility.asBoolean(); + +	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..659ab7779a --- /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(const LLSD& 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(const LLSD& 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 0106a1615d..44649e6ca8 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 68dbb5ae33..8830accbf2 100755 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -30,6 +30,8 @@  #include "lliconctrl.h"  #include "llfloaterreg.h"  #include "llfacebookconnect.h" +#include "llflickrconnect.h" +#include "lltwitterconnect.h"  #include "lllayoutstack.h"  #include "llpluginclassmedia.h"  #include "llprogressbar.h" @@ -51,7 +53,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 ) @@ -242,7 +245,7 @@ void LLFloaterWebContent::open_media(const Params& p)  	LLViewerMedia::proxyWindowOpened(p.target(), p.id());  	mWebBrowser->setHomePageUrl(p.url, "text/html");  	mWebBrowser->setTarget(p.target); -	mWebBrowser->navigateTo(p.url, "text/html"); +	mWebBrowser->navigateTo(p.url, "text/html", p.clean_browser);  	set_current_url(p.url); @@ -253,11 +256,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); @@ -303,7 +301,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();  } @@ -439,9 +454,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 f22940cd07..2bb8e3271f 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 2075aeed63..229542c1c4 100755 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -539,7 +539,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://"; @@ -556,7 +556,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 6c38c1fb56..8429d8188e 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 f551fc96ee..b2e733e1ae 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 c7141f36ee..67e190dafd 100755 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -56,7 +56,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 554fabe5b3..a7b9b6d22e 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 LLRegisterPanelClassWrapper<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 7532ebfc57..db203c7c78 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" @@ -71,9 +75,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), @@ -87,7 +93,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); @@ -106,6 +116,7 @@ LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Param  	mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ;  	mThumbnailUpdateLock = FALSE ;  	mThumbnailUpToDate   = FALSE ; +	mBigThumbnailUpToDate = FALSE ;  }  LLSnapshotLivePreview::~LLSnapshotLivePreview() @@ -121,14 +132,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() @@ -136,97 +140,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.  	lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl; -	if (getSnapshotUpToDate()) -	{ -		S32 old_image_index = mCurImageIndex; -		mCurImageIndex = (mCurImageIndex + 1) % 2;  -		setSize(mWidth[old_image_index], mHeight[old_image_index]); -		mFallAnimTimer.start();		 -	} -	mSnapshotUpToDate = FALSE; 		 - -	// Update snapshot source rect depending on whether we keep the aspect ratio. -	LLRect& rect = mImageRect[mCurImageIndex]; -	rect.set(0, getRect().getHeight(), getRect().getWidth(), 0); - -	F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight()); -	F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()); - -	if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot")) -	{ -		if (image_aspect_ratio > window_aspect_ratio) -		{ -			// trim off top and bottom -			S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio);  -			rect.mBottom += (getRect().getHeight() - new_height) / 2; -			rect.mTop -= (getRect().getHeight() - new_height) / 2; -		} -		else if (image_aspect_ratio < window_aspect_ratio) -		{ -			// trim off left and right -			S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio);  -			rect.mLeft += (getRect().getWidth() - new_width) / 2; -			rect.mRight -= (getRect().getWidth() - new_width) / 2; -		} -	} - -	// Stop shining animation. -	mShineAnimTimer.stop();  	// Update snapshot if requested.  	if (new_snapshot)  	{ +        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,69 +458,58 @@ void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_pare  	LLView::reshape(width, height, called_from_parent);  	if (old_rect.getWidth() != width || old_rect.getHeight() != height)  	{ -		lldebugs << "window reshaped, updating thumbnail" << llendl; -		updateSnapshot(FALSE, TRUE); +		lldebugs << "window reshaped, updating snapshot" << llendl; +		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 ; @@ -557,25 +545,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 ;  	} @@ -583,6 +608,55 @@ 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; +     +    // The big 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 big thumbnail size +    if (!raw->scale(getBigThumbnailWidth(), getBigThumbnailHeight())) +    { +        raw = NULL ; +    } +     +	if (raw) +	{ +        // 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. @@ -599,7 +673,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; @@ -617,157 +691,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(); - -	lldebugs << "producing snapshot" << llendl; -	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()); - -		if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE) +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; +} + +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   			lldebugs << "Encoding new image of format J2C" << llendl;  			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(); -			lldebugs << "Encoding new image of format " << format << llendl; - -			switch(format) -			{ -			case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG: -				previewp->mFormattedImage = new LLImagePNG();  -				break; -			case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG: -				previewp->mFormattedImage = new LLImageJPEG(previewp->mSnapshotQuality);  -				break; -			case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP: -				previewp->mFormattedImage = new LLImageBMP();  -				break; -			} -			if (previewp->mFormattedImage->encode(previewp->mPreviewImage, 0)) -			{ -				previewp->mDataSize = previewp->mFormattedImage->getDataSize(); -				// special case BMP to copy instead of decode otherwise decode will crash. -				if(format == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP) -				{ -					previewp->mPreviewImageEncoded->copy(previewp->mPreviewImage); -				} -				else -				{ -					previewp->mFormattedImage->decode(previewp->mPreviewImageEncoded, 0); -				} -			} -		} - -		LLPointer<LLImageRaw> scaled = new LLImageRaw( -			previewp->mPreviewImageEncoded->getData(), -			previewp->mPreviewImageEncoded->getWidth(), -			previewp->mPreviewImageEncoded->getHeight(), -			previewp->mPreviewImageEncoded->getComponents()); - -		if(!scaled->isBufferInvalid()) -		{ -			// leave original image dimensions, just scale up texture buffer -			if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024) -			{ -				// go ahead and shrink image to appropriate power of 2 for display -				scaled->biasedScaleToPowerOfTwo(1024); -				previewp->setImageScaled(TRUE); -			} -			else -			{ -				// expand image but keep original image data intact -				scaled->expandToPowerOfTwo(1024, FALSE); -			} - -			previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE); -			LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex]; -			gGL.getTexUnit(0)->bind(curr_preview_image); -			if (previewp->getSnapshotType() != SNAPSHOT_TEXTURE) -			{ -				curr_preview_image->setFilteringOption(LLTexUnit::TFO_POINT); -			} -			else -			{ -				curr_preview_image->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC); -			} -			curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP); - -			previewp->mSnapshotUpToDate = TRUE; -			previewp->generateThumbnailImage(TRUE) ; - -			previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal(); -			previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame +            // 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() ; -	} -	lldebugs << "done creating snapshot" << llendl; -	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) @@ -777,6 +950,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(); @@ -831,12 +1013,14 @@ void LLSnapshotLivePreview::saveTexture()  	}  	LLViewerStats::getInstance()->incStat(LLViewerStats::ST_SNAPSHOT_COUNT ); - -	mDataSize = 0;  }  BOOL LLSnapshotLivePreview::saveLocal()  { +    // Update mFormattedImage if necessary +    getFormattedImage(); +     +    // Save the formatted image  	BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage);  	if(success) @@ -848,6 +1032,9 @@ BOOL LLSnapshotLivePreview::saveLocal()  void LLSnapshotLivePreview::saveWeb()  { +    // Update mFormattedImage if necessary +    getFormattedImage(); +      	// *FIX: Will break if the window closes because of CloseSnapshotOnKeep!  	// Needs to pass on ownership of the image.  	LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get()); diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h index fe3d257b02..0e918d165e 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,33 +89,43 @@ 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 saveWeb();  	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 3*mThumbnailWidth ; } +	S32  getBigThumbnailHeight() const { return 3*mThumbnailHeight ; } +  	// Returns TRUE when snapshot generated, FALSE otherwise.  	static BOOL onIdle( void* snapshot_preview ); @@ -117,6 +133,8 @@ public:  	void regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z);  private: +    LLView*                     mViewContainer; +      	LLColor4					mColor;  	LLPointer<LLViewerTexture>	mViewerImage[2]; //used to represent the scene when the frame is frozen.  	LLRect						mImageRect[2]; @@ -133,11 +151,18 @@ private:  	BOOL                        mThumbnailUpdateLock ;  	BOOL                        mThumbnailUpToDate ;  	LLRect                      mThumbnailPlaceholderRect; +    BOOL                        mThumbnailSubsampled; // TRUE if the thumbnail is a subsampled version of the mPreviewImage +     +	LLPointer<LLViewerTexture>	mBigThumbnailImage ; +    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; @@ -154,6 +179,7 @@ private:  	LLQuaternion				mCameraRot;  	BOOL						mSnapshotActive;  	LLViewerWindow::ESnapshotType mSnapshotBufferType; +    std::string                 mFilterName;  public:  	static std::set<LLSnapshotLivePreview*> sList; diff --git a/indra/newview/lltwitterconnect.cpp b/indra/newview/lltwitterconnect.cpp new file mode 100644 index 0000000000..cfdbca1b81 --- /dev/null +++ b/indra/newview/lltwitterconnect.cpp @@ -0,0 +1,474 @@ +/**  + * @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 completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			LL_DEBUGS("TwitterConnect") << "Connect successful. content: " << content << LL_ENDL; +			 +            LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED); +		} +		else if (status != 302) +		{ +            LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED); +            log_twitter_connect_error("Connect", status, reason, content.get("error_code"), content.get("error_description")); +		} +	} +     +    void completedHeader(U32 status, const std::string& reason, const LLSD& content) +    { +        if (status == 302) +        { +            LLTwitterConnect::instance().openTwitterWeb(content["location"]); +        } +    } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLTwitterShareResponder : public LLHTTPClient::Responder +{ +	LOG_CLASS(LLTwitterShareResponder); +public: +     +	LLTwitterShareResponder() +	{ +		LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTING); +	} +	 +	virtual void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +            toast_user_for_twitter_success(); +			LL_DEBUGS("TwitterConnect") << "Post successful. content: " << content << LL_ENDL; +			 +			LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTED); +		} +		else if (status == 404) +		{ +			LLTwitterConnect::instance().connectToTwitter(); +		} +		else +		{ +            LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POST_FAILED); +            log_twitter_connect_error("Share", status, reason, content.get("error_code"), content.get("error_description")); +		} +	} +     +    void completedHeader(U32 status, const std::string& reason, const LLSD& content) +    { +        if (status == 302) +        { +            LLTwitterConnect::instance().openTwitterWeb(content["location"]); +        } +    } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +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 completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status))  +		{ +			LL_DEBUGS("TwitterConnect") << "Disconnect successful. content: " << content << LL_ENDL; +			setUserDisconnected(); + +		} +		//User not found so already disconnected +		else if(status == 404) +		{ +			LL_DEBUGS("TwitterConnect") << "Already disconnected. content: " << content << LL_ENDL; +			setUserDisconnected(); +		} +		else +		{ +			LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECT_FAILED); +            log_twitter_connect_error("Disconnect", status, reason, 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 completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			LL_DEBUGS("TwitterConnect") << "Connect successful. content: " << content << LL_ENDL; +             +            LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED); +		} +		else +		{ +			// show the twitter login page if not connected yet +			if (status == 404) +			{ +				if (mAutoConnect) +				{ +					LLTwitterConnect::instance().connectToTwitter(); +				} +				else +				{ +					LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED); +				} +			} +            else +            { +                LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED); +				log_twitter_connect_error("Connected", status, reason, content.get("error_code"), content.get("error_description")); +            } +		} +	} +     +private: +	bool mAutoConnect; +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLTwitterInfoResponder : public LLHTTPClient::Responder +{ +	LOG_CLASS(LLTwitterInfoResponder); +public: + +	virtual void completed(U32 status, const std::string& reason, const LLSD& info) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << "Twitter: Info received" << llendl; +			LL_DEBUGS("TwitterConnect") << "Getting Twitter info successful. info: " << info << LL_ENDL; +			LLTwitterConnect::instance().storeInfo(info); +		} +		else +		{ +			log_twitter_connect_error("Info", status, reason, info.get("error_code"), info.get("error_description")); +		} +	} + +	void completedHeader(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (status == 302) +		{ +			LLTwitterConnect::instance().openTwitterWeb(content["location"]); +		} +	} +}; + +/////////////////////////////////////////////////////////////////////////////// +// +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 a8eeddb798..982522955c 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -38,6 +38,7 @@  #include "llfloateravatar.h"  #include "llfloateravatarpicker.h"  #include "llfloateravatartextures.h" +#include "llfloaterbigpreview.h"  #include "llfloaterbeacons.h"  #include "llfloaterbuildoptions.h"  #include "llfloaterbuy.h" @@ -53,13 +54,15 @@  #include "llfloaterconversationlog.h"  #include "llfloaterconversationpreview.h"  #include "llfloaterdeleteenvpreset.h" +#include "llfloaterdestinations.h"  #include "llfloaterdisplayname.h"  #include "llfloatereditdaycycle.h"  #include "llfloatereditsky.h"  #include "llfloatereditwater.h"  #include "llfloaterenvironmentsettings.h"  #include "llfloaterevent.h" -#include "llfloaterdestinations.h" +#include "llfloaterfacebook.h" +#include "llfloaterflickr.h"  #include "llfloaterfonttest.h"  #include "llfloatergesture.h"  #include "llfloatergodtools.h" @@ -104,7 +107,6 @@  #include "llfloatersettingsdebug.h"  #include "llfloatersidepanelcontainer.h"  #include "llfloatersnapshot.h" -#include "llfloatersocial.h"  #include "llfloatersounddevices.h"  #include "llfloaterspellchecksettings.h"  #include "llfloatertelehub.h" @@ -116,6 +118,7 @@  #include "llfloatertopobjects.h"  #include "llfloatertoybox.h"  #include "llfloatertranslationsettings.h" +#include "llfloatertwitter.h"  #include "llfloateruipreview.h"  #include "llfloatervoiceeffect.h"  #include "llfloatervoicevolume.h" @@ -306,7 +309,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("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>); @@ -314,8 +316,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 21fb8d519b..3a3b00a604 100755 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -429,7 +429,7 @@ viewer_media_t LLViewerMedia::updateMediaImpl(LLMediaEntry* media_entry, const s  	// Try to find media with the same media ID  	viewer_media_t media_impl = getMediaImplFromTextureID(media_entry->getMediaID()); -	lldebugs << "called, current URL is \"" << media_entry->getCurrentURL()  +	lldebugs << "called, current URL is \"" << media_entry->getCurrentURL()  			<< "\", previous URL is \"" << previous_url   			<< "\", update_from_self is " << (update_from_self?"true":"false")  			<< llendl; @@ -1534,7 +1534,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("text/html", NULL, 0, 0); @@ -1543,7 +1543,7 @@ void LLViewerMedia::createSpareBrowserMediaSource()  /////////////////////////////////////////////////////////////////////////////////////////  // static -LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource()  +LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource()  {  	LLPluginClassMedia* result = sSpareBrowserMediaSource;  	sSpareBrowserMediaSource = NULL; @@ -1592,7 +1592,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));  } @@ -1669,7 +1669,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. @@ -1793,14 +1794,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) @@ -1812,7 +1815,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; @@ -1856,18 +1858,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); @@ -1922,7 +1924,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)  	{ @@ -2543,7 +2545,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(); @@ -2556,6 +2558,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 be78603e2d..a17dd953ad 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 c6ae7d7fa0..796d8fcae2 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1588,7 +1588,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 94c187e21a..751558fc93 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.pngBinary files differ new file mode 100644 index 0000000000..7fce9f0df2 --- /dev/null +++ b/indra/newview/skins/default/textures/toolbar_icons/flickr.png diff --git a/indra/newview/skins/default/textures/toolbar_icons/twitter.png b/indra/newview/skins/default/textures/toolbar_icons/twitter.pngBinary files differ new file mode 100644 index 0000000000..a99c490887 --- /dev/null +++ b/indra/newview/skins/default/textures/toolbar_icons/twitter.png 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_social.xml b/indra/newview/skins/default/xui/en/floater_facebook.xml index b7ff374d5f..544c443c76 100644 --- a/indra/newview/skins/default/xui/en/floater_social.xml +++ b/indra/newview/skins/default/xui/en/floater_facebook.xml @@ -3,9 +3,9 @@    positioning="cascading"    can_close="true"    can_resize="false" -  help_topic="floater_social" +  help_topic="floater_facebook"    layout="topleft" -  name="floater_social" +  name="floater_facebook"    save_rect="true"    single_instance="true"    reuse_instance="true" @@ -28,32 +28,39 @@       tab_position="top"       top="7"       height="437" -     halign="center"> +     halign="center" +     use_highlighting_on_hover="true">       <panel -       filename="panel_social_status.xml" -       class="llsocialstatuspanel" +       filename="panel_facebook_status.xml" +       class="llfacebookstatuspanel"         follows="all"         label="STATUS" -       name="panel_social_status"/> +       name="panel_facebook_status"/>       <panel -       filename="panel_social_photo.xml" -       class="llsocialphotopanel" +       filename="panel_facebook_photo.xml" +       class="llfacebookphotopanel"         follows="all"         label="PHOTO" -       name="panel_social_photo"/> +       name="panel_facebook_photo"/>       <panel -       filename="panel_social_place.xml" -       class="llsocialcheckinpanel" +       filename="panel_facebook_place.xml" +       class="llfacebookcheckinpanel"         follows="all"         label="CHECK IN" -       name="panel_social_place"/> +       name="panel_facebook_place"/>       <panel -       filename="panel_social_account.xml" -       class="llsocialaccountpanel" +       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_social_account"/>      -    </tab_container> +       name="panel_facebook_account"/> +   </tab_container>      <panel       name="connection_status_panel"       follows="left|bottom|right" diff --git a/indra/newview/skins/default/xui/en/floater_flickr.xml b/indra/newview/skins/default/xui/en/floater_flickr.xml new file mode 100644 index 0000000000..1a9ffd0489 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_flickr.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_flickr" +  layout="topleft" +  name="floater_flickr" +  save_rect="true" +  single_instance="true" +  reuse_instance="true" +  title="UPLOAD TO FLICKR" +  height="622" +  width="304"> +  <panel +   height="622" +   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="577" +     halign="center" +     use_highlighting_on_hover="true"> +     <panel +       filename="panel_flickr_photo.xml" +       class="llflickrphotopanel" +       follows="all" +       label="PHOTO" +       name="panel_flickr_photo"/> +     <panel +       filename="panel_flickr_account.xml" +       class="llflickraccountpanel" +       follows="all" +       label="ACCOUNT" +       name="panel_flickr_account"/>      +    </tab_container> +    <panel +     name="connection_status_panel" +     follows="left|bottom|right" +     height="24"> +     <text +      name="connection_error_text" +      type="string" +      follows="left|bottom|right" +      top="5" +      left="9" +      width="250" +      height="20" +      wrap="true" +      halign="left" +      valign="center" +      text_color="DrYellow" +      font="SansSerif"> +      Error +     </text> +     <loading_indicator +      follows="left|bottom|right" +      height="24" +      width="24" +      name="connection_loading_indicator" +      top="2" +      left="9" +      visible="true"/> +     <text +      name="connection_loading_text" +      type="string" +      follows="left|bottom|right" +      top="5" +      left_pad="5" +      width="250" +      height="20" +      wrap="true" +      halign="left" +      valign="center" +      text_color="EmphasisColor" +      font="SansSerif"> +      Loading... +    </text> +  </panel> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_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 64de010eb5..e82f6339d4 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 @@ -288,6 +280,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 a93601f5fd..7e3d3e5d59 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6044,7 +6044,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..f091d2e9b9 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,7 @@  	 height="400"  	 width="304"  	 layout="topleft" -   name="panel_social_account"> +   name="panel_facebook_account">    <string        name="facebook_connected"        value="You are connected to Facebook as:" /> 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..d772dde0c5 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_facebook_friends.xml @@ -0,0 +1,35 @@ +<panel +	 height="400" +	 width="304" +	 layout="topleft" +   name="panel_facebook_friends"> +  <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_suggested_friends" +     title="People you may want to friend"> +      <avatar_list +       ignore_online_status="true" +       allow_select="true" +       follows="all" +       height="173" +       layout="topleft" +       left="0" +       name="suggested_friends" +       show_permissions_granted="true" +       top="0" +       width="307" /> +    </accordion_tab> +  </accordion> +</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 a55613b52a..1d826fdbe1 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,7 @@        height="400"        width="304"        layout="topleft" -      name="panel_social_photo"> +      name="panel_facebook_photo">        <layout_stack  	   layout="topleft"         border_size="0" @@ -15,7 +15,7 @@           name="snapshot_panel"           height="367">              <combo_box -             control_name="SocialPhotoResolution" +             control_name="FacebookPhotoResolution"               follows="left|top"               top="6"               left="9" @@ -39,20 +39,25 @@                 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" @@ -81,7 +86,7 @@                  text_color="EmphasisColor"                  height="14"                  top_pad="-19" -                left_pad="-20" +                left_pad="-30"                  length="1"                  halign="center"                  name="working_lbl" @@ -91,6 +96,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" 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..1eea8f317b 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,7 @@        height="400"        width="304"  	  layout="topleft" -      name="panel_social_place"> +      name="panel_facebook_place">        <layout_stack  	    layout="topleft"          border_size="0" 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..50e15c2e80 100644 --- a/indra/newview/skins/default/xui/en/panel_social_status.xml +++ b/indra/newview/skins/default/xui/en/panel_facebook_status.xml @@ -2,7 +2,7 @@  	 height="400"  	 width="304"  	 layout="topleft" -     name="panel_social_status"> +     name="panel_facebook_status">       <layout_stack        layout="topleft"        border_size="0" 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..3a38852049 --- /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/ta-p/2149711 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..4a413bd711 --- /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/ta-p/2149711 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 7e79d297ef..51576f71a8 100755 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -148,15 +148,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 & White</string> +    <string name="Colors1970">1970'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 --> @@ -3444,6 +3469,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) @@ -3861,6 +3892,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> @@ -3876,8 +3909,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> @@ -3889,6 +3922,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> @@ -3904,8 +3939,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 fe0774b409..748655e4ed 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=""): | 
