diff options
Diffstat (limited to 'indra')
87 files changed, 5932 insertions, 241 deletions
diff --git a/indra/integration_tests/llimage_libtest/1970colorize.xml b/indra/integration_tests/llimage_libtest/1970colorize.xml new file mode 100644 index 0000000000..0dab2489a0 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/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/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/brighten.xml b/indra/integration_tests/llimage_libtest/brighten.xml new file mode 100755 index 0000000000..d17b96d2d7 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/brighten.xml @@ -0,0 +1,11 @@ +<llsd> + <array> + <array> + <string>brighten</string> + <real>50.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/colorize.xml b/indra/integration_tests/llimage_libtest/colorize.xml new file mode 100644 index 0000000000..18c6cd3425 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/colorize.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>colorize</string> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + <real>0.2</real> + <real>0.0</real> + <real>0.2</real> + </array> + </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/contrast.xml b/indra/integration_tests/llimage_libtest/contrast.xml new file mode 100644 index 0000000000..8dcdd1a9a9 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/contrast.xml @@ -0,0 +1,18 @@ +<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>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/darken.xml b/indra/integration_tests/llimage_libtest/darken.xml new file mode 100755 index 0000000000..8d110452e9 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/darken.xml @@ -0,0 +1,11 @@ +<llsd> + <array> + <array> + <string>darken</string> + <real>50.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/gamma.xml b/indra/integration_tests/llimage_libtest/gamma.xml new file mode 100644 index 0000000000..7505a03027 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/gamma.xml @@ -0,0 +1,18 @@ +<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>gamma</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/grayscale.xml b/indra/integration_tests/llimage_libtest/grayscale.xml new file mode 100644 index 0000000000..984312c4fd --- /dev/null +++ b/indra/integration_tests/llimage_libtest/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/horizontalscreen.xml b/indra/integration_tests/llimage_libtest/horizontalscreen.xml new file mode 100644 index 0000000000..ddff4d1977 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/horizontalscreen.xml @@ -0,0 +1,25 @@ +<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>blend</string> + <real>0.0</real> + <real>0.0</real> + </array> + <array> + <string>screen</string> + <string>line</string> + <real>5.0</real> + <real>0.0</real> + </array> + </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/linearize.xml b/indra/integration_tests/llimage_libtest/linearize.xml new file mode 100755 index 0000000000..23d0290e07 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/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/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp index 034c816742..d3373a61f2 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; } @@ -568,7 +593,15 @@ int main(int argc, char** argv) std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl; continue; } - + + if (filter_name != "") + { + // We're interpreting the filter as a filter file name + LLImageFilter filter; + filter.loadFromFile(filter_name); + filter.executeFilter(raw_image); + } + // Save file if (out_file != out_end) { diff --git a/indra/integration_tests/llimage_libtest/newsscreen.xml b/indra/integration_tests/llimage_libtest/newsscreen.xml new file mode 100755 index 0000000000..8247c34500 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/newsscreen.xml @@ -0,0 +1,25 @@ +<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>blend</string> + <real>0.0</real> + <real>0.0</real> + </array> + <array> + <string>screen</string> + <string>2Dsine</string> + <real>5.0</real> + <real>0.0</real> + </array> + </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/posterize.xml b/indra/integration_tests/llimage_libtest/posterize.xml new file mode 100755 index 0000000000..f026278f9e --- /dev/null +++ b/indra/integration_tests/llimage_libtest/posterize.xml @@ -0,0 +1,18 @@ +<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>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/rotatecolors180.xml b/indra/integration_tests/llimage_libtest/rotatecolors180.xml new file mode 100644 index 0000000000..e25029720f --- /dev/null +++ b/indra/integration_tests/llimage_libtest/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/saturate.xml b/indra/integration_tests/llimage_libtest/saturate.xml new file mode 100644 index 0000000000..b77f07a037 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/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/sepia.xml b/indra/integration_tests/llimage_libtest/sepia.xml new file mode 100644 index 0000000000..0304ead015 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/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/slantedscreen.xml b/indra/integration_tests/llimage_libtest/slantedscreen.xml new file mode 100644 index 0000000000..63ad01d51d --- /dev/null +++ b/indra/integration_tests/llimage_libtest/slantedscreen.xml @@ -0,0 +1,25 @@ +<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>blend</string> + <real>0.0</real> + <real>0.0</real> + </array> + <array> + <string>screen</string> + <string>line</string> + <real>5.0</real> + <real>45.0</real> + </array> + </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/spotlight.xml b/indra/integration_tests/llimage_libtest/spotlight.xml new file mode 100644 index 0000000000..203130bdee --- /dev/null +++ b/indra/integration_tests/llimage_libtest/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/verticalscreen.xml b/indra/integration_tests/llimage_libtest/verticalscreen.xml new file mode 100644 index 0000000000..71e48df656 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/verticalscreen.xml @@ -0,0 +1,25 @@ +<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>blend</string> + <real>0.0</real> + <real>0.0</real> + </array> + <array> + <string>screen</string> + <string>line</string> + <real>5.0</real> + <real>90.0</real> + </array> + </array> +</llsd> diff --git a/indra/integration_tests/llimage_libtest/video.xml b/indra/integration_tests/llimage_libtest/video.xml new file mode 100755 index 0000000000..8b10687ef5 --- /dev/null +++ b/indra/integration_tests/llimage_libtest/video.xml @@ -0,0 +1,23 @@ +<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>lines</string> + <real>10.0</real> + <real>0.0</real> + </array> + <array> + <string>brighten</string> + <real>100.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + </array> +</llsd> 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/llimagefilter.cpp b/indra/llimage/llimagefilter.cpp new file mode 100755 index 0000000000..5c969001b7 --- /dev/null +++ b/indra/llimage/llimagefilter.cpp @@ -0,0 +1,768 @@ +/** + * @file llimagefilter.cpp + * @brief Simple Image Filtering. + * + * $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" + +//--------------------------------------------------------------------------- +// LLImageFilter +//--------------------------------------------------------------------------- + +LLImageFilter::LLImageFilter() : + mFilterData(LLSD::emptyArray()), + mImage(NULL), + mHistoRed(NULL), + mHistoGreen(NULL), + mHistoBlue(NULL), + mHistoBrightness(NULL), + mVignetteMode(VIGNETTE_MODE_NONE), + mVignetteGamma(1.0), + mVignetteMin(0.0) +{ +} + +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 vignette to stencil + * Separate shape from mode + * Add shapes : uniform and gradients + * Add modes + * Add stencil (min,max) range + * Suppress alpha from colorcorrect and use uniform alpha instead + * Refactor stencil composition in the filter primitives + + <array> + <string>stencil</string> + <string>shape</string> + <string>blend_mode</string> + <real>min</real> + <real>max</real> + <real>param1</real> + <real>param2</real> + <real>param3</real> + <real>param4</real> + </array> + + vignette : center_x, center_y, width, feather + sine : wavelength, angle + flat + gradient : start_x, start_y, end_x, end_y + + * Document all the admissible names in the wiki + + " Apply the filter <name> to the input images using the optional <param> value. Admissible names:\n" + " - 'grayscale' converts to grayscale (no param).\n" + " - 'sepia' converts to sepia (no param).\n" + " - 'saturate' changes color saturation according to <param>: < 1.0 will desaturate, > 1.0 will saturate.\n" + " - 'rotate' rotates the color hue according to <param> (in degree, positive value only).\n" + " - 'gamma' applies gamma curve <param> to all channels: > 1.0 will darken, < 1.0 will lighten.\n" + " - 'colorize' applies a red tint to the image using <param> as an alpha (transparency between 0.0 and 1.0) value.\n" + " - 'contrast' modifies the contrast according to <param> : > 1.0 will enhance the contrast, <1.0 will flatten it.\n" + " - 'brighten' adds <param> light to the image (<param> between 0 and 255).\n" + " - 'darken' substracts <param> light to the image (<param> between 0 and 255).\n" + " - 'linearize' optimizes the contrast using the brightness histogram. <param> is the fraction (between 0.0 and 1.0) of discarded tail of the histogram.\n" + " - 'posterize' redistributes the colors between <param> classes per channel (<param> between 2 and 255).\n" + " - 'newsscreen' applies a 2D sine screening to the red channel and output to black and white.\n" + " - 'horizontalscreen' applies a horizontal screening to the red channel and output to black and white.\n" + " - 'verticalscreen' applies a vertical screening to the red channel and output to black and white.\n" + " - 'slantedscreen' applies a 45 degrees slanted screening to the red channel and output to black and white.\n" + " - Any other value will be interpreted as a file name describing a sequence of filters and parameters to be applied to the input images.\n" + + " Apply a circular central vignette <name> to the filter using the optional <feather> and <min> values. Admissible names:\n" + " - 'blend' : the filter is applied with full intensity in the center and blends with the image to the periphery.\n" + " - 'fade' : the filter is applied with full intensity in the center and fades to black to the periphery.\n" + */ + +//============================================================================ +// Load filter from file +//============================================================================ + +void LLImageFilter::loadFromFile(const std::string& file_path) +{ + //std::cout << "Loading filter settings from : " << file_path << std::endl; + 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(); + } + else + { + // File couldn't be open, reset the filter data + mFilterData = LLSD(); + } +} + +//============================================================================ +// 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; + + // Execute the filter described on this line + if (filter_name == "blend") + { + setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_CENTER,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal())); + } + else if (filter_name == "fade") + { + setVignette(VIGNETTE_MODE_FADE,VIGNETTE_TYPE_CENTER,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal())); + } + else if (filter_name == "lines") + { + setVignette(VIGNETTE_MODE_BLEND,VIGNETTE_TYPE_LINES,(float)(mFilterData[i][1].asReal()),(float)(mFilterData[i][2].asReal())); + } + 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((S32)(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((S32)(-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,(S32)(mFilterData[i][2].asReal()),(F32)(mFilterData[i][3].asReal())); + } + } +} + +//============================================================================ +// Filter Primitives +//============================================================================ + +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++) + { + if (mVignetteMode == VIGNETTE_MODE_NONE) + { + dst_data[VRED] = lut_red[dst_data[VRED]]; + dst_data[VGREEN] = lut_green[dst_data[VGREEN]]; + dst_data[VBLUE] = lut_blue[dst_data[VBLUE]]; + } + else + { + F32 alpha = getVignetteAlpha(i,j); + if (mVignetteMode == VIGNETTE_MODE_BLEND) + { + // Blends with the source image on the edges + F32 inv_alpha = 1.0 - alpha; + dst_data[VRED] = inv_alpha * dst_data[VRED] + alpha * lut_red[dst_data[VRED]]; + dst_data[VGREEN] = inv_alpha * dst_data[VGREEN] + alpha * lut_green[dst_data[VGREEN]]; + dst_data[VBLUE] = inv_alpha * dst_data[VBLUE] + alpha * lut_blue[dst_data[VBLUE]]; + } + else // VIGNETTE_MODE_FADE + { + // Fade to black on the edges + dst_data[VRED] = alpha * lut_red[dst_data[VRED]]; + dst_data[VGREEN] = alpha * lut_green[dst_data[VGREEN]]; + dst_data[VBLUE] = alpha * 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++) + { + LLVector3 src((F32)(dst_data[VRED]),(F32)(dst_data[VGREEN]),(F32)(dst_data[VBLUE])); + LLVector3 dst = src * transform; + dst.clamp(0.0f,255.0f); + if (mVignetteMode == VIGNETTE_MODE_NONE) + { + dst_data[VRED] = dst.mV[VRED]; + dst_data[VGREEN] = dst.mV[VGREEN]; + dst_data[VBLUE] = dst.mV[VBLUE]; + } + else + { + F32 alpha = getVignetteAlpha(i,j); + if (mVignetteMode == VIGNETTE_MODE_BLEND) + { + // Blends with the source image on the edges + F32 inv_alpha = 1.0 - alpha; + dst_data[VRED] = inv_alpha * src.mV[VRED] + alpha * dst.mV[VRED]; + dst_data[VGREEN] = inv_alpha * src.mV[VGREEN] + alpha * dst.mV[VGREEN]; + dst_data[VBLUE] = inv_alpha * src.mV[VBLUE] + alpha * dst.mV[VBLUE]; + } + else // VIGNETTE_MODE_FADE + { + // Fade to black on the edges + dst_data[VRED] = alpha * dst.mV[VRED]; + dst_data[VGREEN] = alpha * dst.mV[VGREEN]; + dst_data[VBLUE] = alpha * dst.mV[VBLUE]; + } + } + dst_data += components; + } + } +} + +void LLImageFilter::filterScreen(EScreenMode mode, const S32 wave_length, const F32 angle) +{ + const S32 components = mImage->getComponents(); + llassert( components >= 1 && components <= 4 ); + + S32 width = mImage->getWidth(); + S32 height = mImage->getHeight(); + + F32 sin = sinf(angle*DEG_TO_RAD); + F32 cos = cosf(angle*DEG_TO_RAD); + + U8* dst_data = mImage->getData(); + for (S32 j = 0; j < height; j++) + { + for (S32 i = 0; i < width; i++) + { + F32 value = 0.0; + F32 d = 0.0; + switch (mode) + { + case SCREEN_MODE_2DSINE: + value = (sinf(2*F_PI*i/wave_length)*sinf(2*F_PI*j/wave_length)+1.0)*255.0/2.0; + break; + case SCREEN_MODE_LINE: + d = sin*i - cos*j; + value = (sinf(2*F_PI*d/wave_length)+1.0)*255.0/2.0; + break; + } + U8 dst_value = (dst_data[VRED] >= (U8)(value) ? 255 : 0); + + if (mVignetteMode == VIGNETTE_MODE_NONE) + { + dst_data[VRED] = dst_value; + dst_data[VGREEN] = dst_value; + dst_data[VBLUE] = dst_value; + } + else + { + F32 alpha = getVignetteAlpha(i,j); + if (mVignetteMode == VIGNETTE_MODE_BLEND) + { + // Blends with the source image on the edges + F32 inv_alpha = 1.0 - alpha; + dst_data[VRED] = inv_alpha * dst_data[VRED] + alpha * dst_value; + dst_data[VGREEN] = inv_alpha * dst_data[VGREEN] + alpha * dst_value; + dst_data[VBLUE] = inv_alpha * dst_data[VBLUE] + alpha * dst_value; + } + else // VIGNETTE_MODE_FADE + { + // Fade to black on the edges + dst_data[VRED] = alpha * dst_value; + dst_data[VGREEN] = alpha * dst_value; + dst_data[VBLUE] = alpha * dst_value; + } + } + dst_data += components; + } + } +} + +//============================================================================ +// Procedural Stencils +//============================================================================ + +void LLImageFilter::setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min) +{ + mVignetteMode = mode; + mVignetteType = type; + mVignetteGamma = gamma; + mVignetteMin = llclampf(min); + // We always center the vignette on the image and fits it in the image smallest dimension + mVignetteCenterX = mImage->getWidth()/2; + mVignetteCenterY = mImage->getHeight()/2; + mVignetteWidth = llmin(mImage->getWidth()/2,mImage->getHeight()/2); +} + +F32 LLImageFilter::getVignetteAlpha(S32 i, S32 j) +{ + F32 alpha = 1.0; + if (mVignetteType == VIGNETTE_TYPE_CENTER) + { + // 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 - mVignetteCenterX)*(i - mVignetteCenterX) + (j - mVignetteCenterY)*(j - mVignetteCenterY); + alpha = powf(F_E, -(powf((d_center_square/(mVignetteWidth*mVignetteWidth)),mVignetteGamma)/2.0f)); + } + else if (mVignetteType == VIGNETTE_TYPE_LINES) + { + // alpha varies according to a squared sine function vertically. + // gamma is interpreted as the wavelength (in pixels) of the sine in that case. + alpha = (sinf(2*F_PI*j/mVignetteGamma) > 0.0 ? 1.0 : 0.0); + } + // We rescale alpha between min and 1.0 so to avoid complete fading if so desired. + return (mVignetteMin + alpha * (1.0 - mVignetteMin)); +} + +//============================================================================ +// 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,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(S32 add, const LLColor3& alpha) +{ + U8 brightness_red_lut[256]; + U8 brightness_green_lut[256]; + U8 brightness_blue_lut[256]; + + for (S32 i = 0; i < 256; i++) + { + U8 value_i = (U8)(llclampb((S32)((S32)(i) + add))); + // 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..c67789ede0 --- /dev/null +++ b/indra/llimage/llimagefilter.h @@ -0,0 +1,119 @@ +/** + * @file llimagefilter.h + * @brief Simple Image Filtering. + * + * $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_vignette_mode +{ + VIGNETTE_MODE_NONE = 0, + VIGNETTE_MODE_BLEND = 1, + VIGNETTE_MODE_FADE = 2 +} EVignetteMode; + +typedef enum e_vignette_type +{ + VIGNETTE_TYPE_CENTER = 0, + VIGNETTE_TYPE_LINES = 1 +} EVignetteType; + +typedef enum e_screen_mode +{ + SCREEN_MODE_2DSINE = 0, + SCREEN_MODE_LINE = 1 +} EScreenMode; + +//============================================================================ +// Image Filter + +class LLImageFilter +{ +public: + LLImageFilter(); + ~LLImageFilter(); + + void loadFromFile(const std::string& file_path); + 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(S32 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 S32 wave_length, const F32 angle); + + // Procedural Stencils + void setVignette(EVignetteMode mode, EVignetteType type, F32 gamma, F32 min); + F32 getVignetteAlpha(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; + + // Vignette filtering + EVignetteMode mVignetteMode; + EVignetteType mVignetteType; + S32 mVignetteCenterX; + S32 mVignetteCenterY; + S32 mVignetteWidth; + F32 mVignetteGamma; + F32 mVignetteMin; +}; + + +#endif diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c5e1cde4e6..e458193142 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 @@ -228,6 +229,8 @@ set(viewer_SOURCE_FILES llfloatereditwater.cpp llfloaterenvironmentsettings.cpp llfloaterevent.cpp + llfloaterfacebook.cpp + llfloaterflickr.cpp llfloaterfonttest.cpp llfloatergesture.cpp llfloatergodtools.cpp @@ -274,7 +277,6 @@ set(viewer_SOURCE_FILES llfloatersettingsdebug.cpp llfloatersidepanelcontainer.cpp llfloatersnapshot.cpp - llfloatersocial.cpp llfloatersounddevices.cpp llfloaterspellchecksettings.cpp llfloatertelehub.cpp @@ -286,6 +288,7 @@ set(viewer_SOURCE_FILES llfloatertos.cpp llfloatertoybox.cpp llfloatertranslationsettings.cpp + llfloatertwitter.cpp llfloateruipreview.cpp llfloaterurlentry.cpp llfloatervoiceeffect.cpp @@ -325,6 +328,7 @@ set(viewer_SOURCE_FILES llfloaterimsessiontab.cpp llfloaterimsession.cpp llfloaterimcontainer.cpp + llimagefiltersmanager.cpp llimhandler.cpp llimview.cpp llinspect.cpp @@ -567,6 +571,7 @@ set(viewer_SOURCE_FILES lltransientdockablefloater.cpp lltransientfloatermgr.cpp lltranslate.cpp + lltwitterconnect.cpp lluilistener.cpp lluploaddialog.cpp lluploadfloaterobservers.cpp @@ -787,6 +792,7 @@ set(viewer_HEADER_FILES llfilteredwearablelist.h llfirstuse.h llflexibleobject.h + llflickrconnect.h llfloaterabout.h llfloaterbvhpreview.h llfloaterauction.h @@ -816,6 +822,8 @@ set(viewer_HEADER_FILES llfloatereditwater.h llfloaterenvironmentsettings.h llfloaterevent.h + llfloaterfacebook.h + llfloaterflickr.h llfloaterfonttest.h llfloatergesture.h llfloatergodtools.h @@ -862,7 +870,6 @@ set(viewer_HEADER_FILES llfloatersettingsdebug.h llfloatersidepanelcontainer.h llfloatersnapshot.h - llfloatersocial.h llfloatersounddevices.h llfloaterspellchecksettings.h llfloatertelehub.h @@ -874,6 +881,7 @@ set(viewer_HEADER_FILES llfloatertos.h llfloatertoybox.h llfloatertranslationsettings.h + llfloatertwitter.h llfloateruipreview.h llfloaterurlentry.h llfloatervoiceeffect.h @@ -912,6 +920,7 @@ set(viewer_HEADER_FILES llfloaterimsessiontab.h llfloaterimsession.h llfloaterimcontainer.h + llimagefiltersmanager.h llimview.h llinspect.h llinspectavatar.h @@ -1145,6 +1154,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 ce878f156b..d332583ca9 100755 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -260,15 +260,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/1970s color.xml b/indra/newview/app_settings/filters/1970s color.xml new file mode 100644 index 0000000000..0dab2489a0 --- /dev/null +++ b/indra/newview/app_settings/filters/1970s color.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/newview/app_settings/filters/Black & white.xml b/indra/newview/app_settings/filters/Black & white.xml new file mode 100644 index 0000000000..101ed8233a --- /dev/null +++ b/indra/newview/app_settings/filters/Black & white.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/Heatwave.xml b/indra/newview/app_settings/filters/Heatwave.xml new file mode 100644 index 0000000000..5c47ca0f84 --- /dev/null +++ b/indra/newview/app_settings/filters/Heatwave.xml @@ -0,0 +1,32 @@ +<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>fade</string> + <real>4.0</real> + <real>0.5</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/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/Jules Verne.xml b/indra/newview/app_settings/filters/Jules Verne.xml new file mode 100644 index 0000000000..ddff4d1977 --- /dev/null +++ b/indra/newview/app_settings/filters/Jules Verne.xml @@ -0,0 +1,25 @@ +<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>blend</string> + <real>0.0</real> + <real>0.0</real> + </array> + <array> + <string>screen</string> + <string>line</string> + <real>5.0</real> + <real>0.0</real> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/filters/Newspaper.xml b/indra/newview/app_settings/filters/Newspaper.xml new file mode 100755 index 0000000000..8247c34500 --- /dev/null +++ b/indra/newview/app_settings/filters/Newspaper.xml @@ -0,0 +1,25 @@ +<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>blend</string> + <real>0.0</real> + <real>0.0</real> + </array> + <array> + <string>screen</string> + <string>2Dsine</string> + <real>5.0</real> + <real>0.0</real> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/filters/Overcast.xml b/indra/newview/app_settings/filters/Overcast.xml new file mode 100644 index 0000000000..dce5ab3e9e --- /dev/null +++ b/indra/newview/app_settings/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/newview/app_settings/filters/Sepia.xml b/indra/newview/app_settings/filters/Sepia.xml new file mode 100644 index 0000000000..d26df608b9 --- /dev/null +++ b/indra/newview/app_settings/filters/Sepia.xml @@ -0,0 +1,26 @@ +<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>fade</string> + <real>4.0</real> + <real>0.5</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..203130bdee --- /dev/null +++ b/indra/newview/app_settings/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/newview/app_settings/filters/Video.xml b/indra/newview/app_settings/filters/Video.xml new file mode 100755 index 0000000000..144f340f12 --- /dev/null +++ b/indra/newview/app_settings/filters/Video.xml @@ -0,0 +1,30 @@ +<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>15.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + <array> + <string>lines</string> + <real>10.0</real> + <real>0.0</real> + </array> + <array> + <string>brighten</string> + <real>30.0</real> + <real>1.0</real> + <real>1.0</real> + <real>1.0</real> + </array> + </array> +</llsd> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index d9093c2a6d..a82d0b3f95 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13119,7 +13119,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..f9a3a21e94 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()); 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..8f9f55c1ea --- /dev/null +++ b/indra/newview/llflickrconnect.cpp @@ -0,0 +1,479 @@ +/** + * @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=\"snapshot." << imageFormat << "\"\r\n" + << "Content-Type: image/" << imageFormat << "\r\n\r\n"; + + // Insert the image data. + // *FIX: Treating this as a string will probably screw it up ... + U8* image_data = image->getData(); + for (S32 i = 0; i < image->getDataSize(); ++i) + { + body << image_data[i]; + } + + body << "\r\n--" << boundary << "--\r\n"; + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = body.str().size(); + U8 *data = new U8[size]; + memcpy(data, body.str().data(), size); + + // Note: we can use that route for different publish action. We should be able to use the same responder. + LLHTTPClient::postRaw(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) + { + LLSD state_info; + state_info["enum"] = connection_state; + sStateWatcher->post(state_info); + } + + mConnectionState = connection_state; +} + +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/llfloatersocial.cpp b/indra/newview/llfloaterfacebook.cpp index 2a74c8e3ea..7bf74ef6ba 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,7 +27,7 @@ #include "llviewerprecompiledheaders.h" -#include "llfloatersocial.h" +#include "llfloaterfacebook.h" #include "llagent.h" #include "llagentui.h" @@ -46,11 +46,12 @@ #include "llviewerregion.h" #include "llviewercontrol.h" #include "llviewermedia.h" +#include "lltabcontainer.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<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 +59,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 +77,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 +106,7 @@ BOOL LLSocialStatusPanel::postBuild() return LLPanel::postBuild(); } -void LLSocialStatusPanel::draw() +void LLFacebookStatusPanel::draw() { if (mMessageTextEditor && mPostButton && mCancelButton) { @@ -106,10 +120,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 +136,7 @@ void LLSocialStatusPanel::onSend() } } -bool LLSocialStatusPanel::onFacebookConnectStateChange(const LLSD& data) +bool LLFacebookStatusPanel::onFacebookConnectStateChange(const LLSD& data) { switch (data.get("enum").asInteger()) { @@ -131,7 +145,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 +153,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 +162,7 @@ void LLSocialStatusPanel::sendStatus() } } -void LLSocialStatusPanel::clearAndClose() +void LLFacebookStatusPanel::clearAndClose() { mMessageTextEditor->setValue(""); @@ -160,10 +174,10 @@ void LLSocialStatusPanel::clearAndClose() } /////////////////////////// -//LLSocialPhotoPanel/////// +//LLFacebookPhotoPanel/////// /////////////////////////// -LLSocialPhotoPanel::LLSocialPhotoPanel() : +LLFacebookPhotoPanel::LLFacebookPhotoPanel() : mSnapshotPanel(NULL), mResolutionComboBox(NULL), mRefreshBtn(NULL), @@ -171,13 +185,14 @@ mWorkingLabel(NULL), mThumbnailPlaceholder(NULL), mCaptionTextBox(NULL), mLocationCheckbox(NULL), -mPostButton(NULL) +mPostButton(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)); } -LLSocialPhotoPanel::~LLSocialPhotoPanel() +LLFacebookPhotoPanel::~LLFacebookPhotoPanel() { if(mPreviewHandle.get()) { @@ -185,13 +200,13 @@ 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->setCommitCallback(boost::bind(&LLFacebookPhotoPanel::updateResolution, this, TRUE)); mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn"); mWorkingLabel = getChild<LLUICtrl>("working_lbl"); mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder"); @@ -203,7 +218,7 @@ BOOL LLSocialPhotoPanel::postBuild() return LLPanel::postBuild(); } -void LLSocialPhotoPanel::draw() +void LLFacebookPhotoPanel::draw() { LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get()); @@ -256,13 +271,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 +298,11 @@ void LLSocialPhotoPanel::onVisibilityChange(const LLSD& new_visibility) p.rect(full_screen_rect); LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); mPreviewHandle = previewp->getHandle(); + mQuality = MAX_QUALITY; previewp->setSnapshotType(previewp->SNAPSHOT_WEB); previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG); - //previewp->setSnapshotQuality(98); + previewp->setSnapshotQuality(mQuality, false); previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect()); updateControls(); @@ -294,7 +310,7 @@ void LLSocialPhotoPanel::onVisibilityChange(const LLSD& new_visibility) } } -void LLSocialPhotoPanel::onClickNewSnapshot() +void LLFacebookPhotoPanel::onClickNewSnapshot() { LLSnapshotLivePreview* previewp = getPreviewView(); if (previewp) @@ -305,10 +321,10 @@ void LLSocialPhotoPanel::onClickNewSnapshot() } } -void LLSocialPhotoPanel::onSend() +void LLFacebookPhotoPanel::onSend() { - LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialPhotoPanel"); // just in case it is already listening - LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialPhotoPanel", boost::bind(&LLSocialPhotoPanel::onFacebookConnectStateChange, this, _1)); + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLFacebookPhotoPanel"); // just in case it is already listening + LLEventPumps::instance().obtain("FacebookConnectState").listen("LLFacebookPhotoPanel", boost::bind(&LLFacebookPhotoPanel::onFacebookConnectStateChange, this, _1)); // Connect to Facebook if necessary and then post if (LLFacebookConnect::instance().isConnected()) @@ -321,7 +337,7 @@ void LLSocialPhotoPanel::onSend() } } -bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data) +bool LLFacebookPhotoPanel::onFacebookConnectStateChange(const LLSD& data) { switch (data.get("enum").asInteger()) { @@ -330,7 +346,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 +354,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 +387,7 @@ void LLSocialPhotoPanel::sendPhoto() updateControls(); } -void LLSocialPhotoPanel::clearAndClose() +void LLFacebookPhotoPanel::clearAndClose() { mCaptionTextBox->setValue(""); @@ -382,7 +398,7 @@ void LLSocialPhotoPanel::clearAndClose() } } -void LLSocialPhotoPanel::updateControls() +void LLFacebookPhotoPanel::updateControls() { LLSnapshotLivePreview* previewp = getPreviewView(); BOOL got_bytes = previewp && previewp->getDataSize() > 0; @@ -409,7 +425,7 @@ void LLSocialPhotoPanel::updateControls() updateResolution(FALSE); } -void LLSocialPhotoPanel::updateResolution(BOOL do_update) +void LLFacebookPhotoPanel::updateResolution(BOOL do_update) { LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox); @@ -443,8 +459,12 @@ void LLSocialPhotoPanel::updateResolution(BOOL do_update) checkAspectRatio(width); previewp->getSize(width, height); + + // Recompute quality setting + mQuality = compute_jpeg_quality(width, height); + bool quality_reset = previewp->setSnapshotQuality(mQuality, false); - if(original_width != width || original_height != height) + if (original_width != width || original_height != height || quality_reset) { previewp->setSize(width, height); @@ -452,18 +472,18 @@ void LLSocialPhotoPanel::updateResolution(BOOL do_update) lldebugs << "updating thumbnail" << llendl; previewp->updateSnapshot(FALSE, TRUE); - if(do_update) + if (do_update || quality_reset) { lldebugs << "Will update controls" << llendl; updateControls(); - LLSocialPhotoPanel::onClickNewSnapshot(); + LLFacebookPhotoPanel::onClickNewSnapshot(); } } } } -void LLSocialPhotoPanel::checkAspectRatio(S32 index) +void LLFacebookPhotoPanel::checkAspectRatio(S32 index) { LLSnapshotLivePreview *previewp = getPreviewView() ; @@ -484,23 +504,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 +534,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 +575,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 +591,7 @@ void LLSocialCheckinPanel::onSend() } } -bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data) +bool LLFacebookCheckinPanel::onFacebookConnectStateChange(const LLSD& data) { switch (data.get("enum").asInteger()) { @@ -580,7 +600,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 +608,7 @@ bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data) return false; } -void LLSocialCheckinPanel::sendCheckin() +void LLFacebookCheckinPanel::sendCheckin() { // Get the location SLURL LLSLURL slurl; @@ -606,7 +626,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 +648,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 +660,23 @@ void LLSocialCheckinPanel::clearAndClose() } /////////////////////////// -//LLSocialAccountPanel////// +//LLFacebookAccountPanel////// /////////////////////////// -LLSocialAccountPanel::LLSocialAccountPanel() : +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 +687,7 @@ BOOL LLSocialAccountPanel::postBuild() return LLPanel::postBuild(); } -void LLSocialAccountPanel::draw() +void LLFacebookAccountPanel::draw() { LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState(); @@ -677,17 +702,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 +732,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 +755,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 +771,7 @@ bool LLSocialAccountPanel::onFacebookConnectInfoChange() return false; } -void LLSocialAccountPanel::showConnectButton() +void LLFacebookAccountPanel::showConnectButton() { if(!mConnectButton->getVisible()) { @@ -755,7 +780,7 @@ void LLSocialAccountPanel::showConnectButton() } } -void LLSocialAccountPanel::hideConnectButton() +void LLFacebookAccountPanel::hideConnectButton() { if(mConnectButton->getVisible()) { @@ -764,14 +789,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 +804,7 @@ void LLSocialAccountPanel::showConnectedLayout() hideConnectButton(); } -void LLSocialAccountPanel::onConnect() +void LLFacebookAccountPanel::onConnect() { LLFacebookConnect::instance().checkConnectionToFacebook(true); @@ -787,7 +812,7 @@ void LLSocialAccountPanel::onConnect() LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com"); } -void LLSocialAccountPanel::onDisconnect() +void LLFacebookAccountPanel::onDisconnect() { LLFacebookConnect::instance().disconnectFromFacebook(); @@ -795,27 +820,27 @@ void LLSocialAccountPanel::onDisconnect() } //////////////////////// -//LLFloaterSocial/////// +//LLFloaterFacebook/////// //////////////////////// -LLFloaterSocial::LLFloaterSocial(const LLSD& key) : LLFloater(key), - mSocialPhotoPanel(NULL), +LLFloaterFacebook::LLFloaterFacebook(const LLSD& key) : LLFloater(key), + mFacebookPhotoPanel(NULL), mStatusErrorText(NULL), mStatusLoadingText(NULL), mStatusLoadingIndicator(NULL) { - mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterSocial::onCancel, this)); + mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterFacebook::onCancel, this)); } -void LLFloaterSocial::onCancel() +void LLFloaterFacebook::onCancel() { 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,29 +848,41 @@ BOOL LLFloaterSocial::postBuild() return LLFloater::postBuild(); } +void LLFloaterFacebook::showPhotoPanel() +{ + LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mFacebookPhotoPanel->getParent()); + if (!parent) + { + llwarns << "Cannot find panel container" << llendl; + return; + } + + parent->selectTabPanel(mFacebookPhotoPanel); +} + // static -void LLFloaterSocial::preUpdate() +void LLFloaterFacebook::preUpdate() { - LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social"); + LLFloaterFacebook* instance = LLFloaterReg::findTypedInstance<LLFloaterFacebook>("facebook"); if (instance) { //Will set file size text to 'unknown' - instance->mSocialPhotoPanel->updateControls(); + instance->mFacebookPhotoPanel->updateControls(); } } // static -void LLFloaterSocial::postUpdate() +void LLFloaterFacebook::postUpdate() { - LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social"); + LLFloaterFacebook* instance = LLFloaterReg::findTypedInstance<LLFloaterFacebook>("facebook"); if (instance) { //Will set the file size text - instance->mSocialPhotoPanel->updateControls(); + instance->mFacebookPhotoPanel->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(); + LLUICtrl * refresh_button = instance->mFacebookPhotoPanel->getRefreshBtn(); if (!refresh_button->getVisible()) { @@ -855,7 +892,7 @@ void LLFloaterSocial::postUpdate() } } -void LLFloaterSocial::draw() +void LLFloaterFacebook::draw() { if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator) { diff --git a/indra/newview/llfloatersocial.h b/indra/newview/llfloaterfacebook.h index bbe07c9704..0776f24034 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" @@ -35,10 +35,10 @@ class LLIconCtrl; class LLCheckBoxCtrl; class LLSnapshotLivePreview; -class LLSocialStatusPanel : public LLPanel +class LLFacebookStatusPanel : public LLPanel { public: - LLSocialStatusPanel(); + LLFacebookStatusPanel(); BOOL postBuild(); void draw(); void onSend(); @@ -53,11 +53,11 @@ private: LLUICtrl* mCancelButton; }; -class LLSocialPhotoPanel : public LLPanel +class LLFacebookPhotoPanel : public LLPanel { public: - LLSocialPhotoPanel(); - ~LLSocialPhotoPanel(); + LLFacebookPhotoPanel(); + ~LLFacebookPhotoPanel(); BOOL postBuild(); void draw(); @@ -88,12 +88,14 @@ private: LLUICtrl * mLocationCheckbox; LLUICtrl * mPostButton; LLUICtrl* mCancelButton; + + S32 mQuality; // Compression quality }; -class LLSocialCheckinPanel : public LLPanel +class LLFacebookCheckinPanel : public LLPanel { public: - LLSocialCheckinPanel(); + LLFacebookCheckinPanel(); BOOL postBuild(); void draw(); void onSend(); @@ -115,10 +117,10 @@ private: bool mReloadingMapTexture; }; -class LLSocialAccountPanel : public LLPanel +class LLFacebookAccountPanel : public LLPanel { public: - LLSocialAccountPanel(); + LLFacebookAccountPanel(); BOOL postBuild(); void draw(); @@ -143,23 +145,25 @@ private: }; -class LLFloaterSocial : public LLFloater +class LLFloaterFacebook : public LLFloater { public: - LLFloaterSocial(const LLSD& key); + LLFloaterFacebook(const LLSD& key); BOOL postBuild(); void draw(); void onCancel(); + + void showPhotoPanel(); static void preUpdate(); static void postUpdate(); 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..cff57bfa13 --- /dev/null +++ b/indra/newview/llfloaterflickr.cpp @@ -0,0 +1,707 @@ +/** +* @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 "llviewerregion.h" +#include "llviewercontrol.h" +#include "llviewermedia.h" +#include "lltabcontainer.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_PHOTO_LINK_TEXT = "Visit this location now"; +const std::string DEFAULT_TAG_TEXT = "secondlife "; + +/////////////////////////// +//LLFlickrPhotoPanel/////// +/////////////////////////// + +LLFlickrPhotoPanel::LLFlickrPhotoPanel() : +mSnapshotPanel(NULL), +mResolutionComboBox(NULL), +mRefreshBtn(NULL), +mWorkingLabel(NULL), +mThumbnailPlaceholder(NULL), +mTitleTextBox(NULL), +mDescriptionTextBox(NULL), +mLocationCheckbox(NULL), +mTagsTextBox(NULL), +mRatingComboBox(NULL), +mPostButton(NULL) +{ + mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLFlickrPhotoPanel::onSend, this)); + mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLFlickrPhotoPanel::onClickNewSnapshot, 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"); + 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"); + + // 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 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); + mRefreshBtn->setEnabled(no_ongoing_connection); + mLocationCheckbox->setEnabled(no_ongoing_connection); + + // Display the preview if one is available + if (previewp && previewp->getThumbnailImage()) + { + const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect(); + const S32 thumbnail_w = previewp->getThumbnailWidth(); + const S32 thumbnail_h = previewp->getThumbnailHeight(); + + // calc preview offset within the preview rect + const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2 ; + const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2 ; + + // calc preview offset within the floater rect + // Hack : To get the full offset, we need to take into account each and every offset of each widgets up to the floater. + // This is almost as arbitrary as using a fixed offset so that's what we do here for the sake of simplicity. + // *TODO : Get the offset looking through the hierarchy of widgets, should be done in postBuild() so to avoid traversing the hierarchy each time. + S32 offset_x = thumbnail_rect.mLeft + local_offset_x - 1; + S32 offset_y = thumbnail_rect.mBottom + local_offset_y - 39; + + mSnapshotPanel->localPointToOtherView(offset_x, offset_y, &offset_x, &offset_y, getParentByType<LLFloater>()); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + // Apply floater transparency to the texture unless the floater is focused. + F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + LLColor4 color = LLColor4::white; + gl_draw_scaled_image(offset_x, offset_y, + thumbnail_w, thumbnail_h, + previewp->getThumbnailImage(), color % alpha); + + previewp->drawPreviewRect(offset_x, offset_y) ; + } + + // Update the visibility of the working (computing preview) label + mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate())); + + // Enable Post if we have a preview to send and no on going connection being processed + mPostButton->setEnabled(no_ongoing_connection && (previewp && previewp->getSnapshotUpToDate()) && (mRatingComboBox && mRatingComboBox->getValue().isDefined())); + + // 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->setSnapshotType(previewp->SNAPSHOT_WEB); + previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG); + //previewp->setSnapshotQuality(98); + previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect()); + + updateControls(); + } + } +} + +void LLFlickrPhotoPanel::onClickNewSnapshot() +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + //setStatus(Impl::STATUS_READY); + lldebugs << "updating snapshot" << llendl; + previewp->updateSnapshot(TRUE); + } +} + +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; + + slurl_string = "<a href=\"" + slurl_string + "\">" + DEFAULT_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; + } + + // 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(""); + mTagsTextBox->setValue(DEFAULT_TAG_TEXT); + + LLFloater* floater = getParentByType<LLFloater>(); + if (floater) + { + floater->closeFloater(); + } +} + +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]; + + const std::string& filter_name = filterbox->getSimple(); + llinfos << "Merov : filter name is : " << filter_name << llendl; + + 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); + // Merov : + // Get the old filter, compare to the current one "filter_name" and set if changed + // If changed, also force the updateSnapshot() here under + std::string original_filter = previewp->getFilter(); + + if ((original_width != width) || (original_height != height) || (original_filter != filter_name)) + { + previewp->setSize(width, height); + previewp->setFilter(filter_name); + + // hide old preview as the aspect ratio could be wrong + lldebugs << "updating thumbnail" << llendl; + + previewp->updateSnapshot(FALSE, TRUE); + if(do_update) + { + lldebugs << "Will update controls" << llendl; + updateControls(); + LLFlickrPhotoPanel::onClickNewSnapshot(); + } + } + + } +} + +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::onCancel() +{ + 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); +} + +// static +void LLFloaterFlickr::preUpdate() +{ + LLFloaterFlickr* instance = LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr"); + if (instance) + { + //Will set file size text to 'unknown' + instance->mFlickrPhotoPanel->updateControls(); + } +} + +// static +void LLFloaterFlickr::postUpdate() +{ + LLFloaterFlickr* instance = LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr"); + if (instance) + { + //Will set the file size text + instance->mFlickrPhotoPanel->updateControls(); + + // The refresh button is initially hidden. We show it after the first update, + // i.e. after snapshot is taken + LLUICtrl * refresh_button = instance->mFlickrPhotoPanel->getRefreshBtn(); + + if (!refresh_button->getVisible()) + { + refresh_button->setVisible(true); + } + + } +} + +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..1d9e649899 --- /dev/null +++ b/indra/newview/llfloaterflickr.h @@ -0,0 +1,128 @@ +/** +* @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 LLFlickrPhotoPanel : public LLPanel +{ +public: + LLFlickrPhotoPanel(); + ~LLFlickrPhotoPanel(); + + BOOL postBuild(); + void draw(); + + LLSnapshotLivePreview* getPreviewView(); + void onVisibilityChange(const LLSD& new_visibility); + void onClickNewSnapshot(); + 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: + 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; +}; + +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 onCancel(); + + void showPhotoPanel(); + + static void preUpdate(); + static void postUpdate(); + +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..c3efc26991 100755 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -31,7 +31,9 @@ #include "llagent.h" #include "llfacebookconnect.h" #include "llfloaterreg.h" -#include "llfloatersocial.h" +#include "llfloaterfacebook.h" +#include "llfloaterflickr.h" +#include "llfloatertwitter.h" #include "llcheckboxctrl.h" #include "llcombobox.h" #include "llpostcard.h" @@ -1264,9 +1266,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; diff --git a/indra/newview/llfloatertwitter.cpp b/indra/newview/llfloatertwitter.cpp new file mode 100644 index 0000000000..ea263566a6 --- /dev/null +++ b/indra/newview/llfloatertwitter.cpp @@ -0,0 +1,762 @@ +/** +* @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 "llfloaterreg.h" +#include "lliconctrl.h" +#include "llresmgr.h" // LLLocale +#include "llsdserialize.h" +#include "llloadingindicator.h" +#include "llplugincookiestore.h" +#include "llslurl.h" +#include "lltrans.h" +#include "llsnapshotlivepreview.h" +#include "llviewerregion.h" +#include "llviewercontrol.h" +#include "llviewermedia.h" +#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"; + +/////////////////////////// +//LLTwitterPhotoPanel/////// +/////////////////////////// + +LLTwitterPhotoPanel::LLTwitterPhotoPanel() : +mSnapshotPanel(NULL), +mResolutionComboBox(NULL), +mRefreshBtn(NULL), +mWorkingLabel(NULL), +mThumbnailPlaceholder(NULL), +mStatusCounterLabel(NULL), +mStatusTextBox(NULL), +mLocationCheckbox(NULL), +mPhotoCheckbox(NULL), +mPostButton(NULL) +{ + mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLTwitterPhotoPanel::onSend, this)); + mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLTwitterPhotoPanel::onClickNewSnapshot, 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->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::updateResolution, this, TRUE)); + mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn"); + mWorkingLabel = getChild<LLUICtrl>("working_lbl"); + mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder"); + mStatusCounterLabel = getChild<LLUICtrl>("status_counter_label"); + mStatusTextBox = getChild<LLUICtrl>("photo_status"); + 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"); + + return LLPanel::postBuild(); +} + +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()); + mCancelButton->setEnabled(no_ongoing_connection); + mStatusTextBox->setEnabled(no_ongoing_connection); + mResolutionComboBox->setEnabled(no_ongoing_connection && mPhotoCheckbox->getValue().asBoolean()); + mRefreshBtn->setEnabled(no_ongoing_connection && mPhotoCheckbox->getValue().asBoolean()); + mLocationCheckbox->setEnabled(no_ongoing_connection); + mPhotoCheckbox->setEnabled(no_ongoing_connection); + + bool add_location = mLocationCheckbox->getValue().asBoolean(); + bool add_photo = mPhotoCheckbox->getValue().asBoolean(); + updateStatusTextLength(false); + + // 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); + + previewp->drawPreviewRect(offset_x, offset_y) ; + } + + // Update the visibility of the working (computing preview) label + mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate())); + + // Enable Post if we have a preview to send and no on going connection being processed + mPostButton->setEnabled(no_ongoing_connection && ((add_photo && previewp && previewp->getSnapshotUpToDate()) || 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->setSnapshotType(previewp->SNAPSHOT_WEB); + previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG); + //previewp->setSnapshotQuality(98); + 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) + { + //setStatus(Impl::STATUS_READY); + lldebugs << "updating snapshot" << llendl; + previewp->updateSnapshot(TRUE); + } +} + +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(""); + + LLFloater* floater = getParentByType<LLFloater>(); + if (floater) + { + floater->closeFloater(); + } +} + +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_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 LLTwitterPhotoPanel::updateResolution(BOOL do_update) +{ + LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox); + + std::string sdstring = combobox->getSelectedValue(); + LLSD sdres; + std::stringstream sstream(sdstring); + LLSDSerialize::fromNotation(sdres, sstream, sdstring.size()); + + S32 width = sdres[0]; + S32 height = sdres[1]; + + LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get()); + if (previewp && combobox->getCurrentIndex() >= 0) + { + S32 original_width = 0 , original_height = 0 ; + previewp->getSize(original_width, original_height) ; + + if (width == 0 || height == 0) + { + // take resolution from current window size + lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl; + previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw()); + } + else + { + // use the resolution from the selected pre-canned drop-down choice + lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl; + previewp->setSize(width, height); + } + + checkAspectRatio(width); + + previewp->getSize(width, height); + + if(original_width != width || original_height != height) + { + previewp->setSize(width, height); + + // hide old preview as the aspect ratio could be wrong + lldebugs << "updating thumbnail" << llendl; + + previewp->updateSnapshot(FALSE, TRUE); + if(do_update) + { + lldebugs << "Will update controls" << llendl; + updateControls(); + LLTwitterPhotoPanel::onClickNewSnapshot(); + } + } + + } +} + +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::onCancel() +{ + 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); +} + +// static +void LLFloaterTwitter::preUpdate() +{ + LLFloaterTwitter* instance = LLFloaterReg::findTypedInstance<LLFloaterTwitter>("twitter"); + if (instance) + { + //Will set file size text to 'unknown' + instance->mTwitterPhotoPanel->updateControls(); + } +} + +// static +void LLFloaterTwitter::postUpdate() +{ + LLFloaterTwitter* instance = LLFloaterReg::findTypedInstance<LLFloaterTwitter>("twitter"); + if (instance) + { + //Will set the file size text + instance->mTwitterPhotoPanel->updateControls(); + + // The refresh button is initially hidden. We show it after the first update, + // i.e. after snapshot is taken + LLUICtrl * refresh_button = instance->mTwitterPhotoPanel->getRefreshBtn(); + + if (!refresh_button->getVisible()) + { + refresh_button->setVisible(true); + } + + } +} + +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..f267f76694 --- /dev/null +++ b/indra/newview/llfloatertwitter.h @@ -0,0 +1,131 @@ +/** +* @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 LLTwitterPhotoPanel : public LLPanel +{ +public: + LLTwitterPhotoPanel(); + ~LLTwitterPhotoPanel(); + + BOOL postBuild(); + void draw(); + + LLSnapshotLivePreview* getPreviewView(); + void onVisibilityChange(const LLSD& new_visibility); + void onAddLocationToggled(); + void onAddPhotoToggled(); + void onClickNewSnapshot(); + void onSend(); + 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: + LLHandle<LLView> mPreviewHandle; + + LLUICtrl * mSnapshotPanel; + LLUICtrl * mResolutionComboBox; + LLUICtrl * mRefreshBtn; + LLUICtrl * mWorkingLabel; + LLUICtrl * mThumbnailPlaceholder; + LLUICtrl * mStatusCounterLabel; + LLUICtrl * mStatusTextBox; + LLUICtrl * mLocationCheckbox; + LLUICtrl * mPhotoCheckbox; + LLUICtrl * mPostButton; + LLUICtrl* mCancelButton; + + 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 onCancel(); + + void showPhotoPanel(); + + static void preUpdate(); + static void postUpdate(); + +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 76b73fcf29..4151eec3e3 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(); } @@ -436,9 +451,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..14177b4f05 --- /dev/null +++ b/indra/newview/llimagefiltersmanager.cpp @@ -0,0 +1,97 @@ +/** + * @file llimagefiltersmanager.cpp + * @brief Load and execute image filters. Mostly used for Flickr 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" + + +//--------------------------------------------------------------------------- +// LLImageFilters +//--------------------------------------------------------------------------- + +LLImageFiltersManager::LLImageFiltersManager() +{ +} + +LLImageFiltersManager::~LLImageFiltersManager() +{ +} + +// virtual static +void LLImageFiltersManager::initSingleton() +{ + loadAllFilters(); +} + +// static +std::string LLImageFiltersManager::getSysDir() +{ + return gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "filters", ""); +} + +void LLImageFiltersManager::loadAllFilters() +{ + // Load system (coming out of the box) filters + loadFiltersFromDir(getSysDir()); +} + +void LLImageFiltersManager::loadFiltersFromDir(const std::string& dir) +{ + mFiltersList.clear(); + + LLDirIterator dir_iter(dir, "*.xml"); + while (1) + { + std::string file; + if (!dir_iter.next(file)) + { + break; // no more files + } + + // Get the ".xml" out of the file name to get the filter name + std::string filter_name = file.substr(0,file.length()-4); + mFiltersList.push_back(filter_name); + + std::string path = gDirUtilp->add(dir, file); + + // For the moment, just output the file found to the log + llinfos << "Merov : loadFiltersFromDir, filter = " << file << ",path = " << path << llendl; + } +} + +std::string LLImageFiltersManager::getFilterPath(const std::string& filter_name) +{ + // *TODO : we should store (filter name, path) in a std::map + std::string file = filter_name + ".xml"; + std::string dir = getSysDir(); + std::string 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..5591e14a41 --- /dev/null +++ b/indra/newview/llimagefiltersmanager.h @@ -0,0 +1,76 @@ +/** + * @file llimagefiltersmanager.h + * @brief Load and execute image filters. Mostly used for Flickr 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" +#include "llimage.h" +/* +typedef enum e_vignette_mode +{ + VIGNETTE_MODE_NONE = 0, + VIGNETTE_MODE_BLEND = 1, + VIGNETTE_MODE_FADE = 2 +} EVignetteMode; + +typedef enum e_screen_mode +{ + SCREEN_MODE_2DSINE = 0, + SCREEN_MODE_LINE = 1 +} EScreenMode; +*/ +//============================================================================ +// library initialization class + +class LLImageFiltersManager : public LLSingleton<LLImageFiltersManager> +{ + LOG_CLASS(LLImageFiltersManager); +public: + // getFilters(); get a vector of std::string containing the filter names + //LLSD loadFilter(const std::string& filter_name); + //void executeFilter(const LLSD& filter_data, LLPointer<LLImageRaw> raw_image); + const std::vector<std::string> &getFiltersList() const { return mFiltersList; } + std::string getFilterPath(const std::string& filter_name); + +protected: +private: + void loadAllFilters(); + void loadFiltersFromDir(const std::string& dir); +// LLSD loadFilter(const std::string& path); + + static std::string getSysDir(); + + friend class LLSingleton<LLImageFiltersManager>; + /*virtual*/ void initSingleton(); + LLImageFiltersManager(); + ~LLImageFiltersManager(); + + // List of filters + std::vector<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/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..c38798bbea 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" @@ -87,7 +91,8 @@ 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("") { setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")); mSnapshotDelayTimer.setTimerExpirySec(0.0f); @@ -208,7 +213,9 @@ void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail mSnapshotDelayTimer.start(); mSnapshotDelayTimer.setTimerExpirySec(delay); LLFloaterSnapshot::preUpdate(); - LLFloaterSocial::preUpdate(); + LLFloaterFacebook::preUpdate(); + LLFloaterFlickr::preUpdate(); + LLFloaterTwitter::preUpdate(); } // Update thumbnail if requested. @@ -218,15 +225,21 @@ void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail } } -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); + if (set_by_user) + { + gSavedSettings.setS32("SnapshotQuality", quality); + } mSnapshotUpToDate = FALSE; + return true; } + return false; } void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y) @@ -575,7 +588,16 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) if(raw) { raw->expandToPowerOfTwo(); - mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE); + // Filter the thumbnail + if (getFilter() != "") + { + LLImageFilter filter; + std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(getFilter()); + filter.loadFromFile(filter_path); + filter.executeFilter(raw); + //raw->filterGrayScale(); + } + mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE); mThumbnailUpToDate = TRUE ; } @@ -679,6 +701,16 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) } else { + // Apply the filter to mPreviewImage + if (previewp->getFilter() != "") + { + LLImageFilter filter; + std::string filter_path = LLImageFiltersManager::getInstance()->getFilterPath(previewp->getFilter()); + filter.loadFromFile(filter_path); + filter.executeFilter(previewp->mPreviewImage); + //previewp->mPreviewImage->filterGrayScale(); + } + // delete any existing image previewp->mFormattedImage = NULL; // now create the new one of the appropriate format. @@ -765,7 +797,9 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) } lldebugs << "done creating snapshot" << llendl; LLFloaterSnapshot::postUpdate(); - LLFloaterSocial::postUpdate(); + LLFloaterFacebook::postUpdate(); + LLFloaterFlickr::postUpdate(); + LLFloaterTwitter::postUpdate(); return TRUE; } diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h index fe3d257b02..6addc87de2 100644 --- a/indra/newview/llsnapshotlivepreview.h +++ b/indra/newview/llsnapshotlivepreview.h @@ -93,8 +93,10 @@ public: void setSnapshotType(ESnapshotType type) { mSnapshotType = type; } void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; } - void setSnapshotQuality(S32 quality); + bool setSnapshotQuality(S32 quality, bool set_by_user = true); void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; } + void setFilter(std::string filter_name) { mFilterName = filter_name; } + std::string getFilter() { return mFilterName; } void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f); void saveWeb(); void saveTexture(); @@ -154,6 +156,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..7942b21319 --- /dev/null +++ b/indra/newview/lltwitterconnect.cpp @@ -0,0 +1,473 @@ +/** + * @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=\"snapshot." << imageFormat << "\"\r\n" + << "Content-Type: image/" << imageFormat << "\r\n\r\n"; + + // Insert the image data. + // *FIX: Treating this as a string will probably screw it up ... + U8* image_data = image->getData(); + for (S32 i = 0; i < image->getDataSize(); ++i) + { + body << image_data[i]; + } + + body << "\r\n--" << boundary << "--\r\n"; + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = body.str().size(); + U8 *data = new U8[size]; + memcpy(data, body.str().data(), size); + + // Note: we can use that route for different publish action. We should be able to use the same responder. + LLHTTPClient::postRaw(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) + { + LLSD state_info; + state_info["enum"] = connection_state; + sStateWatcher->post(state_info); + } + + mConnectionState = connection_state; +} + +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 4ce049df03..e38ffbbc63 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -53,13 +53,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" @@ -103,7 +105,6 @@ #include "llfloatersettingsdebug.h" #include "llfloatersidepanelcontainer.h" #include "llfloatersnapshot.h" -#include "llfloatersocial.h" #include "llfloatersounddevices.h" #include "llfloaterspellchecksettings.h" #include "llfloatertelehub.h" @@ -115,6 +116,7 @@ #include "llfloatertopobjects.h" #include "llfloatertoybox.h" #include "llfloatertranslationsettings.h" +#include "llfloatertwitter.h" #include "llfloateruipreview.h" #include "llfloatervoiceeffect.h" #include "llfloatervoicevolume.h" @@ -304,7 +306,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>); @@ -312,8 +313,15 @@ 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>); 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 6018c2d250..f56a2dd04f 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 bb891996c9..47d105728d 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="Command_Highlighting_Icon" file_name="toolbar_icons/highlighting.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" /> diff --git a/indra/newview/skins/default/textures/toolbar_icons/flickr.png b/indra/newview/skins/default/textures/toolbar_icons/flickr.png Binary files differnew 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.png Binary files differnew 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_social.xml b/indra/newview/skins/default/xui/en/floater_facebook.xml index b7ff374d5f..c1ff8571e9 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,31 +28,32 @@ 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_account.xml" + class="llfacebookaccountpanel" follows="all" label="ACCOUNT" - name="panel_social_account"/> + name="panel_facebook_account"/> </tab_container> <panel name="connection_status_panel" 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..019ddad33c 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"> 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 e5e2bd4c11..74bfd2cf01 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 35f2e7b31e..f6db3aa953 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_social_photo.xml b/indra/newview/skins/default/xui/en/panel_facebook_photo.xml index a55613b52a..2d46665bae 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" @@ -39,6 +39,10 @@ label="1024x768" name="1024x768" value="[i1024,i768]" /> + <combo_box.item + label="1200x630" + name="1200x630" + value="[i1200,i630]" /> </combo_box> <text follows="left|top" 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..47ddecdf38 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_flickr_photo.xml @@ -0,0 +1,232 @@ + <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="SocialPhotoResolution" + follows="left|top" + top="6" + left="9" + name="resolution_combobox" + tool_tip="Image resolution" + height="21" + width="135"> + <combo_box.item + label="Current Window" + name="CurrentWindow" + value="[i0,i0]" /> + <combo_box.item + label="640x480" + name="640x480" + value="[i640,i480]" /> + <combo_box.item + label="800x600" + name="800x600" + value="[i800,i600]" /> + <combo_box.item + label="1024x768" + name="1024x768" + value="[i1024,i768]" /> + </combo_box> + <combo_box + control_name="SocialPhotoFilters" + 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="-20" + length="1" + halign="center" + name="working_lbl" + translate="false" + type="string" + visible="true" + width="150"> + Refreshing... + </text> + <text + length="1" + follows="top|left|right" + font="SansSerif" + height="16" + left="9" + name="title_label" + top_pad="20" + 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" + label="Choose Flickr rating (required)..." + name="rating_combobox" + tool_tip="Flickr content rating" + height="21" + width="250"> + <combo_box.item + label="Safe content rating" + name="SafeRating" + value="1" /> + <combo_box.item + label="Moderate content rating" + name="ModerateRating" + value="2" /> + <combo_box.item + label="Restricted content 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..14268c1bcf --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_twitter_photo.xml @@ -0,0 +1,178 @@ + <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="SocialPhotoResolution" + follows="left|top" + top="6" + left="9" + name="resolution_combobox" + tool_tip="Image resolution" + height="21" + width="135"> + <combo_box.item + label="Current Window" + name="CurrentWindow" + value="[i0,i0]" /> + <combo_box.item + label="640x480" + name="640x480" + value="[i640,i480]" /> + <combo_box.item + label="800x600" + name="800x600" + value="[i800,i600]" /> + <combo_box.item + label="1024x768" + name="1024x768" + value="[i1024,i768]" /> + </combo_box> + <text + follows="left|top" + font="SansSerifSmall" + height="14" + left="208" + length="1" + halign="right" + name="file_size_label" + top="9" + type="string" + width="50"> + [SIZE] KB + </text> + <panel + height="150" + width="250" + visible="true" + name="thumbnail_placeholder" + top="33" + follows="left|top" + left="9"> + </panel> + <button + follows="left|top" + height="23" + label="Refresh" + left="9" + top_pad="5" + name="new_snapshot_btn" + tool_tip="Click to refresh" + visible="true" + width="100" > + <button.commit_callback + function="SocialSharing.RefreshPhoto" /> + </button> + <text + follows="left|top" + font="SansSerif" + text_color="EmphasisColor" + height="14" + top_pad="-19" + left_pad="-20" + length="1" + halign="center" + name="working_lbl" + translate="false" + type="string" + visible="true" + width="150"> + Refreshing... + </text> + </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..d226a72d7b 100755 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -148,13 +148,25 @@ 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 --> <string name="TooltipPerson">Person</string><!-- Object under mouse pointer is an avatar --> @@ -3444,6 +3456,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 +3879,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 +3896,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 +3909,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 +3926,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 9a617c2a13..6f811ecb6d 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=""): |