diff options
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=""):  | 
