diff options
72 files changed, 7088 insertions, 1228 deletions
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index bdbfc55fa2..410d25ad97 100755 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -63,10 +63,11 @@ add_subdirectory(${VIEWER_PREFIX}test) # viewer media plugins add_subdirectory(${LIBS_OPEN_PREFIX}media_plugins) -# llplugin testbed code (is this the right way to include it?) -if (LL_TESTS AND NOT LINUX) - add_subdirectory(${VIEWER_PREFIX}test_apps/llplugintest) -endif (LL_TESTS AND NOT LINUX) + # llplugin testbed code (is this the right way to include it?) + if (LL_TESTS AND NOT LINUX) + add_subdirectory(${VIEWER_PREFIX}test_apps/llplugintest) + add_subdirectory(${VIEWER_PREFIX}test_apps/llfbconnecttest) + endif (LL_TESTS AND NOT LINUX) if (LINUX) add_subdirectory(${VIEWER_PREFIX}linux_crash_logger) diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 6110b035dc..11648717ad 100755 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -217,7 +217,8 @@ static void request( Injector* body_injector, LLCurl::ResponderPtr responder, const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, - const LLSD& headers = LLSD() + const LLSD& headers = LLSD(), + bool follow_redirects = true ) { if (!LLHTTPClient::hasPump()) @@ -231,7 +232,7 @@ static void request( } LLPumpIO::chain_t chain; - LLURLRequest* req = new LLURLRequest(method, url); + LLURLRequest* req = new LLURLRequest(method, url, follow_redirects); if(!req->isValid())//failed { if (responder) @@ -334,7 +335,8 @@ void LLHTTPClient::getByteRange( S32 bytes, ResponderPtr responder, const LLSD& hdrs, - const F32 timeout) + const F32 timeout, + bool follow_redirects /* = true */) { LLSD headers = hdrs; if(offset > 0 || bytes > 0) @@ -342,37 +344,42 @@ void LLHTTPClient::getByteRange( std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1); headers["Range"] = range; } - request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers); + request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers, follow_redirects); } void LLHTTPClient::head( const std::string& url, ResponderPtr responder, const LLSD& headers, - const F32 timeout) + const F32 timeout, + bool follow_redirects /* = true */) { - request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers); + request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers, follow_redirects); } -void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout) +void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout, + bool follow_redirects /* = true */) { - request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers); + request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers, follow_redirects); } -void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout) +void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const LLSD& headers, + const F32 timeout, bool follow_redirects /* = true */) { - request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers); + request(url, LLURLRequest::HTTP_HEAD, NULL, responder, timeout, headers, follow_redirects); } -void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const F32 timeout) +void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const F32 timeout, + bool follow_redirects /* = true */) { - getHeaderOnly(url, responder, LLSD(), timeout); + getHeaderOnly(url, responder, LLSD(), timeout, follow_redirects); } -void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers, const F32 timeout) +void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers, + const F32 timeout, bool follow_redirects /* = true */) { LLURI uri; uri = LLURI::buildHTTP(url, LLSD::emptyArray(), query); - get(uri.asString(), responder, headers, timeout); + get(uri.asString(), responder, headers, timeout, follow_redirects); } // A simple class for managing data returned from a curl http request. diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index a7236ba169..5de257a4f6 100755 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -63,10 +63,15 @@ public: const std::string& url, ResponderPtr, const LLSD& headers = LLSD(), - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void getByteRange(const std::string& url, S32 offset, S32 bytes, ResponderPtr, const LLSD& headers=LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void get(const std::string& url, ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); + const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, + bool follow_redirects = true); + static void getByteRange(const std::string& url, S32 offset, S32 bytes, ResponderPtr, + const LLSD& headers=LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, + bool follow_redirects = true); + static void get(const std::string& url, ResponderPtr, const LLSD& headers = LLSD(), + const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); + static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers = LLSD(), + const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); static void put( const std::string& url, @@ -74,8 +79,10 @@ public: ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void getHeaderOnly(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void getHeaderOnly(const std::string& url, ResponderPtr, const LLSD& headers, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); + static void getHeaderOnly(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, + bool follow_redirects = true); + static void getHeaderOnly(const std::string& url, ResponderPtr, const LLSD& headers, + const F32 timeout=HTTP_REQUEST_EXPIRY_SECS, bool follow_redirects = true); static void post( const std::string& url, diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index de9e2fe294..683065357d 100755 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -150,16 +150,19 @@ std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action) return VERBS[action]; } -LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) : - mAction(action) +LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, bool follow_redirects /* = true */) : + mAction(action), + mFollowRedirects(follow_redirects) { initialize(); } LLURLRequest::LLURLRequest( LLURLRequest::ERequestAction action, - const std::string& url) : - mAction(action) + const std::string& url, + bool follow_redirects /* = true */) : + mAction(action), + mFollowRedirects(follow_redirects) { initialize(); setURL(url); @@ -479,12 +482,18 @@ bool LLURLRequest::configure() case HTTP_HEAD: mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1); mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1); - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); + if (mFollowRedirects) + { + mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); + } rv = true; break; case HTTP_GET: mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1); - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); + if (mFollowRedirects) + { + mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); + } // Set Accept-Encoding to allow response compression mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 44d358d906..20d6e30d17 100755 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -95,7 +95,7 @@ public: * * @param action One of the ERequestAction enumerations. */ - LLURLRequest(ERequestAction action); + LLURLRequest(ERequestAction action, bool follow_redirects = true); /** * @brief Constructor. @@ -103,7 +103,7 @@ public: * @param action One of the ERequestAction enumerations. * @param url The url of the request. It should already be encoded. */ - LLURLRequest(ERequestAction action, const std::string& url); + LLURLRequest(ERequestAction action, const std::string& url, bool follow_redirects = true); /** * @brief Destructor. @@ -219,10 +219,11 @@ protected: }; EState mState; ERequestAction mAction; + bool mFollowRedirects; LLURLRequestDetail* mDetail; LLIOPipe::ptr_t mCompletionCallback; - S32 mRequestTransferedBytes; - S32 mResponseTransferedBytes; + S32 mRequestTransferedBytes; + S32 mResponseTransferedBytes; static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param); diff --git a/indra/llplugin/llplugincookiestore.cpp b/indra/llplugin/llplugincookiestore.cpp index 82017ab3fa..9f4d65e723 100755 --- a/indra/llplugin/llplugincookiestore.cpp +++ b/indra/llplugin/llplugincookiestore.cpp @@ -87,6 +87,16 @@ std::string LLPluginCookieStore::Cookie::getKey() const return result; } +std::string LLPluginCookieStore::Cookie::getDomain() const +{ + std::string result; + if(mDomainEnd > mDomainStart) + { + result += mCookie.substr(mDomainStart, mDomainEnd - mDomainStart); + } + return result; +} + bool LLPluginCookieStore::Cookie::parse(const std::string &host) { bool first_field = true; @@ -662,3 +672,21 @@ void LLPluginCookieStore::removeCookie(const std::string &key) } } +void LLPluginCookieStore::removeCookiesByDomain(const std::string &domain) +{ + cookie_map_t::iterator iter = mCookies.begin(); + while(iter != mCookies.end()) + { + if(iter->second->getDomain() == domain) + { + cookie_map_t::iterator doErase = iter; + iter++; + delete doErase->second; + mCookies.erase(doErase); + } + else + { + iter++; + } + } +} diff --git a/indra/llplugin/llplugincookiestore.h b/indra/llplugin/llplugincookiestore.h index 91289d38a5..a2fdeab647 100755 --- a/indra/llplugin/llplugincookiestore.h +++ b/indra/llplugin/llplugincookiestore.h @@ -67,6 +67,8 @@ public: // quote or unquote a string as per the definition of 'quoted-string' in rfc2616 static std::string quoteString(const std::string &s); static std::string unquoteString(const std::string &s); + + void removeCookiesByDomain(const std::string &domain); private: @@ -79,6 +81,7 @@ private: // Construct a string from the cookie that uniquely represents it, to be used as a key in a std::map. std::string getKey() const; + std::string getDomain() const; const std::string &getCookie() const { return mCookie; }; bool isSessionCookie() const { return mDate.isNull(); }; diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h index e67f39b21b..3f13691a30 100755 --- a/indra/llui/llchatentry.h +++ b/indra/llui/llchatentry.h @@ -62,8 +62,8 @@ public: virtual void draw(); virtual void onCommit(); - /*virtual*/ void onFocusReceived(); - /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); + /*virtual*/ void onFocusLost(); void enableSingleLineMode(bool single_line_mode); boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 273ceb4038..6ea0080f6c 100755 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -509,8 +509,8 @@ LLFloater::~LLFloater() if( gFocusMgr.childHasKeyboardFocus(this)) { - // Just in case we might still have focus here, release it. - releaseFocus(); + // Just in case we might still have focus here, release it. + releaseFocus(); } // This is important so that floaters with persistent rects (i.e., those @@ -1335,7 +1335,7 @@ void LLFloater::setFocus( BOOL b ) { return; } - LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this); + LLView* last_focus = gFocusMgr.getLastFocusForGroup(this); // a descendent already has focus BOOL child_had_focus = hasFocus(); diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 724d190307..f03c8d444b 100755 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -469,7 +469,7 @@ void LLFocusMgr::setAppHasFocus(BOOL focus) mAppHasFocus = focus; } -LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const +LLView* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const { if (subtree_root) { @@ -477,7 +477,7 @@ LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const if (found_it != mImpl->mFocusHistory.end()) { // found last focus for this subtree - return static_cast<LLUICtrl*>(found_it->second.get()); + return found_it->second.get(); } } return NULL; diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index 25ae1d2579..1c7326260c 100755 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -97,7 +97,7 @@ public: void triggerFocusFlash(); BOOL getAppHasFocus() const { return mAppHasFocus; } void setAppHasFocus(BOOL focus); - LLUICtrl* getLastFocusForGroup(LLView* subtree_root) const; + LLView* getLastFocusForGroup(LLView* subtree_root) const; void clearLastFocusForGroup(LLView* subtree_root); // If setKeyboardFocus(NULL) is called, and there is a non-NULL default diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index cf449217f5..f32a52e6c6 100755 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -185,7 +185,7 @@ LLFolderView::LLFolderView(const Params& p) mAutoOpenCandidate = NULL; mAutoOpenTimer.stop(); mKeyboardSelection = FALSE; - mIndentation = p.folder_indentation; + mIndentation = getParentFolder() ? getParentFolder()->getIndentation() + mLocalIndentation : 0; //clear label // go ahead and render root folder as usual @@ -1613,7 +1613,7 @@ void LLFolderView::update() { getFolderViewModel()->getFilter().clearModified(); } - + // automatically show matching items, and select first one if we had a selection if (mNeedsAutoSelect) { @@ -1653,13 +1653,13 @@ void LLFolderView::update() BOOL is_visible = isInVisibleChain(); - // Puts folders/items in proper positions + //Puts folders/items in proper positions // arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849) // It also handles the open/close folder animation - if (is_visible) + if ( is_visible ) { sanitizeSelection(); - if (needsArrange()) + if( needsArrange() ) { S32 height = 0; S32 width = 0; diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 6c147ccc12..f061313645 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -307,7 +307,12 @@ std::set<LLFolderViewItem*> LLFolderViewItem::getSelectionList() const // addToFolder() returns TRUE if it succeeds. FALSE otherwise void LLFolderViewItem::addToFolder(LLFolderViewFolder* folder) { - folder->addItem(this); + folder->addItem(this); + + // Compute indentation since parent folder changed + mIndentation = (getParentFolder()) + ? getParentFolder()->getIndentation() + mLocalIndentation + : 0; } @@ -940,6 +945,11 @@ LLFolderViewFolder::~LLFolderViewFolder( void ) void LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder) { folder->addFolder(this); + + // Compute indentation since parent folder changed + mIndentation = (getParentFolder()) + ? getParentFolder()->getIndentation() + mLocalIndentation + : 0; } static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange"); @@ -1109,7 +1119,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) BOOL LLFolderViewFolder::needsArrange() { - return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); + return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); } // Passes selection information on to children and record selection diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d3835a5b2a..1fea6dea9f 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -191,6 +191,7 @@ set(viewer_SOURCE_FILES llexpandabletextbox.cpp llexternaleditor.cpp llface.cpp + llfacebookconnect.cpp llfasttimerview.cpp llfavoritesbar.cpp llfeaturemanager.cpp @@ -273,6 +274,7 @@ set(viewer_SOURCE_FILES llfloatersettingsdebug.cpp llfloatersidepanelcontainer.cpp llfloatersnapshot.cpp + llfloatersocial.cpp llfloatersounddevices.cpp llfloaterspellchecksettings.cpp llfloatertelehub.cpp @@ -508,6 +510,7 @@ set(viewer_SOURCE_FILES llsidetraypanelcontainer.cpp llsky.cpp llslurl.cpp + llsnapshotlivepreview.cpp llspatialpartition.cpp llspeakers.cpp llspeakingindicatormanager.cpp @@ -776,6 +779,7 @@ set(viewer_HEADER_FILES llexpandabletextbox.h llexternaleditor.h llface.h + llfacebookconnect.h llfasttimerview.h llfavoritesbar.h llfeaturemanager.h @@ -858,6 +862,7 @@ set(viewer_HEADER_FILES llfloatersettingsdebug.h llfloatersidepanelcontainer.h llfloatersnapshot.h + llfloatersocial.h llfloatersounddevices.h llfloaterspellchecksettings.h llfloatertelehub.h @@ -1082,6 +1087,7 @@ set(viewer_HEADER_FILES llsidetraypanelcontainer.h llsky.h llslurl.h + llsnapshotlivepreview.h llspatialpartition.h llspeakers.h llspeakingindicatormanager.h diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 4659673333..60c942094a 100755 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -216,6 +216,16 @@ is_running_function="Floater.IsOpen" is_running_parameters="snapshot" /> + <command name="social" + available_in_toybox="true" + icon="Command_Social_Icon" + label_ref="Command_Social_Label" + tooltip_ref="Command_Social_Tooltip" + execute_function="Floater.ToggleOrBringToFront" + execute_parameters="social" + is_running_function="Floater.IsOpen" + is_running_parameters="social" + /> <command name="speak" available_in_toybox="true" icon="Command_Speak_Icon" diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index eeed12499d..458ec591dc 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13084,6 +13084,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>SocialPhotoResolution</key> + <map> + <key>Comment</key> + <string>Default resolution when sharing photo using the social floater</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>[i800,i600]</string> + </map> <key>sourceid</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/toolbars.xml b/indra/newview/app_settings/toolbars.xml index 29c019719d..86f9912815 100755 --- a/indra/newview/app_settings/toolbars.xml +++ b/indra/newview/app_settings/toolbars.xml @@ -6,6 +6,7 @@ <command name="speak"/> <command name="destinations"/> <command name="people"/> + <command name="social"/> <command name="profile"/> <command name="move"/> <command name="view"/> diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index b9ec304b7e..3410a37890 100755 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -112,6 +112,11 @@ BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const case LOCATION_FORMAT_NORMAL: buffer = llformat("%s", region_name.c_str()); break; + case LOCATION_FORMAT_NORMAL_COORDS: + buffer = llformat("%s (%d, %d, %d)", + region_name.c_str(), + pos_x, pos_y, pos_z); + break; case LOCATION_FORMAT_NO_COORDS: buffer = llformat("%s%s%s", region_name.c_str(), @@ -143,6 +148,11 @@ BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const case LOCATION_FORMAT_NORMAL: buffer = llformat("%s, %s", parcel_name.c_str(), region_name.c_str()); break; + case LOCATION_FORMAT_NORMAL_COORDS: + buffer = llformat("%s (%d, %d, %d)", + parcel_name.c_str(), + pos_x, pos_y, pos_z); + break; case LOCATION_FORMAT_NO_MATURITY: buffer = llformat("%s, %s (%d, %d, %d)", parcel_name.c_str(), diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h index dda5dc1fd1..bb48dad14c 100755 --- a/indra/newview/llagentui.h +++ b/indra/newview/llagentui.h @@ -35,6 +35,7 @@ public: enum ELocationFormat { LOCATION_FORMAT_NORMAL, // Parcel + LOCATION_FORMAT_NORMAL_COORDS, // Parcel (x, y, z) LOCATION_FORMAT_LANDMARK, // Parcel, Region LOCATION_FORMAT_NO_MATURITY, // Parcel, Region (x, y, z) LOCATION_FORMAT_NO_COORDS, // Parcel, Region - Maturity diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f67142d1ed..5e8958d7d8 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2577,7 +2577,7 @@ bool LLAppViewer::initConfiguration() std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel")); if(! CmdLineChannel.empty()) - { + { LLVersionInfo::resetChannel(CmdLineChannel); } @@ -2589,16 +2589,16 @@ bool LLAppViewer::initConfiguration() LLFastTimer::sLog = TRUE; LLFastTimer::sLogName = std::string("performance"); } - + std::string test_name(gSavedSettings.getString("LogMetrics")); if (! test_name.empty()) - { - LLFastTimer::sMetricLog = TRUE ; + { + LLFastTimer::sMetricLog = TRUE ; // '--logmetrics' is specified with a named test metric argument so the data gathering is done only on that test // In the absence of argument, every metric would be gathered (makes for a rather slow run and hard to decipher report...) llinfos << "'--logmetrics' argument : " << test_name << llendl; - LLFastTimer::sLogName = test_name; - } + LLFastTimer::sLogName = test_name; + } if (clp.hasOption("graphicslevel")) { @@ -2607,14 +2607,14 @@ bool LLAppViewer::initConfiguration() // that value for validity. U32 graphicslevel = gSavedSettings.getU32("RenderQualityPerformance"); if (LLFeatureManager::instance().isValidGraphicsLevel(graphicslevel)) - { + { // graphicslevel is valid: save it and engage it later. Capture // the requested value separately from the settings variable // because, if this is the first run, LLViewerWindow's constructor // will call LLFeatureManager::applyRecommendedSettings(), which // overwrites this settings variable! mForceGraphicsLevel = graphicslevel; - } + } } LLFastTimerView::sAnalyzePerformance = gSavedSettings.getBOOL("AnalyzePerformance"); @@ -2645,16 +2645,32 @@ bool LLAppViewer::initConfiguration() // What can happen is that someone can use IE (or potentially // other browsers) and do the rough equivalent of command // injection and steal passwords. Phoenix. SL-55321 + LLSLURL start_slurl; std::string CmdLineLoginLocation(gSavedSettings.getString("CmdLineLoginLocation")); if(! CmdLineLoginLocation.empty()) - { - LLSLURL start_slurl(CmdLineLoginLocation); + { + start_slurl = CmdLineLoginLocation; LLStartUp::setStartSLURL(start_slurl); if(start_slurl.getType() == LLSLURL::LOCATION) { LLGridManager::getInstance()->setGridChoice(start_slurl.getGrid()); + } + } + + //RN: if we received a URL, hand it off to the existing instance. + // don't call anotherInstanceRunning() when doing URL handoff, as + // it relies on checking a marker file which will not work when running + // out of different directories + + if (start_slurl.isValid() && + (gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) + { + if (sendURLToOtherInstance(start_slurl.getSLURLString())) + { + // successfully handed off URL to existing instance, exit + return false; } - } + } const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) @@ -2795,7 +2811,7 @@ bool LLAppViewer::initConfiguration() LL_DEBUGS("AppInit")<<"set start from NextLoginLocation: "<<nextLoginLocation<<LL_ENDL; LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation)); } - else if ((clp.hasOption("login") || clp.hasOption("autologin")) + else if ( ( clp.hasOption("login") || clp.hasOption("autologin")) && gSavedSettings.getString("CmdLineLoginLocation").empty()) { // If automatic login from command line with --login switch @@ -3179,7 +3195,7 @@ bool LLAppViewer::initWindow() LLFeatureManager::getInstance()->setGraphicsLevel(*mForceGraphicsLevel, false); gSavedSettings.setU32("RenderQualityPerformance", *mForceGraphicsLevel); } - + // Set this flag in case we crash while initializing GL gSavedSettings.setBOOL("RenderInitError", TRUE); gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp new file mode 100644 index 0000000000..497354acc7 --- /dev/null +++ b/indra/newview/llfacebookconnect.cpp @@ -0,0 +1,606 @@ +/** + * @file llfacebookconnect.h + * @author Merov, Cho, Gil + * @brief Connection to Facebook Service + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfacebookconnect.h" + +#include "llagent.h" +#include "llcallingcard.h" // for LLAvatarTracker +#include "llcommandhandler.h" +#include "llhttpclient.h" +#include "llnotificationsutil.h" +#include "llurlaction.h" +#include "llimagepng.h" +#include "llimagejpeg.h" +#include "lltrans.h" +#include "llevents.h" +#include "llviewerregion.h" + +#include "llfloaterwebcontent.h" +#include "llfloaterreg.h" + +boost::scoped_ptr<LLEventPump> LLFacebookConnect::sStateWatcher(new LLEventStream("FacebookConnectState")); +boost::scoped_ptr<LLEventPump> LLFacebookConnect::sInfoWatcher(new LLEventStream("FacebookConnectInfo")); +boost::scoped_ptr<LLEventPump> LLFacebookConnect::sContentWatcher(new LLEventStream("FacebookConnectContent")); + +// Local functions +void log_facebook_connect_error(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description) +{ + // Note: 302 (redirect) is *not* an error that warrants logging + if (status != 302) + { + LL_WARNS("FacebookConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL; + } +} + +void toast_user_for_success() +{ + LLSD args; + args["MESSAGE"] = LLTrans::getString("facebook_post_success"); + LLNotificationsUtil::add("FacebookConnect", args); +} + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookConnectHandler : public LLCommandHandler +{ +public: + LLFacebookConnectHandler() : LLCommandHandler("fbc", UNTRUSTED_THROTTLE) { } + + bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) + { + if (tokens.size() > 0) + { + if (tokens[0].asString() == "connect") + { + // this command probably came from the fbc_web browser, so close it + LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web"); + if (fbc_web) + { + fbc_web->closeFloater(); + } + + // connect to facebook + if (query_map.has("code")) + { + LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state")); + } + return true; + } + } + return false; + } +}; +LLFacebookConnectHandler gFacebookConnectHandler; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookConnectResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookConnectResponder); +public: + + LLFacebookConnectResponder() + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS); + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + LL_DEBUGS("FacebookConnect") << "Connect successful. content: " << content << LL_ENDL; + + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED); + } + else if (status != 302) + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED); + log_facebook_connect_error("Connect", status, reason, content.get("error_code"), content.get("error_description")); + } + } + + void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + if (status == 302) + { + LLFacebookConnect::instance().openFacebookWeb(content["location"]); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookShareResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookShareResponder); +public: + + LLFacebookShareResponder() + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTING); + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + toast_user_for_success(); + LL_DEBUGS("FacebookConnect") << "Post successful. content: " << content << LL_ENDL; + + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED); + } + else if (status == 404) + { + LLFacebookConnect::instance().connectToFacebook(); + } + else + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POST_FAILED); + log_facebook_connect_error("Share", status, reason, content.get("error_code"), content.get("error_description")); + } + } + + void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + if (status == 302) + { + LLFacebookConnect::instance().openFacebookWeb(content["location"]); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookDisconnectResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookDisconnectResponder); +public: + + LLFacebookDisconnectResponder() + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECTING); + } + + void setUserDisconnected() + { + // Clear data + LLFacebookConnect::instance().clearInfo(); + LLFacebookConnect::instance().clearContent(); + //Notify state change + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED); + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + LL_DEBUGS("FacebookConnect") << "Disconnect successful. content: " << content << LL_ENDL; + setUserDisconnected(); + + } + //User not found so already disconnected + else if(status == 404) + { + LL_DEBUGS("FacebookConnect") << "Already disconnected. content: " << content << LL_ENDL; + setUserDisconnected(); + } + else + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_DISCONNECT_FAILED); + log_facebook_connect_error("Disconnect", status, reason, content.get("error_code"), content.get("error_description")); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookConnectedResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookConnectedResponder); +public: + + LLFacebookConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect) + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_IN_PROGRESS); + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + LL_DEBUGS("FacebookConnect") << "Connect successful. content: " << content << LL_ENDL; + + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTED); + } + else + { + // show the facebook login page if not connected yet + if (status == 404) + { + if (mAutoConnect) + { + LLFacebookConnect::instance().connectToFacebook(); + } + else + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_NOT_CONNECTED); + } + } + else + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED); + log_facebook_connect_error("Connected", status, reason, content.get("error_code"), content.get("error_description")); + } + } + } + +private: + bool mAutoConnect; +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookInfoResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookInfoResponder); +public: + + virtual void completed(U32 status, const std::string& reason, const LLSD& info) + { + if (isGoodStatus(status)) + { + llinfos << "Facebook: Info received" << llendl; + LL_DEBUGS("FacebookConnect") << "Getting Facebook info successful. info: " << info << LL_ENDL; + LLFacebookConnect::instance().storeInfo(info); + } + else + { + log_facebook_connect_error("Info", status, reason, info.get("error_code"), info.get("error_description")); + } + } + + void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + if (status == 302) + { + LLFacebookConnect::instance().openFacebookWeb(content["location"]); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookFriendsResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookFriendsResponder); +public: + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + LL_DEBUGS("FacebookConnect") << "Getting Facebook friends successful. content: " << content << LL_ENDL; + LLFacebookConnect::instance().storeContent(content); + } + else + { + log_facebook_connect_error("Friends", status, reason, content.get("error_code"), content.get("error_description")); + } + } + + void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + if (status == 302) + { + LLFacebookConnect::instance().openFacebookWeb(content["location"]); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +LLFacebookConnect::LLFacebookConnect() +: mConnectionState(FB_NOT_CONNECTED), + mConnected(false), + mInfo(), + mContent(), + mRefreshInfo(false), + mRefreshContent(false), + mReadFromMaster(false) +{ +} + +void LLFacebookConnect::openFacebookWeb(std::string url) +{ + // Open the URL in an internal browser window without navigation UI + LLFloaterWebContent::Params p; + p.url(url).show_chrome(true); + p.url(url).allow_address_entry(false); + p.url(url).allow_back_forward_navigation(false); + p.url(url).trusted_content(true); + LLFloater *floater = LLFloaterReg::showInstance("fbc_web", p); + //the internal web browser has a bug that prevents it from gaining focus unless a mouse event occurs first (it seems). + //So when showing the internal web browser, set focus to it's containing floater "fbc_web". When a mouse event + //occurs on the "webbrowser" panel part of the floater, a mouse cursor will properly show and the "webbrowser" will gain focus. + //fbc_web floater contains the "webbrowser" panel. JIRA: ACME-744 + gFocusMgr.setKeyboardFocus( floater ); + + //LLUrlAction::openURLExternal(url); +} + +std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route, bool include_read_from_master) +{ + std::string url = gAgent.getRegion()->getCapability("FacebookConnect"); + url += route; + + if (include_read_from_master && mReadFromMaster) + { + url += "?read_from_master=true"; + } + return url; +} + +std::string LLFacebookConnect::getFlickrConnectURL(const std::string& route, bool include_read_from_master) +{ + std::string url = gAgent.getRegion()->getCapability("FlickrConnect"); + url += route; + + if (include_read_from_master && mReadFromMaster) + { + url += "?read_from_master=true"; + } + return url; +} + +std::string LLFacebookConnect::getTwitterConnectURL(const std::string& route, bool include_read_from_master) +{ + std::string url = gAgent.getRegion()->getCapability("TwitterConnect"); + url += route; + + if (include_read_from_master && mReadFromMaster) + { + url += "?read_from_master=true"; + } + return url; +} + +void LLFacebookConnect::connectToFacebook(const std::string& auth_code, const std::string& auth_state) +{ + LLSD body; + if (!auth_code.empty()) + body["code"] = auth_code; + if (!auth_state.empty()) + body["state"] = auth_state; + + LLHTTPClient::put(getFacebookConnectURL("/connection"), body, new LLFacebookConnectResponder()); +} + +void LLFacebookConnect::disconnectFromFacebook() +{ + LLHTTPClient::del(getFacebookConnectURL("/connection"), new LLFacebookDisconnectResponder()); +} + +void LLFacebookConnect::checkConnectionToFacebook(bool auto_connect) +{ + const bool follow_redirects = false; + const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + LLHTTPClient::get(getFacebookConnectURL("/connection", true), new LLFacebookConnectedResponder(auto_connect), + LLSD(), timeout, follow_redirects); + + // TEMPORARY FOR TESTING - CHO + llinfos << "FlickrConnect URL: " << getFlickrConnectURL() << LL_ENDL; + llinfos << "TwitterConnect URL: " << getTwitterConnectURL() << LL_ENDL; + +} + +void LLFacebookConnect::loadFacebookInfo() +{ + if(mRefreshInfo) + { + const bool follow_redirects = false; + const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + LLHTTPClient::get(getFacebookConnectURL("/info", true), new LLFacebookInfoResponder(), + LLSD(), timeout, follow_redirects); + } +} + +void LLFacebookConnect::loadFacebookFriends() +{ + if(mRefreshContent) + { + const bool follow_redirects = false; + const F32 timeout = HTTP_REQUEST_EXPIRY_SECS; + LLHTTPClient::get(getFacebookConnectURL("/friends", true), new LLFacebookFriendsResponder(), + LLSD(), timeout, follow_redirects); + } +} + +void LLFacebookConnect::postCheckin(const std::string& location, const std::string& name, const std::string& description, const std::string& image, const std::string& message) +{ + LLSD body; + if (!location.empty()) + body["location"] = location; + if (!name.empty()) + body["name"] = name; + if (!description.empty()) + body["description"] = description; + if (!image.empty()) + body["image"] = image; + if (!message.empty()) + body["message"] = message; + + // Note: we can use that route for different publish action. We should be able to use the same responder. + LLHTTPClient::post(getFacebookConnectURL("/share/checkin", true), body, new LLFacebookShareResponder()); +} + +void LLFacebookConnect::sharePhoto(const std::string& image_url, const std::string& caption) +{ + LLSD body; + body["image"] = image_url; + body["caption"] = caption; + + // Note: we can use that route for different publish action. We should be able to use the same responder. + LLHTTPClient::post(getFacebookConnectURL("/share/photo", true), body, new LLFacebookShareResponder()); +} + +void LLFacebookConnect::sharePhoto(LLPointer<LLImageFormatted> image, const std::string& caption) +{ + std::string imageFormat; + if (dynamic_cast<LLImagePNG*>(image.get())) + { + imageFormat = "png"; + } + else if (dynamic_cast<LLImageJPEG*>(image.get())) + { + imageFormat = "jpg"; + } + else + { + llwarns << "Image to upload is not a PNG or JPEG" << llendl; + return; + } + + // All this code is mostly copied from LLWebProfile::post() + const std::string boundary = "----------------------------0123abcdefab"; + + LLSD headers; + headers["Content-Type"] = "multipart/form-data; boundary=" + boundary; + + std::ostringstream body; + + // *NOTE: The order seems to matter. + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"caption\"\r\n\r\n" + << caption << "\r\n"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << imageFormat << "\"\r\n" + << "Content-Type: image/" << imageFormat << "\r\n\r\n"; + + // Insert the image data. + // *FIX: Treating this as a string will probably screw it up ... + U8* image_data = image->getData(); + for (S32 i = 0; i < image->getDataSize(); ++i) + { + body << image_data[i]; + } + + body << "\r\n--" << boundary << "--\r\n"; + + // postRaw() takes ownership of the buffer and releases it later. + size_t size = body.str().size(); + U8 *data = new U8[size]; + memcpy(data, body.str().data(), size); + + // Note: we can use that route for different publish action. We should be able to use the same responder. + LLHTTPClient::postRaw(getFacebookConnectURL("/share/photo", true), data, size, new LLFacebookShareResponder(), headers); +} + +void LLFacebookConnect::updateStatus(const std::string& message) +{ + LLSD body; + body["message"] = message; + + // Note: we can use that route for different publish action. We should be able to use the same responder. + LLHTTPClient::post(getFacebookConnectURL("/share/wall", true), body, new LLFacebookShareResponder()); +} + +void LLFacebookConnect::storeInfo(const LLSD& info) +{ + mInfo = info; + mRefreshInfo = false; + + sInfoWatcher->post(info); +} + +const LLSD& LLFacebookConnect::getInfo() const +{ + return mInfo; +} + +void LLFacebookConnect::clearInfo() +{ + mInfo = LLSD(); +} + +void LLFacebookConnect::storeContent(const LLSD& content) +{ + mContent = content; + mRefreshContent = false; + + sContentWatcher->post(content); +} + +const LLSD& LLFacebookConnect::getContent() const +{ + return mContent; +} + +void LLFacebookConnect::clearContent() +{ + mContent = LLSD(); +} + +void LLFacebookConnect::setDataDirty() +{ + mRefreshInfo = true; + mRefreshContent = true; +} + +void LLFacebookConnect::setConnectionState(LLFacebookConnect::EConnectionState connection_state) +{ + if(connection_state == FB_CONNECTED) + { + mReadFromMaster = true; + setConnected(true); + setDataDirty(); + } + else if(connection_state == FB_NOT_CONNECTED) + { + setConnected(false); + } + else if(connection_state == FB_POSTED) + { + mReadFromMaster = false; + } + + if (mConnectionState != connection_state) + { + LLSD state_info; + state_info["enum"] = connection_state; + sStateWatcher->post(state_info); + } + + mConnectionState = connection_state; +} + +void LLFacebookConnect::setConnected(bool connected) +{ + mConnected = connected; +} diff --git a/indra/newview/llfacebookconnect.h b/indra/newview/llfacebookconnect.h new file mode 100644 index 0000000000..c4117174c1 --- /dev/null +++ b/indra/newview/llfacebookconnect.h @@ -0,0 +1,110 @@ +/** + * @file llfacebookconnect.h + * @author Merov, Cho, Gil + * @brief Connection to Facebook Service + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFACEBOOKCONNECT_H +#define LL_LLFACEBOOKCONNECT_H + +#include "llsingleton.h" +#include "llimage.h" + +class LLEventPump; + +/** + * @class LLFacebookConnect + * + * Manages authentication to, and interaction with, a web service allowing the + * the viewer to get Facebook OpenGraph data. + */ +class LLFacebookConnect : public LLSingleton<LLFacebookConnect> +{ + LOG_CLASS(LLFacebookConnect); +public: + enum EConnectionState + { + FB_NOT_CONNECTED = 0, + FB_CONNECTION_IN_PROGRESS = 1, + FB_CONNECTED = 2, + FB_CONNECTION_FAILED = 3, + FB_POSTING = 4, + FB_POSTED = 5, + FB_POST_FAILED = 6, + FB_DISCONNECTING = 7, + FB_DISCONNECT_FAILED = 8 + }; + + void connectToFacebook(const std::string& auth_code = "", const std::string& auth_state = ""); // Initiate the complete FB connection. Please use checkConnectionToFacebook() in normal use. + void disconnectFromFacebook(); // Disconnect from the FBC service. + void checkConnectionToFacebook(bool auto_connect = false); // Check if an access token is available on the FBC service. If not, call connectToFacebook(). + + void loadFacebookInfo(); + void loadFacebookFriends(); + void postCheckin(const std::string& location, const std::string& name, const std::string& description, const std::string& picture, const std::string& message); + void sharePhoto(const std::string& image_url, const std::string& caption); + void sharePhoto(LLPointer<LLImageFormatted> image, const std::string& caption); + void updateStatus(const std::string& message); + + void storeInfo(const LLSD& info); + const LLSD& getInfo() const; + void clearInfo(); + void storeContent(const LLSD& content); + const LLSD& getContent() const; + void clearContent(); + void setDataDirty(); + + void setConnectionState(EConnectionState connection_state); + void setConnected(bool connected); + bool isConnected() { return mConnected; } + bool isTransactionOngoing() { return ((mConnectionState == FB_CONNECTION_IN_PROGRESS) || (mConnectionState == FB_POSTING) || (mConnectionState == FB_DISCONNECTING)); } + EConnectionState getConnectionState() { return mConnectionState; } + + void openFacebookWeb(std::string url); + +private: + friend class LLSingleton<LLFacebookConnect>; + + LLFacebookConnect(); + ~LLFacebookConnect() {}; + std::string getFacebookConnectURL(const std::string& route = "", bool include_read_from_master = false); + + // TEMPORARY FOR TESTING - CHO + std::string getFlickrConnectURL(const std::string& route = "", bool include_read_from_master = false); + std::string getTwitterConnectURL(const std::string& route = "", bool include_read_from_master = false); + + EConnectionState mConnectionState; + BOOL mConnected; + LLSD mInfo; + LLSD mContent; + bool mRefreshInfo; + bool mRefreshContent; + bool mReadFromMaster; + + static boost::scoped_ptr<LLEventPump> sStateWatcher; + static boost::scoped_ptr<LLEventPump> sInfoWatcher; + static boost::scoped_ptr<LLEventPump> sContentWatcher; +}; + +#endif // LL_LLFACEBOOKCONNECT_H diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 2c3b34e128..21bc747648 100755 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -662,10 +662,10 @@ void LLFloaterIMContainer::setVisible(BOOL visible) LLFloater* session_floater = widget->getSessionFloater(); if (session_floater != nearby_chat) { - widget->setVisibleIfDetached(visible); - } + widget->setVisibleIfDetached(visible); } } + } // Now, do the normal multifloater show/hide LLMultiFloater::setVisible(visible); @@ -700,13 +700,13 @@ void LLFloaterIMContainer::setVisibleAndFrontmost(BOOL take_focus, const LLSD& k // Only select other sessions if (!getSelectedSession().isNull()) { - selectConversationPair(getSelectedSession(), false, take_focus); + selectConversationPair(getSelectedSession(), false, take_focus); } if (mInitialized && mIsFirstLaunch) { collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); mIsFirstLaunch = false; - } +} } void LLFloaterIMContainer::updateResizeLimits() @@ -834,7 +834,7 @@ void LLFloaterIMContainer::assignResizeLimits() S32 conv_pane_target_width = is_conv_pane_expanded ? ( is_msg_pane_expanded?mConversationsPane->getRect().getWidth():mConversationsPane->getExpandedMinDim() ) - : mConversationsPane->getMinDim(); + : mConversationsPane->getMinDim(); S32 msg_pane_min_width = is_msg_pane_expanded ? mMessagesPane->getExpandedMinDim() : 0; S32 new_min_width = conv_pane_target_width + msg_pane_min_width + summary_width_of_visible_borders; @@ -995,7 +995,7 @@ void LLFloaterIMContainer::setSortOrder(const LLConversationSort& order) conversation_floater->setSortOrder(order); } } - + gSavedSettings.setU32("ConversationSortOrder", (U32)order); } @@ -1182,7 +1182,7 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, } else if("chat_history" == command) { - if (selectedIDS.size() > 0) + if (selectedIDS.size() > 0) { LLAvatarActions::viewChatHistory(selectedIDS.front()); } @@ -1204,7 +1204,7 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, { LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true); } - } +} } } @@ -1235,7 +1235,7 @@ void LLFloaterIMContainer::doToSelectedGroup(const LLSD& userdata) if (action == "group_profile") { - LLGroupActions::show(mSelectedSession); + LLGroupActions::show(mSelectedSession); } else if (action == "activate_group") { diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index d8d62e5bbb..ea385d7baf 100755 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -28,60 +28,23 @@ #include "llfloatersnapshot.h" -#include "llfloaterreg.h" - -// Viewer includes #include "llagent.h" -#include "llagentcamera.h" -#include "llcallbacklist.h" -#include "llcriticaldamp.h" -#include "llfloaterperms.h" -#include "llui.h" -#include "llfocusmgr.h" -#include "llbutton.h" +#include "llfacebookconnect.h" +#include "llfloaterreg.h" +#include "llfloatersocial.h" +#include "llcheckboxctrl.h" #include "llcombobox.h" -#include "lleconomy.h" -#include "lllandmarkactions.h" -#include "llpanelsnapshot.h" +#include "llpostcard.h" +#include "llresmgr.h" // LLLocale +#include "llsdserialize.h" #include "llsidetraypanelcontainer.h" -#include "llsliderctrl.h" +#include "llsnapshotlivepreview.h" #include "llspinctrl.h" #include "llviewercontrol.h" -#include "lluictrlfactory.h" -#include "llviewerstats.h" -#include "llviewercamera.h" -#include "llviewerwindow.h" -#include "llviewermenufile.h" // upload_new_resource() -#include "llcheckboxctrl.h" -#include "llslurl.h" #include "lltoolfocus.h" #include "lltoolmgr.h" -#include "llwebsharing.h" -#include "llworld.h" -#include "llagentui.h" - -// Linden library includes -#include "llfontgl.h" -#include "llsys.h" -#include "llrender.h" -#include "v3dmath.h" -#include "llmath.h" -#include "lldir.h" -#include "llsdserialize.h" -#include "llgl.h" -#include "llglheaders.h" -#include "llimagejpeg.h" -#include "llimagepng.h" -#include "llimagebmp.h" -#include "llimagej2c.h" -#include "lllocalcliprect.h" -#include "llnotificationsutil.h" -#include "llpostcard.h" -#include "llresmgr.h" // LLLocale -#include "llvfile.h" -#include "llvfs.h" #include "llwebprofile.h" -#include "llwindow.h" +#include "llwebsharing.h" ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs @@ -91,949 +54,12 @@ LLSnapshotFloaterView* gSnapshotFloaterView = NULL; const F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f; -F32 SHINE_TIME = 0.5f; -F32 SHINE_WIDTH = 0.6f; -F32 SHINE_OPACITY = 0.3f; -F32 FALL_TIME = 0.6f; -S32 BORDER_WIDTH = 6; - const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512 static LLDefaultChildRegistry::Register<LLSnapshotFloaterView> r("snapshot_floater_view"); -///---------------------------------------------------------------------------- -/// Class LLSnapshotLivePreview -///---------------------------------------------------------------------------- -class LLSnapshotLivePreview : public LLView -{ - LOG_CLASS(LLSnapshotLivePreview); -public: - enum ESnapshotType - { - SNAPSHOT_POSTCARD, - SNAPSHOT_TEXTURE, - SNAPSHOT_LOCAL, - SNAPSHOT_WEB - }; - - - struct Params : public LLInitParam::Block<Params, LLView::Params> - { - Params() - { - name = "snapshot_live_preview"; - mouse_opaque = false; - } - }; - - - LLSnapshotLivePreview(const LLSnapshotLivePreview::Params& p); - ~LLSnapshotLivePreview(); - - /*virtual*/ void draw(); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent); - - void setSize(S32 w, S32 h); - void setWidth(S32 w) { mWidth[mCurImageIndex] = w; } - void setHeight(S32 h) { mHeight[mCurImageIndex] = h; } - void getSize(S32& w, S32& h) const; - S32 getWidth() const { return mWidth[mCurImageIndex]; } - S32 getHeight() const { return mHeight[mCurImageIndex]; } - S32 getDataSize() const { return mDataSize; } - void setMaxImageSize(S32 size) ; - S32 getMaxImageSize() {return mMaxImageSize ;} - - ESnapshotType getSnapshotType() const { return mSnapshotType; } - LLFloaterSnapshot::ESnapshotFormat getSnapshotFormat() const { return mSnapshotFormat; } - BOOL getSnapshotUpToDate() const { return mSnapshotUpToDate; } - BOOL isSnapshotActive() { return mSnapshotActive; } - LLViewerTexture* getThumbnailImage() const { return mThumbnailImage ; } - S32 getThumbnailWidth() const { return mThumbnailWidth ; } - S32 getThumbnailHeight() const { return mThumbnailHeight ; } - BOOL getThumbnailLock() const { return mThumbnailUpdateLock ; } - BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;} - LLViewerTexture* getCurrentImage(); - F32 getImageAspect(); - F32 getAspect() ; - const LLRect& getImageRect() const { return mImageRect[mCurImageIndex]; } - BOOL isImageScaled() const { return mImageScaled[mCurImageIndex]; } - void setImageScaled(BOOL scaled) { mImageScaled[mCurImageIndex] = scaled; } - const LLVector3d& getPosTakenGlobal() const { return mPosTakenGlobal; } - - void setSnapshotType(ESnapshotType type) { mSnapshotType = type; } - void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; } - void setSnapshotQuality(S32 quality); - void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; } - void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f); - void saveWeb(); - void saveTexture(); - BOOL saveLocal(); - - LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; } - LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; } - - /// Sets size of preview thumbnail image and thhe surrounding rect. - BOOL setThumbnailImageSize() ; - void generateThumbnailImage(BOOL force_update = FALSE) ; - void resetThumbnailImage() { mThumbnailImage = NULL ; } - void drawPreviewRect(S32 offset_x, S32 offset_y) ; - - // Returns TRUE when snapshot generated, FALSE otherwise. - static BOOL onIdle( void* snapshot_preview ); - - // callback for region name resolve - void regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z); - -private: - LLColor4 mColor; - LLPointer<LLViewerTexture> mViewerImage[2]; //used to represent the scene when the frame is frozen. - LLRect mImageRect[2]; - S32 mWidth[2]; - S32 mHeight[2]; - BOOL mImageScaled[2]; - S32 mMaxImageSize ; - - //thumbnail image - LLPointer<LLViewerTexture> mThumbnailImage ; - S32 mThumbnailWidth ; - S32 mThumbnailHeight ; - LLRect mPreviewRect ; - BOOL mThumbnailUpdateLock ; - BOOL mThumbnailUpToDate ; - - S32 mCurImageIndex; - LLPointer<LLImageRaw> mPreviewImage; - LLPointer<LLImageRaw> mPreviewImageEncoded; - LLPointer<LLImageFormatted> mFormattedImage; - LLFrameTimer mSnapshotDelayTimer; - S32 mShineCountdown; - LLFrameTimer mShineAnimTimer; - F32 mFlashAlpha; - BOOL mNeedsFlash; - LLVector3d mPosTakenGlobal; - S32 mSnapshotQuality; - S32 mDataSize; - ESnapshotType mSnapshotType; - LLFloaterSnapshot::ESnapshotFormat mSnapshotFormat; - BOOL mSnapshotUpToDate; - LLFrameTimer mFallAnimTimer; - LLVector3 mCameraPos; - LLQuaternion mCameraRot; - BOOL mSnapshotActive; - LLViewerWindow::ESnapshotType mSnapshotBufferType; - -public: - static std::set<LLSnapshotLivePreview*> sList; - BOOL mKeepAspectRatio ; -}; - -std::set<LLSnapshotLivePreview*> LLSnapshotLivePreview::sList; - -LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Params& p) -: LLView(p), - mColor(1.f, 0.f, 0.f, 0.5f), - mCurImageIndex(0), - mPreviewImage(NULL), - mThumbnailImage(NULL) , - mThumbnailWidth(0), - mThumbnailHeight(0), - mPreviewImageEncoded(NULL), - mFormattedImage(NULL), - mShineCountdown(0), - mFlashAlpha(0.f), - mNeedsFlash(TRUE), - mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")), - mDataSize(0), - mSnapshotType(SNAPSHOT_POSTCARD), - mSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))), - mSnapshotUpToDate(FALSE), - mCameraPos(LLViewerCamera::getInstance()->getOrigin()), - mCameraRot(LLViewerCamera::getInstance()->getQuaternion()), - mSnapshotActive(FALSE), - mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR) -{ - setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")); - mSnapshotDelayTimer.setTimerExpirySec(0.0f); - mSnapshotDelayTimer.start(); -// gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this ); - sList.insert(this); - setFollowsAll(); - mWidth[0] = gViewerWindow->getWindowWidthRaw(); - mWidth[1] = gViewerWindow->getWindowWidthRaw(); - mHeight[0] = gViewerWindow->getWindowHeightRaw(); - mHeight[1] = gViewerWindow->getWindowHeightRaw(); - mImageScaled[0] = FALSE; - mImageScaled[1] = FALSE; - - mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ; - mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ; - mThumbnailUpdateLock = FALSE ; - mThumbnailUpToDate = FALSE ; -} - -LLSnapshotLivePreview::~LLSnapshotLivePreview() -{ - // delete images - mPreviewImage = NULL; - mPreviewImageEncoded = NULL; - mFormattedImage = NULL; - -// gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this ); - sList.erase(this); -} - -void LLSnapshotLivePreview::setMaxImageSize(S32 size) -{ - if(size < MAX_SNAPSHOT_IMAGE_SIZE) - { - mMaxImageSize = size; - } - else - { - mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ; - } -} - -LLViewerTexture* LLSnapshotLivePreview::getCurrentImage() -{ - return mViewerImage[mCurImageIndex]; -} - -F32 LLSnapshotLivePreview::getAspect() -{ - F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight()); - F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()); - - if (!mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot")) - { - return image_aspect_ratio; - } - else - { - return window_aspect_ratio; - } -} - -F32 LLSnapshotLivePreview::getImageAspect() -{ - if (!getCurrentImage()) - { - return 0.f; - } - - return getAspect() ; -} - -void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay) -{ - // Invalidate current image. - lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl; - if (getSnapshotUpToDate()) - { - S32 old_image_index = mCurImageIndex; - mCurImageIndex = (mCurImageIndex + 1) % 2; - setSize(mWidth[old_image_index], mHeight[old_image_index]); - mFallAnimTimer.start(); - } - mSnapshotUpToDate = FALSE; - - // Update snapshot source rect depending on whether we keep the aspect ratio. - LLRect& rect = mImageRect[mCurImageIndex]; - rect.set(0, getRect().getHeight(), getRect().getWidth(), 0); - - F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight()); - F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()); - - if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot")) - { - if (image_aspect_ratio > window_aspect_ratio) - { - // trim off top and bottom - S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio); - rect.mBottom += (getRect().getHeight() - new_height) / 2; - rect.mTop -= (getRect().getHeight() - new_height) / 2; - } - else if (image_aspect_ratio < window_aspect_ratio) - { - // trim off left and right - S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio); - rect.mLeft += (getRect().getWidth() - new_width) / 2; - rect.mRight -= (getRect().getWidth() - new_width) / 2; - } - } - - // Stop shining animation. - mShineAnimTimer.stop(); - - // Update snapshot if requested. - if (new_snapshot) - { - mSnapshotDelayTimer.start(); - mSnapshotDelayTimer.setTimerExpirySec(delay); - LLFloaterSnapshot::preUpdate(); - } - - // Update thumbnail if requested. - if(new_thumbnail) - { - mThumbnailUpToDate = FALSE ; - } -} - -void LLSnapshotLivePreview::setSnapshotQuality(S32 quality) -{ - llclamp(quality, 0, 100); - if (quality != mSnapshotQuality) - { - mSnapshotQuality = quality; - gSavedSettings.setS32("SnapshotQuality", quality); - mSnapshotUpToDate = FALSE; - } -} - -void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y) -{ - F32 line_width ; - glGetFloatv(GL_LINE_WIDTH, &line_width) ; - glLineWidth(2.0f * line_width) ; - LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ; - gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y, - mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, FALSE ) ; - glLineWidth(line_width) ; - - //draw four alpha rectangles to cover areas outside of the snapshot image - if(!mKeepAspectRatio) - { - LLColor4 alpha_color(0.5f, 0.5f, 0.5f, 0.8f) ; - S32 dwl = 0, dwr = 0 ; - if(mThumbnailWidth > mPreviewRect.getWidth()) - { - dwl = (mThumbnailWidth - mPreviewRect.getWidth()) >> 1 ; - dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ; - - gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y, - mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ; - gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y, - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ; - } - - if(mThumbnailHeight > mPreviewRect.getHeight()) - { - S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ; - gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y , - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, TRUE ) ; - - dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ; - gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh, - mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, TRUE ) ; - } - } -} - -//called when the frame is frozen. -void LLSnapshotLivePreview::draw() -{ - if (getCurrentImage() && - mPreviewImageEncoded.notNull() && - getSnapshotUpToDate()) - { - LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f); - gl_rect_2d(getRect(), bg_color); - const LLRect& rect = getImageRect(); - LLRect shadow_rect = rect; - shadow_rect.stretch(BORDER_WIDTH); - gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10); - - LLColor4 image_color(1.f, 1.f, 1.f, 1.f); - gGL.color4fv(image_color.mV); - gGL.getTexUnit(0)->bind(getCurrentImage()); - // calculate UV scale - F32 uv_width = isImageScaled() ? 1.f : llmin((F32)getWidth() / (F32)getCurrentImage()->getWidth(), 1.f); - F32 uv_height = isImageScaled() ? 1.f : llmin((F32)getHeight() / (F32)getCurrentImage()->getHeight(), 1.f); - gGL.pushMatrix(); - { - gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom, 0.f); - gGL.begin(LLRender::QUADS); - { - gGL.texCoord2f(uv_width, uv_height); - gGL.vertex2i(rect.getWidth(), rect.getHeight() ); - - gGL.texCoord2f(0.f, uv_height); - gGL.vertex2i(0, rect.getHeight() ); - - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2i(0, 0); - - gGL.texCoord2f(uv_width, 0.f); - gGL.vertex2i(rect.getWidth(), 0); - } - gGL.end(); - } - gGL.popMatrix(); - - gGL.color4f(1.f, 1.f, 1.f, mFlashAlpha); - gl_rect_2d(getRect()); - if (mNeedsFlash) - { - if (mFlashAlpha < 1.f) - { - mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f)); - } - else - { - mNeedsFlash = FALSE; - } - } - else - { - mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f)); - } - - // Draw shining animation if appropriate. - if (mShineCountdown > 0) - { - mShineCountdown--; - if (mShineCountdown == 0) - { - mShineAnimTimer.start(); - } - } - else if (mShineAnimTimer.getStarted()) - { - lldebugs << "Drawing shining animation" << llendl; - F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME); - - // draw "shine" effect - LLLocalClipRect clip(getLocalRect()); - { - // draw diagonal stripe with gradient that passes over screen - S32 x1 = gViewerWindow->getWindowWidthScaled() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f))); - S32 x2 = x1 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); - S32 x3 = x2 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); - S32 y1 = 0; - S32 y2 = gViewerWindow->getWindowHeightScaled(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.begin(LLRender::QUADS); - { - gGL.color4f(1.f, 1.f, 1.f, 0.f); - gGL.vertex2i(x1, y1); - gGL.vertex2i(x1 + gViewerWindow->getWindowWidthScaled(), y2); - gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY); - gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2); - gGL.vertex2i(x2, y1); - - gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY); - gGL.vertex2i(x2, y1); - gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2); - gGL.color4f(1.f, 1.f, 1.f, 0.f); - gGL.vertex2i(x3 + gViewerWindow->getWindowWidthScaled(), y2); - gGL.vertex2i(x3, y1); - } - gGL.end(); - } - - // if we're at the end of the animation, stop - if (shine_interp >= 1.f) - { - mShineAnimTimer.stop(); - } - } - } - - // draw framing rectangle - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4f(1.f, 1.f, 1.f, 1.f); - const LLRect& outline_rect = getImageRect(); - gGL.begin(LLRender::QUADS); - { - gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH); - gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH); - gGL.vertex2i(outline_rect.mRight, outline_rect.mTop); - gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop); - - gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom); - gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom); - gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH); - gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH); - - gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop); - gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom); - gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH); - gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH); - - gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom); - gGL.vertex2i(outline_rect.mRight, outline_rect.mTop); - gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH); - gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH); - } - gGL.end(); - } - - // draw old image dropping away - if (mFallAnimTimer.getStarted()) - { - S32 old_image_index = (mCurImageIndex + 1) % 2; - if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME) - { - lldebugs << "Drawing fall animation" << llendl; - F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME; - F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f); - LLColor4 image_color(1.f, 1.f, 1.f, alpha); - gGL.color4fv(image_color.mV); - gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]); - // calculate UV scale - // *FIX get this to work with old image - BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull(); - F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f; - F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f; - gGL.pushMatrix(); - { - LLRect& rect = mImageRect[old_image_index]; - gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - llround(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f); - gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f); - gGL.begin(LLRender::QUADS); - { - gGL.texCoord2f(uv_width, uv_height); - gGL.vertex2i(rect.getWidth(), rect.getHeight() ); - - gGL.texCoord2f(0.f, uv_height); - gGL.vertex2i(0, rect.getHeight() ); - - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2i(0, 0); - gGL.texCoord2f(uv_width, 0.f); - gGL.vertex2i(rect.getWidth(), 0); - } - gGL.end(); - } - gGL.popMatrix(); - } - } -} - -/*virtual*/ -void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLRect old_rect = getRect(); - LLView::reshape(width, height, called_from_parent); - if (old_rect.getWidth() != width || old_rect.getHeight() != height) - { - lldebugs << "window reshaped, updating thumbnail" << llendl; - updateSnapshot(FALSE, TRUE); - } -} - -BOOL LLSnapshotLivePreview::setThumbnailImageSize() -{ - if(getWidth() < 10 || getHeight() < 10) - { - return FALSE ; - } - S32 window_width = gViewerWindow->getWindowWidthRaw() ; - S32 window_height = gViewerWindow->getWindowHeightRaw() ; - - F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height); - - // UI size for thumbnail - // *FIXME: the rect does not change, so maybe there's no need to recalculate max w/h. - const LLRect& thumbnail_rect = LLFloaterSnapshot::getThumbnailPlaceholderRect(); - S32 max_width = thumbnail_rect.getWidth(); - S32 max_height = thumbnail_rect.getHeight(); - - if (window_aspect_ratio > (F32)max_width / max_height) - { - // image too wide, shrink to width - mThumbnailWidth = max_width; - mThumbnailHeight = llround((F32)max_width / window_aspect_ratio); - } - else - { - // image too tall, shrink to height - mThumbnailHeight = max_height; - mThumbnailWidth = llround((F32)max_height * window_aspect_ratio); - } - - if(mThumbnailWidth > window_width || mThumbnailHeight > window_height) - { - return FALSE ;//if the window is too small, ignore thumbnail updating. - } - - S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ; - if(!mKeepAspectRatio) - { - F32 ratio_x = (F32)getWidth() / window_width ; - F32 ratio_y = (F32)getHeight() / window_height ; - - //if(getWidth() > window_width || - // getHeight() > window_height ) - { - if(ratio_x > ratio_y) - { - top = (S32)(top * ratio_y / ratio_x) ; - } - else - { - right = (S32)(right * ratio_x / ratio_y) ; - } - } - //else - //{ - // right = (S32)(right * ratio_x) ; - // top = (S32)(top * ratio_y) ; - //} - left = (S32)((mThumbnailWidth - right) * 0.5f) ; - bottom = (S32)((mThumbnailHeight - top) * 0.5f) ; - top += bottom ; - right += left ; - } - mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ; - - return TRUE ; -} - -void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) -{ - if(mThumbnailUpdateLock) //in the process of updating - { - return ; - } - if(getThumbnailUpToDate() && !force_update)//already updated - { - return ; - } - if(getWidth() < 10 || getHeight() < 10) - { - return ; - } - - ////lock updating - mThumbnailUpdateLock = TRUE ; - - if(!setThumbnailImageSize()) - { - mThumbnailUpdateLock = FALSE ; - mThumbnailUpToDate = TRUE ; - return ; - } - - if(mThumbnailImage) - { - resetThumbnailImage() ; - } - - LLPointer<LLImageRaw> raw = new LLImageRaw; - if(!gViewerWindow->thumbnailSnapshot(raw, - mThumbnailWidth, mThumbnailHeight, - gSavedSettings.getBOOL("RenderUIInSnapshot"), - FALSE, - mSnapshotBufferType) ) - { - raw = NULL ; - } - - if(raw) - { - raw->expandToPowerOfTwo(); - mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE); - mThumbnailUpToDate = TRUE ; - } - - //unlock updating - mThumbnailUpdateLock = FALSE ; -} - - -// Called often. Checks whether it's time to grab a new snapshot and if so, does it. -// Returns TRUE if new snapshot generated, FALSE otherwise. -//static -BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) -{ - LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview; - if (previewp->getWidth() == 0 || previewp->getHeight() == 0) - { - llwarns << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << llendl; - return FALSE; - } - - // If we're in freeze-frame mode and camera has moved, update snapshot. - LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin(); - LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion(); - if (gSavedSettings.getBOOL("FreezeTime") && - (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f)) - { - previewp->mCameraPos = new_camera_pos; - previewp->mCameraRot = new_camera_rot; - // request a new snapshot whenever the camera moves, with a time delay - BOOL autosnap = gSavedSettings.getBOOL("AutoSnapshot"); - lldebugs << "camera moved, updating thumbnail" << llendl; - previewp->updateSnapshot( - autosnap, // whether a new snapshot is needed or merely invalidate the existing one - FALSE, // or if 1st arg is false, whether to produce a new thumbnail image. - autosnap ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true. - } - - // see if it's time yet to snap the shot and bomb out otherwise. - previewp->mSnapshotActive = - (previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired()) - && !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active - if ( ! previewp->mSnapshotActive) - { - return FALSE; - } - - // time to produce a snapshot - previewp->setThumbnailImageSize(); - - lldebugs << "producing snapshot" << llendl; - if (!previewp->mPreviewImage) - { - previewp->mPreviewImage = new LLImageRaw; - } - - if (!previewp->mPreviewImageEncoded) - { - previewp->mPreviewImageEncoded = new LLImageRaw; - } - - previewp->setVisible(FALSE); - previewp->setEnabled(FALSE); - - previewp->getWindow()->incBusyCount(); - previewp->setImageScaled(FALSE); - - // grab the raw image and encode it into desired format - if(gViewerWindow->rawSnapshot( - previewp->mPreviewImage, - previewp->getWidth(), - previewp->getHeight(), - previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"), - previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE, - gSavedSettings.getBOOL("RenderUIInSnapshot"), - FALSE, - previewp->mSnapshotBufferType, - previewp->getMaxImageSize())) - { - previewp->mPreviewImageEncoded->resize( - previewp->mPreviewImage->getWidth(), - previewp->mPreviewImage->getHeight(), - previewp->mPreviewImage->getComponents()); - - if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE) - { - lldebugs << "Encoding new image of format J2C" << llendl; - LLPointer<LLImageJ2C> formatted = new LLImageJ2C; - LLPointer<LLImageRaw> scaled = new LLImageRaw( - previewp->mPreviewImage->getData(), - previewp->mPreviewImage->getWidth(), - previewp->mPreviewImage->getHeight(), - previewp->mPreviewImage->getComponents()); - - scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); - previewp->setImageScaled(TRUE); - if (formatted->encode(scaled, 0.f)) - { - previewp->mDataSize = formatted->getDataSize(); - formatted->decode(previewp->mPreviewImageEncoded, 0); - } - } - else - { - // delete any existing image - previewp->mFormattedImage = NULL; - // now create the new one of the appropriate format. - LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat(); - lldebugs << "Encoding new image of format " << format << llendl; - - switch(format) - { - case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG: - previewp->mFormattedImage = new LLImagePNG(); - break; - case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG: - previewp->mFormattedImage = new LLImageJPEG(previewp->mSnapshotQuality); - break; - case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP: - previewp->mFormattedImage = new LLImageBMP(); - break; - } - if (previewp->mFormattedImage->encode(previewp->mPreviewImage, 0)) - { - previewp->mDataSize = previewp->mFormattedImage->getDataSize(); - // special case BMP to copy instead of decode otherwise decode will crash. - if(format == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP) - { - previewp->mPreviewImageEncoded->copy(previewp->mPreviewImage); - } - else - { - previewp->mFormattedImage->decode(previewp->mPreviewImageEncoded, 0); - } - } - } - - LLPointer<LLImageRaw> scaled = new LLImageRaw( - previewp->mPreviewImageEncoded->getData(), - previewp->mPreviewImageEncoded->getWidth(), - previewp->mPreviewImageEncoded->getHeight(), - previewp->mPreviewImageEncoded->getComponents()); - - if(!scaled->isBufferInvalid()) - { - // leave original image dimensions, just scale up texture buffer - if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024) - { - // go ahead and shrink image to appropriate power of 2 for display - scaled->biasedScaleToPowerOfTwo(1024); - previewp->setImageScaled(TRUE); - } - else - { - // expand image but keep original image data intact - scaled->expandToPowerOfTwo(1024, FALSE); - } - - previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE); - LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex]; - gGL.getTexUnit(0)->bind(curr_preview_image); - if (previewp->getSnapshotType() != SNAPSHOT_TEXTURE) - { - curr_preview_image->setFilteringOption(LLTexUnit::TFO_POINT); - } - else - { - curr_preview_image->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC); - } - curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP); - - previewp->mSnapshotUpToDate = TRUE; - previewp->generateThumbnailImage(TRUE) ; - - previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal(); - previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame - } - } - previewp->getWindow()->decBusyCount(); - // only show fullscreen preview when in freeze frame mode - previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame")); - previewp->mSnapshotDelayTimer.stop(); - previewp->mSnapshotActive = FALSE; - - if(!previewp->getThumbnailUpToDate()) - { - previewp->generateThumbnailImage() ; - } - lldebugs << "done creating snapshot" << llendl; - LLFloaterSnapshot::postUpdate(); - - return TRUE; -} - -void LLSnapshotLivePreview::setSize(S32 w, S32 h) -{ - lldebugs << "setSize(" << w << ", " << h << ")" << llendl; - setWidth(w); - setHeight(h); -} - -void LLSnapshotLivePreview::getSize(S32& w, S32& h) const -{ - w = getWidth(); - h = getHeight(); -} - -void LLSnapshotLivePreview::saveTexture() -{ - lldebugs << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << llendl; - // gen a new uuid for this asset - LLTransactionID tid; - tid.generate(); - LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - - LLPointer<LLImageJ2C> formatted = new LLImageJ2C; - LLPointer<LLImageRaw> scaled = new LLImageRaw(mPreviewImage->getData(), - mPreviewImage->getWidth(), - mPreviewImage->getHeight(), - mPreviewImage->getComponents()); - - scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); - lldebugs << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << llendl; - - if (formatted->encode(scaled, 0.0f)) - { - LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE); - std::string pos_string; - LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); - std::string who_took_it; - LLAgentUI::buildFullname(who_took_it); - LLAssetStorage::LLStoreAssetCallback callback = NULL; - S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - void *userdata = NULL; - upload_new_resource(tid, // tid - LLAssetType::AT_TEXTURE, - "Snapshot : " + pos_string, - "Taken by " + who_took_it + " at " + pos_string, - 0, - LLFolderType::FT_SNAPSHOT_CATEGORY, - LLInventoryType::IT_SNAPSHOT, - PERM_ALL, // Note: Snapshots to inventory is a special case of content upload - LLFloaterPerms::getGroupPerms(), // that is more permissive than other uploads - LLFloaterPerms::getEveryonePerms(), - "Snapshot : " + pos_string, - callback, expected_upload_cost, userdata); - gViewerWindow->playSnapshotAnimAndSound(); - } - else - { - LLNotificationsUtil::add("ErrorEncodingSnapshot"); - llwarns << "Error encoding snapshot" << llendl; - } - - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_SNAPSHOT_COUNT ); - - mDataSize = 0; -} - -BOOL LLSnapshotLivePreview::saveLocal() -{ - BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage); - - if(success) - { - gViewerWindow->playSnapshotAnimAndSound(); - } - return success; -} - -void LLSnapshotLivePreview::saveWeb() -{ - // *FIX: Will break if the window closes because of CloseSnapshotOnKeep! - // Needs to pass on ownership of the image. - LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get()); - if(!jpg) - { - llwarns << "Formatted image not a JPEG" << llendl; - return; - } - - LLSD metadata; - metadata["description"] = getChild<LLLineEditor>("description")->getText(); - - LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(gAgentCamera.getCameraPositionGlobal(), - boost::bind(&LLSnapshotLivePreview::regionNameCallback, this, jpg, metadata, _1, _2, _3, _4)); - - gViewerWindow->playSnapshotAnimAndSound(); -} - -void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z) -{ - metadata["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString(); - - LLWebSharing::instance().shareSnapshot(snapshot, metadata); -} ///---------------------------------------------------------------------------- /// Class LLFloaterSnapshot::Impl @@ -2037,7 +1063,7 @@ BOOL LLFloaterSnapshot::postBuild() getChild<LLUICtrl>("auto_snapshot_check")->setValue(gSavedSettings.getBOOL("AutoSnapshot")); childSetCommitCallback("auto_snapshot_check", Impl::onClickAutoSnap, this); - + LLWebProfile::setImageUploadResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSnapshotUploadFinished, _1)); LLPostCard::setPostResultCallback(boost::bind(&LLFloaterSnapshot::Impl::onSendingPostcardFinished, _1)); @@ -2070,6 +1096,9 @@ BOOL LLFloaterSnapshot::postBuild() impl.updateControls(this); impl.updateLayout(this); + + previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect()); + return TRUE; } @@ -2235,7 +1264,9 @@ S32 LLFloaterSnapshot::notify(const LLSD& info) void LLFloaterSnapshot::update() { LLFloaterSnapshot* inst = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot"); - if (!inst) + LLFloaterSocial* floater_social = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social"); + + if (!inst && !floater_social) return; BOOL changed = FALSE; @@ -2245,7 +1276,8 @@ void LLFloaterSnapshot::update() { changed |= LLSnapshotLivePreview::onIdle(*iter); } - if(changed) + + if (inst && changed) { lldebugs << "changed" << llendl; inst->impl.updateControls(inst); diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h index afe135fa40..82af8c7a9d 100755 --- a/indra/newview/llfloatersnapshot.h +++ b/indra/newview/llfloatersnapshot.h @@ -27,7 +27,6 @@ #ifndef LL_LLFLOATERSNAPSHOT_H #define LL_LLFLOATERSNAPSHOT_H -#include "llimage.h" #include "llfloater.h" class LLSpinCtrl; diff --git a/indra/newview/llfloatersocial.cpp b/indra/newview/llfloatersocial.cpp new file mode 100644 index 0000000000..2a74c8e3ea --- /dev/null +++ b/indra/newview/llfloatersocial.cpp @@ -0,0 +1,920 @@ +/** +* @file llfloatersocial.cpp +* @brief Implementation of llfloatersocial +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatersocial.h" + +#include "llagent.h" +#include "llagentui.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llfacebookconnect.h" +#include "llfloaterreg.h" +#include "lliconctrl.h" +#include "llresmgr.h" // LLLocale +#include "llsdserialize.h" +#include "llloadingindicator.h" +#include "llplugincookiestore.h" +#include "llslurl.h" +#include "lltrans.h" +#include "llsnapshotlivepreview.h" +#include "llviewerregion.h" +#include "llviewercontrol.h" +#include "llviewermedia.h" + +static LLRegisterPanelClassWrapper<LLSocialStatusPanel> t_panel_status("llsocialstatuspanel"); +static LLRegisterPanelClassWrapper<LLSocialPhotoPanel> t_panel_photo("llsocialphotopanel"); +static LLRegisterPanelClassWrapper<LLSocialCheckinPanel> t_panel_checkin("llsocialcheckinpanel"); +static LLRegisterPanelClassWrapper<LLSocialAccountPanel> t_panel_account("llsocialaccountpanel"); + +const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte +const std::string DEFAULT_CHECKIN_LOCATION_URL = "http://maps.secondlife.com/"; +const std::string DEFAULT_CHECKIN_ICON_URL = "http://map.secondlife.com.s3.amazonaws.com/map_placeholder.png"; +const std::string DEFAULT_CHECKIN_QUERY_PARAMETERS = "?sourceid=slshare_checkin&utm_source=facebook&utm_medium=checkin&utm_campaign=slshare"; +const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=facebook&utm_medium=photo&utm_campaign=slshare"; + +std::string get_map_url() +{ + LLVector3d center_agent; + if (gAgent.getRegion()) + { + center_agent = gAgent.getRegion()->getCenterGlobal(); + } + int x_pos = center_agent[0] / 256.0; + int y_pos = center_agent[1] / 256.0; + std::string map_url = gSavedSettings.getString("CurrentMapServerURL") + llformat("map-1-%d-%d-objects.jpg", x_pos, y_pos); + return map_url; +} + +/////////////////////////// +//LLSocialStatusPanel////// +/////////////////////////// + +LLSocialStatusPanel::LLSocialStatusPanel() : + mMessageTextEditor(NULL), + mPostButton(NULL), + mCancelButton(NULL) +{ + mCommitCallbackRegistrar.add("SocialSharing.SendStatus", boost::bind(&LLSocialStatusPanel::onSend, this)); +} + +BOOL LLSocialStatusPanel::postBuild() +{ + mMessageTextEditor = getChild<LLUICtrl>("status_message"); + mPostButton = getChild<LLUICtrl>("post_status_btn"); + mCancelButton = getChild<LLUICtrl>("cancel_status_btn"); + + return LLPanel::postBuild(); +} + +void LLSocialStatusPanel::draw() +{ + if (mMessageTextEditor && mPostButton && mCancelButton) + { + bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing()); + std::string message = mMessageTextEditor->getValue().asString(); + mMessageTextEditor->setEnabled(no_ongoing_connection); + mCancelButton->setEnabled(no_ongoing_connection); + mPostButton->setEnabled(no_ongoing_connection && !message.empty()); + } + + LLPanel::draw(); +} + +void LLSocialStatusPanel::onSend() +{ + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialStatusPanel"); // just in case it is already listening + LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialStatusPanel", boost::bind(&LLSocialStatusPanel::onFacebookConnectStateChange, this, _1)); + + // Connect to Facebook if necessary and then post + if (LLFacebookConnect::instance().isConnected()) + { + sendStatus(); + } + else + { + LLFacebookConnect::instance().checkConnectionToFacebook(true); + } +} + +bool LLSocialStatusPanel::onFacebookConnectStateChange(const LLSD& data) +{ + switch (data.get("enum").asInteger()) + { + case LLFacebookConnect::FB_CONNECTED: + sendStatus(); + break; + + case LLFacebookConnect::FB_POSTED: + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialStatusPanel"); + clearAndClose(); + break; + } + + return false; +} + +void LLSocialStatusPanel::sendStatus() +{ + std::string message = mMessageTextEditor->getValue().asString(); + if (!message.empty()) + { + LLFacebookConnect::instance().updateStatus(message); + } +} + +void LLSocialStatusPanel::clearAndClose() +{ + mMessageTextEditor->setValue(""); + + LLFloater* floater = getParentByType<LLFloater>(); + if (floater) + { + floater->closeFloater(); + } +} + +/////////////////////////// +//LLSocialPhotoPanel/////// +/////////////////////////// + +LLSocialPhotoPanel::LLSocialPhotoPanel() : +mSnapshotPanel(NULL), +mResolutionComboBox(NULL), +mRefreshBtn(NULL), +mWorkingLabel(NULL), +mThumbnailPlaceholder(NULL), +mCaptionTextBox(NULL), +mLocationCheckbox(NULL), +mPostButton(NULL) +{ + mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLSocialPhotoPanel::onSend, this)); + mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLSocialPhotoPanel::onClickNewSnapshot, this)); +} + +LLSocialPhotoPanel::~LLSocialPhotoPanel() +{ + if(mPreviewHandle.get()) + { + mPreviewHandle.get()->die(); + } +} + +BOOL LLSocialPhotoPanel::postBuild() +{ + setVisibleCallback(boost::bind(&LLSocialPhotoPanel::onVisibilityChange, this, _2)); + + mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel"); + mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox"); + mResolutionComboBox->setCommitCallback(boost::bind(&LLSocialPhotoPanel::updateResolution, this, TRUE)); + mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn"); + mWorkingLabel = getChild<LLUICtrl>("working_lbl"); + mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder"); + mCaptionTextBox = getChild<LLUICtrl>("photo_caption"); + mLocationCheckbox = getChild<LLUICtrl>("add_location_cb"); + mPostButton = getChild<LLUICtrl>("post_photo_btn"); + mCancelButton = getChild<LLUICtrl>("cancel_photo_btn"); + + return LLPanel::postBuild(); +} + +void LLSocialPhotoPanel::draw() +{ + LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get()); + + // Enable interaction only if no transaction with the service is on-going (prevent duplicated posts) + bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing()); + mCancelButton->setEnabled(no_ongoing_connection); + mCaptionTextBox->setEnabled(no_ongoing_connection); + mResolutionComboBox->setEnabled(no_ongoing_connection); + mRefreshBtn->setEnabled(no_ongoing_connection); + mLocationCheckbox->setEnabled(no_ongoing_connection); + + // Display the preview if one is available + if (previewp && previewp->getThumbnailImage()) + { + const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect(); + const S32 thumbnail_w = previewp->getThumbnailWidth(); + const S32 thumbnail_h = previewp->getThumbnailHeight(); + + // calc preview offset within the preview rect + const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2 ; + const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2 ; + + // calc preview offset within the floater rect + // Hack : To get the full offset, we need to take into account each and every offset of each widgets up to the floater. + // This is almost as arbitrary as using a fixed offset so that's what we do here for the sake of simplicity. + // *TODO : Get the offset looking through the hierarchy of widgets, should be done in postBuild() so to avoid traversing the hierarchy each time. + S32 offset_x = thumbnail_rect.mLeft + local_offset_x - 1; + S32 offset_y = thumbnail_rect.mBottom + local_offset_y - 39; + + mSnapshotPanel->localPointToOtherView(offset_x, offset_y, &offset_x, &offset_y, getParentByType<LLFloater>()); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + // Apply floater transparency to the texture unless the floater is focused. + F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + LLColor4 color = LLColor4::white; + gl_draw_scaled_image(offset_x, offset_y, + thumbnail_w, thumbnail_h, + previewp->getThumbnailImage(), color % alpha); + + previewp->drawPreviewRect(offset_x, offset_y) ; + } + + // Update the visibility of the working (computing preview) label + mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate())); + + // Enable Post if we have a preview to send and no on going connection being processed + mPostButton->setEnabled(no_ongoing_connection && (previewp && previewp->getSnapshotUpToDate())); + + // Draw the rest of the panel on top of it + LLPanel::draw(); +} + +LLSnapshotLivePreview* LLSocialPhotoPanel::getPreviewView() +{ + LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get(); + return previewp; +} + +void LLSocialPhotoPanel::onVisibilityChange(const LLSD& new_visibility) +{ + bool visible = new_visibility.asBoolean(); + if (visible) + { + if (mPreviewHandle.get()) + { + LLSnapshotLivePreview* preview = getPreviewView(); + if(preview) + { + lldebugs << "opened, updating snapshot" << llendl; + preview->updateSnapshot(TRUE); + } + } + else + { + LLRect full_screen_rect = getRootView()->getRect(); + LLSnapshotLivePreview::Params p; + p.rect(full_screen_rect); + LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); + mPreviewHandle = previewp->getHandle(); + + previewp->setSnapshotType(previewp->SNAPSHOT_WEB); + previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG); + //previewp->setSnapshotQuality(98); + previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect()); + + updateControls(); + } + } +} + +void LLSocialPhotoPanel::onClickNewSnapshot() +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + //setStatus(Impl::STATUS_READY); + lldebugs << "updating snapshot" << llendl; + previewp->updateSnapshot(TRUE); + } +} + +void LLSocialPhotoPanel::onSend() +{ + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialPhotoPanel"); // just in case it is already listening + LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialPhotoPanel", boost::bind(&LLSocialPhotoPanel::onFacebookConnectStateChange, this, _1)); + + // Connect to Facebook if necessary and then post + if (LLFacebookConnect::instance().isConnected()) + { + sendPhoto(); + } + else + { + LLFacebookConnect::instance().checkConnectionToFacebook(true); + } +} + +bool LLSocialPhotoPanel::onFacebookConnectStateChange(const LLSD& data) +{ + switch (data.get("enum").asInteger()) + { + case LLFacebookConnect::FB_CONNECTED: + sendPhoto(); + break; + + case LLFacebookConnect::FB_POSTED: + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialPhotoPanel"); + clearAndClose(); + break; + } + + return false; +} + +void LLSocialPhotoPanel::sendPhoto() +{ + // Get the caption + std::string caption = mCaptionTextBox->getValue().asString(); + + // Add the location if required + bool add_location = mLocationCheckbox->getValue().asBoolean(); + if (add_location) + { + // Get the SLURL for the location + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + std::string slurl_string = slurl.getSLURLString(); + + // Add query parameters so Google Analytics can track incoming clicks! + slurl_string += DEFAULT_PHOTO_QUERY_PARAMETERS; + + // Add it to the caption (pretty crude, but we don't have a better option with photos) + if (caption.empty()) + caption = slurl_string; + else + caption = caption + " " + slurl_string; + } + + // Get the image + LLSnapshotLivePreview* previewp = getPreviewView(); + + // Post to Facebook + LLFacebookConnect::instance().sharePhoto(previewp->getFormattedImage(), caption); + + updateControls(); +} + +void LLSocialPhotoPanel::clearAndClose() +{ + mCaptionTextBox->setValue(""); + + LLFloater* floater = getParentByType<LLFloater>(); + if (floater) + { + floater->closeFloater(); + } +} + +void LLSocialPhotoPanel::updateControls() +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + BOOL got_bytes = previewp && previewp->getDataSize() > 0; + BOOL got_snap = previewp && previewp->getSnapshotUpToDate(); + LLSnapshotLivePreview::ESnapshotType shot_type = (previewp ? previewp->getSnapshotType() : LLSnapshotLivePreview::SNAPSHOT_POSTCARD); + + // *TODO: Separate maximum size for Web images from postcards + lldebugs << "Is snapshot up-to-date? " << got_snap << llendl; + + LLLocale locale(LLLocale::USER_LOCALE); + std::string bytes_string; + if (got_snap) + { + LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10 ); + } + + //getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : getString("unknown")); <---uses localized string + getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : "unknown"); + getChild<LLUICtrl>("file_size_label")->setColor( + shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD + && got_bytes + && previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLUIColor(LLColor4::red) : LLUIColorTable::instance().getColor( "LabelTextColor" )); + + updateResolution(FALSE); +} + +void LLSocialPhotoPanel::updateResolution(BOOL do_update) +{ + LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox); + + std::string sdstring = combobox->getSelectedValue(); + LLSD sdres; + std::stringstream sstream(sdstring); + LLSDSerialize::fromNotation(sdres, sstream, sdstring.size()); + + S32 width = sdres[0]; + S32 height = sdres[1]; + + LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get()); + if (previewp && combobox->getCurrentIndex() >= 0) + { + S32 original_width = 0 , original_height = 0 ; + previewp->getSize(original_width, original_height) ; + + if (width == 0 || height == 0) + { + // take resolution from current window size + lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl; + previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw()); + } + else + { + // use the resolution from the selected pre-canned drop-down choice + lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl; + previewp->setSize(width, height); + } + + checkAspectRatio(width); + + previewp->getSize(width, height); + + if(original_width != width || original_height != height) + { + previewp->setSize(width, height); + + // hide old preview as the aspect ratio could be wrong + lldebugs << "updating thumbnail" << llendl; + + previewp->updateSnapshot(FALSE, TRUE); + if(do_update) + { + lldebugs << "Will update controls" << llendl; + updateControls(); + LLSocialPhotoPanel::onClickNewSnapshot(); + } + } + + } +} + +void LLSocialPhotoPanel::checkAspectRatio(S32 index) +{ + LLSnapshotLivePreview *previewp = getPreviewView() ; + + BOOL keep_aspect = FALSE; + + if (0 == index) // current window size + { + keep_aspect = TRUE; + } + else // predefined resolution + { + keep_aspect = FALSE; + } + + if (previewp) + { + previewp->mKeepAspectRatio = keep_aspect; + } +} + +LLUICtrl* LLSocialPhotoPanel::getRefreshBtn() +{ + return mRefreshBtn; +} + +//////////////////////// +//LLSocialCheckinPanel// +//////////////////////// + +LLSocialCheckinPanel::LLSocialCheckinPanel() : + mMapUrl(""), + mReloadingMapTexture(false) +{ + mCommitCallbackRegistrar.add("SocialSharing.SendCheckin", boost::bind(&LLSocialCheckinPanel::onSend, this)); +} + +BOOL LLSocialCheckinPanel::postBuild() +{ + // Keep pointers to widgets so we don't traverse the UI hierarchy too often + mPostButton = getChild<LLUICtrl>("post_place_btn"); + mCancelButton = getChild<LLUICtrl>("cancel_place_btn"); + mMessageTextEditor = getChild<LLUICtrl>("place_caption"); + mMapLoadingIndicator = getChild<LLUICtrl>("map_loading_indicator"); + mMapPlaceholder = getChild<LLIconCtrl>("map_placeholder"); + mMapDefault = getChild<LLIconCtrl>("map_default"); + mMapCheckBox = getChild<LLCheckBoxCtrl>("add_place_view_cb"); + + return LLPanel::postBuild(); +} + +void LLSocialCheckinPanel::draw() +{ + bool no_ongoing_connection = !(LLFacebookConnect::instance().isTransactionOngoing()); + mPostButton->setEnabled(no_ongoing_connection); + mCancelButton->setEnabled(no_ongoing_connection); + mMessageTextEditor->setEnabled(no_ongoing_connection); + mMapCheckBox->setEnabled(no_ongoing_connection); + + std::string map_url = get_map_url(); + // Did we change location? + if (map_url != mMapUrl) + { + mMapUrl = map_url; + // Load the map tile + mMapTexture = LLViewerTextureManager::getFetchedTextureFromUrl(mMapUrl, FTT_MAP_TILE, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + mMapTexture->setBoostLevel(LLGLTexture::BOOST_MAP); + mReloadingMapTexture = true; + // In the meantime, put the "loading" indicator on, hide the tile map and disable the checkbox + mMapLoadingIndicator->setVisible(true); + mMapPlaceholder->setVisible(false); + } + // Are we done loading the map tile? + if (mReloadingMapTexture && mMapTexture->isFullyLoaded()) + { + // Don't do it again next time around + mReloadingMapTexture = false; + // Convert the map texture to the appropriate image object + LLPointer<LLUIImage> ui_image = new LLUIImage(mMapUrl, mMapTexture); + // Load the map widget with the correct map tile image + mMapPlaceholder->setImage(ui_image); + // Now hide the loading indicator, bring the tile in view and reenable the checkbox with its previous value + mMapLoadingIndicator->setVisible(false); + mMapPlaceholder->setVisible(true); + } + // Show the default icon if that's the checkbox value (the real one...) + // This will hide/show the loading indicator and/or tile underneath + mMapDefault->setVisible(!(mMapCheckBox->get())); + + LLPanel::draw(); +} + +void LLSocialCheckinPanel::onSend() +{ + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialCheckinPanel"); // just in case it is already listening + LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialCheckinPanel", boost::bind(&LLSocialCheckinPanel::onFacebookConnectStateChange, this, _1)); + + // Connect to Facebook if necessary and then post + if (LLFacebookConnect::instance().isConnected()) + { + sendCheckin(); + } + else + { + LLFacebookConnect::instance().checkConnectionToFacebook(true); + } +} + +bool LLSocialCheckinPanel::onFacebookConnectStateChange(const LLSD& data) +{ + switch (data.get("enum").asInteger()) + { + case LLFacebookConnect::FB_CONNECTED: + sendCheckin(); + break; + + case LLFacebookConnect::FB_POSTED: + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialCheckinPanel"); + clearAndClose(); + break; + } + + return false; +} + +void LLSocialCheckinPanel::sendCheckin() +{ + // Get the location SLURL + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + std::string slurl_string = slurl.getSLURLString(); + + // Use a valid http:// URL if the scheme is secondlife:// + LLURI slurl_uri(slurl_string); + if (slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME) + { + slurl_string = DEFAULT_CHECKIN_LOCATION_URL; + } + + // Add query parameters so Google Analytics can track incoming clicks! + slurl_string += DEFAULT_CHECKIN_QUERY_PARAMETERS; + + // Get the region name + std::string region_name = gAgent.getRegion()->getName(); + + // Get the region description + std::string description; + LLAgentUI::buildLocationString(description, LLAgentUI::LOCATION_FORMAT_NORMAL_COORDS, gAgent.getPositionAgent()); + + // Optionally add the region map view + bool add_map_view = mMapCheckBox->getValue().asBoolean(); + std::string map_url = (add_map_view ? get_map_url() : DEFAULT_CHECKIN_ICON_URL); + + // Get the caption + std::string caption = mMessageTextEditor->getValue().asString(); + + // Post to Facebook + LLFacebookConnect::instance().postCheckin(slurl_string, region_name, description, map_url, caption); +} + +void LLSocialCheckinPanel::clearAndClose() +{ + mMessageTextEditor->setValue(""); + + LLFloater* floater = getParentByType<LLFloater>(); + if (floater) + { + floater->closeFloater(); + } +} + +/////////////////////////// +//LLSocialAccountPanel////// +/////////////////////////// + +LLSocialAccountPanel::LLSocialAccountPanel() : +mAccountCaptionLabel(NULL), +mAccountNameLabel(NULL), +mPanelButtons(NULL), +mConnectButton(NULL), +mDisconnectButton(NULL) +{ + mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLSocialAccountPanel::onConnect, this)); + mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLSocialAccountPanel::onDisconnect, this)); + + setVisibleCallback(boost::bind(&LLSocialAccountPanel::onVisibilityChange, this, _2)); +} + +BOOL LLSocialAccountPanel::postBuild() +{ + mAccountCaptionLabel = getChild<LLTextBox>("account_caption_label"); + mAccountNameLabel = getChild<LLTextBox>("account_name_label"); + mPanelButtons = getChild<LLUICtrl>("panel_buttons"); + mConnectButton = getChild<LLUICtrl>("connect_btn"); + mDisconnectButton = getChild<LLUICtrl>("disconnect_btn"); + + return LLPanel::postBuild(); +} + +void LLSocialAccountPanel::draw() +{ + LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState(); + + //Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress + bool disconnecting = connection_state == LLFacebookConnect::FB_DISCONNECTING; + mDisconnectButton->setEnabled(!disconnecting); + + //Disable the 'connect' button when a connection is in progress + bool connecting = connection_state == LLFacebookConnect::FB_CONNECTION_IN_PROGRESS; + mConnectButton->setEnabled(!connecting); + + LLPanel::draw(); +} + +void LLSocialAccountPanel::onVisibilityChange(const LLSD& new_visibility) +{ + bool visible = new_visibility.asBoolean(); + + if(visible) + { + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialAccountPanel"); + LLEventPumps::instance().obtain("FacebookConnectState").listen("LLSocialAccountPanel", boost::bind(&LLSocialAccountPanel::onFacebookConnectStateChange, this, _1)); + + LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLSocialAccountPanel"); + LLEventPumps::instance().obtain("FacebookConnectInfo").listen("LLSocialAccountPanel", boost::bind(&LLSocialAccountPanel::onFacebookConnectInfoChange, this)); + + //Connected + if(LLFacebookConnect::instance().isConnected()) + { + showConnectedLayout(); + } + //Check if connected (show disconnected layout in meantime) + else + { + showDisconnectedLayout(); + } + if ((LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_NOT_CONNECTED) || + (LLFacebookConnect::instance().getConnectionState() == LLFacebookConnect::FB_CONNECTION_FAILED)) + { + LLFacebookConnect::instance().checkConnectionToFacebook(); + } + } + else + { + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLSocialAccountPanel"); + LLEventPumps::instance().obtain("FacebookConnectInfo").stopListening("LLSocialAccountPanel"); + } +} + +bool LLSocialAccountPanel::onFacebookConnectStateChange(const LLSD& data) +{ + if(LLFacebookConnect::instance().isConnected()) + { + //In process of disconnecting so leave the layout as is + if(data.get("enum").asInteger() != LLFacebookConnect::FB_DISCONNECTING) + { + showConnectedLayout(); + } + } + else + { + showDisconnectedLayout(); + } + + return false; +} + +bool LLSocialAccountPanel::onFacebookConnectInfoChange() +{ + LLSD info = LLFacebookConnect::instance().getInfo(); + std::string clickable_name; + + //Strings of format [http://www.somewebsite.com Click Me] become clickable text + if(info.has("link") && info.has("name")) + { + clickable_name = "[" + info["link"].asString() + " " + info["name"].asString() + "]"; + } + + mAccountNameLabel->setText(clickable_name); + + return false; +} + +void LLSocialAccountPanel::showConnectButton() +{ + if(!mConnectButton->getVisible()) + { + mConnectButton->setVisible(TRUE); + mDisconnectButton->setVisible(FALSE); + } +} + +void LLSocialAccountPanel::hideConnectButton() +{ + if(mConnectButton->getVisible()) + { + mConnectButton->setVisible(FALSE); + mDisconnectButton->setVisible(TRUE); + } +} + +void LLSocialAccountPanel::showDisconnectedLayout() +{ + mAccountCaptionLabel->setText(getString("facebook_disconnected")); + mAccountNameLabel->setText(std::string("")); + showConnectButton(); +} + +void LLSocialAccountPanel::showConnectedLayout() +{ + LLFacebookConnect::instance().loadFacebookInfo(); + + mAccountCaptionLabel->setText(getString("facebook_connected")); + hideConnectButton(); +} + +void LLSocialAccountPanel::onConnect() +{ + LLFacebookConnect::instance().checkConnectionToFacebook(true); + + //Clear only the facebook browser cookies so that the facebook login screen appears + LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com"); +} + +void LLSocialAccountPanel::onDisconnect() +{ + LLFacebookConnect::instance().disconnectFromFacebook(); + + LLViewerMedia::getCookieStore()->removeCookiesByDomain(".facebook.com"); +} + +//////////////////////// +//LLFloaterSocial/////// +//////////////////////// + +LLFloaterSocial::LLFloaterSocial(const LLSD& key) : LLFloater(key), + mSocialPhotoPanel(NULL), + mStatusErrorText(NULL), + mStatusLoadingText(NULL), + mStatusLoadingIndicator(NULL) +{ + mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterSocial::onCancel, this)); +} + +void LLFloaterSocial::onCancel() +{ + closeFloater(); +} + +BOOL LLFloaterSocial::postBuild() +{ + // Keep tab of the Photo Panel + mSocialPhotoPanel = static_cast<LLSocialPhotoPanel*>(getChild<LLUICtrl>("panel_social_photo")); + // Connection status widgets + mStatusErrorText = getChild<LLTextBox>("connection_error_text"); + mStatusLoadingText = getChild<LLTextBox>("connection_loading_text"); + mStatusLoadingIndicator = getChild<LLUICtrl>("connection_loading_indicator"); + return LLFloater::postBuild(); +} + +// static +void LLFloaterSocial::preUpdate() +{ + LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social"); + if (instance) + { + //Will set file size text to 'unknown' + instance->mSocialPhotoPanel->updateControls(); + } +} + +// static +void LLFloaterSocial::postUpdate() +{ + LLFloaterSocial* instance = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social"); + if (instance) + { + //Will set the file size text + instance->mSocialPhotoPanel->updateControls(); + + // The refresh button is initially hidden. We show it after the first update, + // i.e. after snapshot is taken + LLUICtrl * refresh_button = instance->mSocialPhotoPanel->getRefreshBtn(); + + if (!refresh_button->getVisible()) + { + refresh_button->setVisible(true); + } + + } +} + +void LLFloaterSocial::draw() +{ + if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator) + { + mStatusErrorText->setVisible(false); + mStatusLoadingText->setVisible(false); + mStatusLoadingIndicator->setVisible(false); + LLFacebookConnect::EConnectionState connection_state = LLFacebookConnect::instance().getConnectionState(); + std::string status_text; + + switch (connection_state) + { + case LLFacebookConnect::FB_NOT_CONNECTED: + // No status displayed when first opening the panel and no connection done + case LLFacebookConnect::FB_CONNECTED: + // When successfully connected, no message is displayed + case LLFacebookConnect::FB_POSTED: + // No success message to show since we actually close the floater after successful posting completion + break; + case LLFacebookConnect::FB_CONNECTION_IN_PROGRESS: + // Connection loading indicator + mStatusLoadingText->setVisible(true); + status_text = LLTrans::getString("SocialFacebookConnecting"); + mStatusLoadingText->setValue(status_text); + mStatusLoadingIndicator->setVisible(true); + break; + case LLFacebookConnect::FB_POSTING: + // Posting indicator + mStatusLoadingText->setVisible(true); + status_text = LLTrans::getString("SocialFacebookPosting"); + mStatusLoadingText->setValue(status_text); + mStatusLoadingIndicator->setVisible(true); + break; + case LLFacebookConnect::FB_CONNECTION_FAILED: + // Error connecting to the service + mStatusErrorText->setVisible(true); + status_text = LLTrans::getString("SocialFacebookErrorConnecting"); + mStatusErrorText->setValue(status_text); + break; + case LLFacebookConnect::FB_POST_FAILED: + // Error posting to the service + mStatusErrorText->setVisible(true); + status_text = LLTrans::getString("SocialFacebookErrorPosting"); + mStatusErrorText->setValue(status_text); + break; + case LLFacebookConnect::FB_DISCONNECTING: + // Disconnecting loading indicator + mStatusLoadingText->setVisible(true); + status_text = LLTrans::getString("SocialFacebookDisconnecting"); + mStatusLoadingText->setValue(status_text); + mStatusLoadingIndicator->setVisible(true); + break; + case LLFacebookConnect::FB_DISCONNECT_FAILED: + // Error disconnecting from the service + mStatusErrorText->setVisible(true); + status_text = LLTrans::getString("SocialFacebookErrorDisconnecting"); + mStatusErrorText->setValue(status_text); + break; + } + } + LLFloater::draw(); +} + diff --git a/indra/newview/llfloatersocial.h b/indra/newview/llfloatersocial.h new file mode 100644 index 0000000000..bbe07c9704 --- /dev/null +++ b/indra/newview/llfloatersocial.h @@ -0,0 +1,165 @@ +/** +* @file llfloatersocial.h +* @brief Header file for llfloatersocial +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLFLOATERSOCIAL_H +#define LL_LLFLOATERSOCIAL_H + +#include "llfloater.h" +#include "lltextbox.h" +#include "llviewertexture.h" + +class LLIconCtrl; +class LLCheckBoxCtrl; +class LLSnapshotLivePreview; + +class LLSocialStatusPanel : public LLPanel +{ +public: + LLSocialStatusPanel(); + BOOL postBuild(); + void draw(); + void onSend(); + bool onFacebookConnectStateChange(const LLSD& data); + + void sendStatus(); + void clearAndClose(); + +private: + LLUICtrl* mMessageTextEditor; + LLUICtrl* mPostButton; + LLUICtrl* mCancelButton; +}; + +class LLSocialPhotoPanel : public LLPanel +{ +public: + LLSocialPhotoPanel(); + ~LLSocialPhotoPanel(); + + BOOL postBuild(); + void draw(); + + LLSnapshotLivePreview* getPreviewView(); + void onVisibilityChange(const LLSD& new_visibility); + void onClickNewSnapshot(); + void onSend(); + bool onFacebookConnectStateChange(const LLSD& data); + + void sendPhoto(); + void clearAndClose(); + + void updateControls(); + void updateResolution(BOOL do_update); + void checkAspectRatio(S32 index); + LLUICtrl* getRefreshBtn(); + +private: + LLHandle<LLView> mPreviewHandle; + + LLUICtrl * mSnapshotPanel; + LLUICtrl * mResolutionComboBox; + LLUICtrl * mRefreshBtn; + LLUICtrl * mWorkingLabel; + LLUICtrl * mThumbnailPlaceholder; + LLUICtrl * mCaptionTextBox; + LLUICtrl * mLocationCheckbox; + LLUICtrl * mPostButton; + LLUICtrl* mCancelButton; +}; + +class LLSocialCheckinPanel : public LLPanel +{ +public: + LLSocialCheckinPanel(); + BOOL postBuild(); + void draw(); + void onSend(); + bool onFacebookConnectStateChange(const LLSD& data); + + void sendCheckin(); + void clearAndClose(); + +private: + std::string mMapUrl; + LLPointer<LLViewerFetchedTexture> mMapTexture; + LLUICtrl* mPostButton; + LLUICtrl* mCancelButton; + LLUICtrl* mMessageTextEditor; + LLUICtrl* mMapLoadingIndicator; + LLIconCtrl* mMapPlaceholder; + LLIconCtrl* mMapDefault; + LLCheckBoxCtrl* mMapCheckBox; + bool mReloadingMapTexture; +}; + +class LLSocialAccountPanel : public LLPanel +{ +public: + LLSocialAccountPanel(); + BOOL postBuild(); + void draw(); + +private: + void onVisibilityChange(const LLSD& new_visibility); + bool onFacebookConnectStateChange(const LLSD& data); + bool onFacebookConnectInfoChange(); + void onConnect(); + void onUseAnotherAccount(); + void onDisconnect(); + + void showConnectButton(); + void hideConnectButton(); + void showDisconnectedLayout(); + void showConnectedLayout(); + + LLTextBox * mAccountCaptionLabel; + LLTextBox * mAccountNameLabel; + LLUICtrl * mPanelButtons; + LLUICtrl * mConnectButton; + LLUICtrl * mDisconnectButton; +}; + + +class LLFloaterSocial : public LLFloater +{ +public: + LLFloaterSocial(const LLSD& key); + BOOL postBuild(); + void draw(); + void onCancel(); + + static void preUpdate(); + static void postUpdate(); + +private: + LLSocialPhotoPanel* mSocialPhotoPanel; + LLTextBox* mStatusErrorText; + LLTextBox* mStatusLoadingText; + LLUICtrl* mStatusLoadingIndicator; +}; + +#endif // LL_LLFLOATERSOCIAL_H + diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp index 3fe2518de6..5c569b9bf0 100755 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -29,6 +29,7 @@ #include "llcombobox.h" #include "lliconctrl.h" #include "llfloaterreg.h" +#include "llfacebookconnect.h" #include "lllayoutstack.h" #include "llpluginclassmedia.h" #include "llprogressbar.h" @@ -46,7 +47,8 @@ LLFloaterWebContent::_Params::_Params() id("id"), window_class("window_class", "web_content"), show_chrome("show_chrome", true), - allow_address_entry("allow_address_entry", true), + allow_address_entry("allow_address_entry", true), + allow_back_forward_navigation("allow_back_forward_navigation", true), preferred_media_size("preferred_media_size"), trusted_content("trusted_content", false), show_page_title("show_page_title", true) @@ -65,7 +67,11 @@ LLFloaterWebContent::LLFloaterWebContent( const Params& params ) mBtnReload(NULL), mBtnStop(NULL), mUUID(params.id()), - mShowPageTitle(params.show_page_title) + mShowPageTitle(params.show_page_title), + mAllowNavigation(true), + mCurrentURL(""), + mDisplayURL(""), + mSecureURL(false) { mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this )); mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this )); @@ -97,7 +103,7 @@ BOOL LLFloaterWebContent::postBuild() // cache image for secure browsing mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag"); - + // initialize the URL history using the system URL History manager initializeURLHistory(); @@ -243,14 +249,10 @@ void LLFloaterWebContent::open_media(const Params& p) getChild<LLLayoutPanel>("status_bar")->setVisible(p.show_chrome); getChild<LLLayoutPanel>("nav_controls")->setVisible(p.show_chrome); bool address_entry_enabled = p.allow_address_entry && !p.trusted_content; + mAllowNavigation = p.allow_back_forward_navigation; getChildView("address")->setEnabled(address_entry_enabled); getChildView("popexternal")->setEnabled(address_entry_enabled); - if (!address_entry_enabled) - { - mWebBrowser->setFocus(TRUE); - } - if (!p.show_chrome) { setResizeLimits(100, 100); @@ -287,6 +289,16 @@ void LLFloaterWebContent::onOpen(const LLSD& key) //virtual void LLFloaterWebContent::onClose(bool app_quitting) { + // If we close the web browsing window showing the facebook login, we need to signal to this object that the connection will not happen + LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web"); + if (fbc_web == this) + { + if (!LLFacebookConnect::instance().isConnected()) + { + LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED); + } + } + LLViewerMedia::proxyWindowClosed(mUUID); destroy(); } @@ -295,8 +307,11 @@ void LLFloaterWebContent::onClose(bool app_quitting) void LLFloaterWebContent::draw() { // this is asynchronous so we need to keep checking - mBtnBack->setEnabled( mWebBrowser->canNavigateBack() ); - mBtnForward->setEnabled( mWebBrowser->canNavigateForward() ); + mBtnBack->setEnabled( mWebBrowser->canNavigateBack() && mAllowNavigation); + mBtnForward->setEnabled( mWebBrowser->canNavigateForward() && mAllowNavigation); + + // Show/hide the lock icon + mSecureLockIcon->setVisible(mSecureURL && !mAddressCombo->hasFocus()); LLFloater::draw(); } @@ -342,19 +357,6 @@ void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent // we populate the status bar with URLs as they change so clear it now we're done const std::string end_str = ""; mStatusBarText->setText( end_str ); - - // decide if secure browsing icon should be displayed - std::string prefix = std::string("https://"); - std::string test_prefix = mCurrentURL.substr(0, prefix.length()); - LLStringUtil::toLower(test_prefix); - if(test_prefix == prefix) - { - mSecureLockIcon->setVisible(true); - } - else - { - mSecureLockIcon->setVisible(false); - } } else if(event == MEDIA_EVENT_CLOSE_REQUEST) { @@ -397,15 +399,37 @@ void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent void LLFloaterWebContent::set_current_url(const std::string& url) { - mCurrentURL = url; - - // serialize url history into the system URL History manager - LLURLHistory::removeURL("browser", mCurrentURL); - LLURLHistory::addURL("browser", mCurrentURL); - - mAddressCombo->remove( mCurrentURL ); - mAddressCombo->add( mCurrentURL ); - mAddressCombo->selectByValue( mCurrentURL ); + if (!url.empty()) + { + if (!mCurrentURL.empty()) + { + // Clean up the current browsing list to show true URL + mAddressCombo->remove(mDisplayURL); + mAddressCombo->add(mCurrentURL); + } + + // Update current URL + mCurrentURL = url; + LLStringUtil::trim(mCurrentURL); + + // Serialize url history into the system URL History manager + LLURLHistory::removeURL("browser", mCurrentURL); + LLURLHistory::addURL("browser", mCurrentURL); + + // Check if this is a secure URL + static const std::string secure_prefix = std::string("https://"); + std::string prefix = mCurrentURL.substr(0, secure_prefix.length()); + LLStringUtil::toLower(prefix); + mSecureURL = (prefix == secure_prefix); + + // Hack : we move the text a bit to make space for the lock icon in the secure URL case + mDisplayURL = (mSecureURL ? " " + mCurrentURL : mCurrentURL); + + // Clean up browsing list (prevent dupes) and add/select the new URL to it + mAddressCombo->remove(mCurrentURL); + mAddressCombo->add(mDisplayURL); + mAddressCombo->selectByValue(mDisplayURL); + } } void LLFloaterWebContent::onClickForward() @@ -449,6 +473,7 @@ void LLFloaterWebContent::onEnterAddress() // make sure there is at least something there. // (perhaps this test should be for minimum length of a URL) std::string url = mAddressCombo->getValue().asString(); + LLStringUtil::trim(url); if ( url.length() > 0 ) { mWebBrowser->navigateTo( url, "text/html"); @@ -460,6 +485,7 @@ void LLFloaterWebContent::onPopExternal() // make sure there is at least something there. // (perhaps this test should be for minimum length of a URL) std::string url = mAddressCombo->getValue().asString(); + LLStringUtil::trim(url); if ( url.length() > 0 ) { LLWeb::loadURLExternal( url ); diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h index 86b5a5e00b..f22940cd07 100755 --- a/indra/newview/llfloaterwebcontent.h +++ b/indra/newview/llfloaterwebcontent.h @@ -55,6 +55,7 @@ public: id; Optional<bool> show_chrome, allow_address_entry, + allow_back_forward_navigation, trusted_content, show_page_title; Optional<LLRect> preferred_media_size; @@ -106,9 +107,12 @@ protected: LLView* mBtnReload; LLView* mBtnStop; - std::string mCurrentURL; + std::string mCurrentURL; // Current URL + std::string mDisplayURL; // URL being displayed in the address bar (can differ by trailing / leading space) std::string mUUID; bool mShowPageTitle; + bool mAllowNavigation; + bool mSecureURL; // true when the current url is prefixed "https://" }; #endif // LL_LLFLOATERWEBCONTENT_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 2e53effcac..9ffbd1a675 100755 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -130,10 +130,10 @@ void process_dnd_im(const LLSD& notification) fromID, false, false); //will need slight refactor to retrieve whether offline message or not (assume online for now) - } + } notify_of_message(data, true); -} + } @@ -155,22 +155,22 @@ static void on_avatar_name_cache_toast(const LLUUID& agent_id, void notify_of_message(const LLSD& msg, bool is_dnd_msg) { - std::string user_preferences; + std::string user_preferences; LLUUID participant_id = msg[is_dnd_msg ? "FROM_ID" : "from_id"].asUUID(); LLUUID session_id = msg[is_dnd_msg ? "SESSION_ID" : "session_id"].asUUID(); - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); - // do not show notification which goes from agent - if (gAgent.getID() == participant_id) - { - return; - } + // do not show notification which goes from agent + if (gAgent.getID() == participant_id) + { + return; + } - // determine state of conversations floater - enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status; + // determine state of conversations floater + enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status; - LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); + LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id); bool store_dnd_message = false; // flag storage of a dnd message bool is_session_focused = session_floater->isTornOff() && session_floater->hasFocus(); @@ -179,23 +179,23 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) conversations_floater_status = CLOSED; } else if (!im_box->hasFocus() && - !(session_floater && LLFloater::isVisible(session_floater) - && !session_floater->isMinimized() && session_floater->hasFocus())) + !(session_floater && LLFloater::isVisible(session_floater) + && !session_floater->isMinimized() && session_floater->hasFocus())) { conversations_floater_status = NOT_ON_TOP; } else if (im_box->getSelectedSession() != session_id) { conversations_floater_status = ON_TOP; - } + } else { conversations_floater_status = ON_TOP_AND_ITEM_IS_SELECTED; } - // determine user prefs for this session - if (session_id.isNull()) - { + // determine user prefs for this session + if (session_id.isNull()) + { if (msg["source_type"].asInteger() == CHAT_SOURCE_OBJECT) { user_preferences = gSavedSettings.getString("NotificationObjectIMOptions"); @@ -206,50 +206,50 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) } else { - user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); + user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNearbyChatIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); - } + } } } - else if(session->isP2PSessionType()) - { - if (LLAvatarTracker::instance().isBuddy(participant_id)) - { - user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); + else if(session->isP2PSessionType()) + { + if (LLAvatarTracker::instance().isBuddy(participant_id)) + { + user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundFriendIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); } - } - else - { - user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); + } + else + { + user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNonFriendIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); - } - } + } + } } - else if(session->isAdHocSessionType()) - { - user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); + else if(session->isAdHocSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundConferenceIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); - } + } } - else if(session->isGroupSessionType()) - { - user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); + else if(session->isGroupSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundGroupChatIM") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); } - } + } - // actions: + // actions: // 0. nothing - exit if (("noaction" == user_preferences || @@ -287,23 +287,23 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) } } } - else - { + else + { store_dnd_message = true; - } + } - } + } - // 2. Flash line item - if ("openconversations" == user_preferences - || ON_TOP == conversations_floater_status - || ("toast" == user_preferences && ON_TOP != conversations_floater_status) + // 2. Flash line item + if ("openconversations" == user_preferences + || ON_TOP == conversations_floater_status + || ("toast" == user_preferences && ON_TOP != conversations_floater_status) || ("flash" == user_preferences && (CLOSED == conversations_floater_status || NOT_ON_TOP == conversations_floater_status)) || is_dnd_msg) - { - if(!LLMuteList::getInstance()->isMuted(participant_id)) - { + { + if(!LLMuteList::getInstance()->isMuted(participant_id)) + { if(gAgent.isDoNotDisturb()) { store_dnd_message = true; @@ -318,43 +318,43 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) } else { - im_box->flashConversationItemWidget(session_id, true); - } - } + im_box->flashConversationItemWidget(session_id, true); + } + } } } - // 3. Flash FUI button - if (("toast" == user_preferences || "flash" == user_preferences) && - (CLOSED == conversations_floater_status + // 3. Flash FUI button + if (("toast" == user_preferences || "flash" == user_preferences) && + (CLOSED == conversations_floater_status || NOT_ON_TOP == conversations_floater_status) && !is_session_focused && !is_dnd_msg) //prevent flashing FUI button because the conversation floater will have already opened { if(!LLMuteList::getInstance()->isMuted(participant_id)) - { + { if(!gAgent.isDoNotDisturb()) - { + { gToolBarView->flashCommand(LLCommandId("chat"), true, im_box->isMinimized()); - } + } else { store_dnd_message = true; } - } + } } - // 4. Toast - if ((("toast" == user_preferences) && + // 4. Toast + if ((("toast" == user_preferences) && (ON_TOP_AND_ITEM_IS_SELECTED != conversations_floater_status) && (!session_floater->isTornOff() || !LLFloater::isVisible(session_floater))) - || !session_floater->isMessagePaneExpanded()) + || !session_floater->isMessagePaneExpanded()) - { - //Show IM toasts (upper right toasts) - // Skip toasting for system messages and for nearby chat - if(session_id.notNull() && participant_id.notNull()) - { + { + //Show IM toasts (upper right toasts) + // Skip toasting for system messages and for nearby chat + if(session_id.notNull() && participant_id.notNull()) + { if(!is_dnd_msg) { if(gAgent.isDoNotDisturb()) @@ -363,10 +363,10 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg) } else { - LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); - } - } - } + LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); + } + } +} } if (store_dnd_message) { diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp index 08c98e4f28..2854962922 100755 --- a/indra/newview/llnotificationscripthandler.cpp +++ b/indra/newview/llnotificationscripthandler.cpp @@ -35,6 +35,9 @@ #include "llnotificationmanager.h" #include "llnotifications.h" #include "llscriptfloater.h" +#include "llfacebookconnect.h" +#include "llavatarname.h" +#include "llavatarnamecache.h" using namespace LLNotificationsUI; diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index d7c634d619..f551fc96ee 100755 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -28,6 +28,8 @@ // libs #include "llavatarname.h" +#include "llconversationview.h" +#include "llfloaterimcontainer.h" #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llmenubutton.h" @@ -48,15 +50,19 @@ #include "llavataractions.h" #include "llavatarlist.h" #include "llavatarlistitem.h" +#include "llavatarnamecache.h" #include "llcallingcard.h" // for LLAvatarTracker +#include "llcallbacklist.h" +#include "llerror.h" +#include "llfacebookconnect.h" #include "llfloateravatarpicker.h" -//#include "llfloaterminiinspector.h" #include "llfriendcard.h" #include "llgroupactions.h" #include "llgrouplist.h" #include "llinventoryobserver.h" #include "llnetmap.h" #include "llpanelpeoplemenus.h" +#include "llparticipantlist.h" #include "llsidetraypanelcontainer.h" #include "llrecentpeople.h" #include "llviewercontrol.h" // for gSavedSettings @@ -64,6 +70,10 @@ #include "llvoiceclient.h" #include "llworld.h" #include "llspeakers.h" +#include "llfloaterwebcontent.h" + +#include "llagentui.h" +#include "llslurl.h" #define FRIEND_LIST_UPDATE_TIMEOUT 0.5 #define NEARBY_LIST_UPDATE_INTERVAL 1 @@ -73,9 +83,9 @@ static const std::string FRIENDS_TAB_NAME = "friends_panel"; static const std::string GROUP_TAB_NAME = "groups_panel"; static const std::string RECENT_TAB_NAME = "recent_panel"; static const std::string BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars - static const std::string COLLAPSED_BY_USER = "collapsed_by_user"; + extern S32 gMaxAgentGroups; /** Comparator for comparing avatar items by last interaction date */ @@ -495,6 +505,7 @@ public: LLPanelPeople::LLPanelPeople() : LLPanel(), + mTryToConnectToFbc(true), mTabContainer(NULL), mOnlineFriendList(NULL), mAllFriendList(NULL), @@ -573,6 +584,7 @@ BOOL LLPanelPeople::postBuild() getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("fbc_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); mTabContainer = getChild<LLTabContainer>("tabs"); mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); @@ -583,8 +595,11 @@ BOOL LLPanelPeople::postBuild() // updater is active only if panel is visible to user. friends_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFriendListUpdater, _2)); friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::removePicker, this)); + friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::updateFacebookList, this, _2)); + mOnlineFriendList = friends_tab->getChild<LLAvatarList>("avatars_online"); mAllFriendList = friends_tab->getChild<LLAvatarList>("avatars_all"); + mSuggestedFriends = friends_tab->getChild<LLAvatarList>("suggested_friends"); mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online")); mOnlineFriendList->setShowIcons("FriendsListShowIcons"); mOnlineFriendList->showPermissions("FriendsListShowPermissions"); @@ -617,6 +632,7 @@ BOOL LLPanelPeople::postBuild() mRecentList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); + mSuggestedFriends->setContextMenu(&LLPanelPeopleMenus::gSuggestedFriendsContextMenu); setSortOrder(mRecentList, (ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"), false); setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false); @@ -695,7 +711,7 @@ void LLPanelPeople::updateFriendListHelpText() // Seems sometimes all_friends can be empty because of issue with Inventory loading (clear cache, slow connection...) // So, lets check all lists to avoid overlapping the text with online list. See EXT-6448. - bool any_friend_exists = mAllFriendList->filterHasMatches() || mOnlineFriendList->filterHasMatches(); + bool any_friend_exists = mAllFriendList->filterHasMatches() || mOnlineFriendList->filterHasMatches() || mSuggestedFriends->filterHasMatches(); no_friends_text->setVisible(!any_friend_exists); if (no_friends_text->getVisible()) { @@ -762,9 +778,40 @@ void LLPanelPeople::updateFriendList() mAllFriendList->setDirty(true, !mAllFriendList->filterHasMatches()); //update trash and other buttons according to a selected item updateButtons(); + updateSuggestedFriendList(); showFriendsAccordionsIfNeeded(); } +bool LLPanelPeople::updateSuggestedFriendList() +{ + const LLAvatarTracker& av_tracker = LLAvatarTracker::instance(); + uuid_vec_t& suggested_friends = mSuggestedFriends->getIDs(); + suggested_friends.clear(); + + //Add suggested friends + LLSD friends = LLFacebookConnect::instance().getContent(); + for (LLSD::array_const_iterator i = friends.beginArray(); i != friends.endArray(); ++i) + { + LLUUID agent_id = (*i).asUUID(); + bool second_life_buddy = agent_id.notNull() ? av_tracker.isBuddy(agent_id) : false; + + if(!second_life_buddy) + { + //FB+SL but not SL friend + if (agent_id.notNull()) + { + suggested_friends.push_back(agent_id); + } + } + } + + //Force a refresh when there aren't any filter matches (prevent displaying content that shouldn't display) + mSuggestedFriends->setDirty(true, !mSuggestedFriends->filterHasMatches()); + showFriendsAccordionsIfNeeded(); + + return false; +} + void LLPanelPeople::updateNearbyList() { if (!mNearbyList) @@ -788,6 +835,51 @@ void LLPanelPeople::updateRecentList() mRecentList->setDirty(); } +bool LLPanelPeople::onConnectedToFacebook(const LLSD& data) +{ + LLSD::Integer connection_state = data.get("enum").asInteger(); + + if (connection_state == LLFacebookConnect::FB_CONNECTED) + { + LLFacebookConnect::instance().loadFacebookFriends(); + } + else if(connection_state == LLFacebookConnect::FB_NOT_CONNECTED) + { + updateSuggestedFriendList(); + }; + + return false; +} + +void LLPanelPeople::updateFacebookList(bool visible) +{ + if (visible) + { + LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLPanelPeople"); // just in case it is already listening + LLEventPumps::instance().obtain("FacebookConnectContent").listen("LLPanelPeople", boost::bind(&LLPanelPeople::updateSuggestedFriendList, this)); + + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLPanelPeople"); // just in case it is already listening + LLEventPumps::instance().obtain("FacebookConnectState").listen("LLPanelPeople", boost::bind(&LLPanelPeople::onConnectedToFacebook, this, _1)); + + if (LLFacebookConnect::instance().isConnected()) + { + LLFacebookConnect::instance().loadFacebookFriends(); + } + else if(mTryToConnectToFbc) + { + LLFacebookConnect::instance().checkConnectionToFacebook(); + mTryToConnectToFbc = false; + } + + updateSuggestedFriendList(); + } + else + { + LLEventPumps::instance().obtain("FacebookConnectState").stopListening("LLPanelPeople"); + LLEventPumps::instance().obtain("FacebookConnectContent").stopListening("LLPanelPeople"); + } +} + void LLPanelPeople::updateButtons() { std::string cur_tab = getActiveTabName(); @@ -993,23 +1085,25 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string) { // store accordion tabs opened/closed state before any manipulation with accordion tabs if (!saved_filter.empty()) - { - notifyChildren(LLSD().with("action","store_state")); - } + { + notifyChildren(LLSD().with("action","store_state")); + } mOnlineFriendList->setNameFilter(filter); mAllFriendList->setNameFilter(filter); + mSuggestedFriends->setNameFilter(filter); - setAccordionCollapsedByUser("tab_online", false); - setAccordionCollapsedByUser("tab_all", false); - showFriendsAccordionsIfNeeded(); + setAccordionCollapsedByUser("tab_online", false); + setAccordionCollapsedByUser("tab_all", false); + setAccordionCollapsedByUser("tab_suggested_friends", false); + showFriendsAccordionsIfNeeded(); // restore accordion tabs state _after_ all manipulations if(saved_filter.empty()) - { - notifyChildren(LLSD().with("action","restore_state")); - } -} + { + notifyChildren(LLSD().with("action","restore_state")); + } + } else if (cur_tab == GROUP_TAB_NAME) { mGroupList->setNameFilter(filter); @@ -1229,7 +1323,7 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) mAllFriendList->showPermissions(show_permissions); mOnlineFriendList->showPermissions(show_permissions); } -} + } void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) { @@ -1387,6 +1481,7 @@ void LLPanelPeople::showFriendsAccordionsIfNeeded() // Expand and show accordions if needed, else - hide them showAccordion("tab_online", mOnlineFriendList->filterHasMatches()); showAccordion("tab_all", mAllFriendList->filterHasMatches()); + showAccordion("tab_suggested_friends", mSuggestedFriends->filterHasMatches()); // Rearrange accordions LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("friends_accordion"); @@ -1450,4 +1545,5 @@ bool LLPanelPeople::isAccordionCollapsedByUser(const std::string& name) return isAccordionCollapsedByUser(getChild<LLUICtrl>(name)); } + // EOF diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 4740964dee..c7141f36ee 100755 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -22,7 +22,7 @@ * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ - */ + */ #ifndef LL_LLPANELPEOPLE_H #define LL_LLPANELPEOPLE_H @@ -30,6 +30,7 @@ #include <llpanel.h> #include "llcallingcard.h" // for avatar tracker +#include "llfloaterwebcontent.h" #include "llvoiceclient.h" class LLAvatarList; @@ -55,6 +56,8 @@ public: // when voice is available /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + bool mTryToConnectToFbc; + // internals class Updater; @@ -73,8 +76,10 @@ private: // methods indirectly called by the updaters void updateFriendListHelpText(); void updateFriendList(); + bool updateSuggestedFriendList(); void updateNearbyList(); void updateRecentList(); + void updateFacebookList(bool visible); bool isItemsFreeOfFriends(const uuid_vec_t& uuids); @@ -121,6 +126,8 @@ private: void onFriendListRefreshComplete(LLUICtrl*ctrl, const LLSD& param); + bool onConnectedToFacebook(const LLSD& data); + void setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool collapsed); void setAccordionCollapsedByUser(const std::string& name, bool collapsed); bool isAccordionCollapsedByUser(LLUICtrl* acc_tab); @@ -129,6 +136,7 @@ private: LLTabContainer* mTabContainer; LLAvatarList* mOnlineFriendList; LLAvatarList* mAllFriendList; + LLAvatarList* mSuggestedFriends; LLAvatarList* mNearbyList; LLAvatarList* mRecentList; LLGroupList* mGroupList; @@ -140,6 +148,7 @@ private: Updater* mFriendListUpdater; Updater* mNearbyListUpdater; Updater* mRecentListUpdater; + Updater* mFacebookListUpdater; Updater* mButtonsUpdater; LLHandle< LLFloater > mPicker; }; diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index 49f7361c4a..313056f06a 100755 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -47,6 +47,7 @@ namespace LLPanelPeopleMenus PeopleContextMenu gPeopleContextMenu; NearbyPeopleContextMenu gNearbyPeopleContextMenu; +SuggestedFriendsContextMenu gSuggestedFriendsContextMenu; //== PeopleContextMenu =============================================================== @@ -301,4 +302,36 @@ void NearbyPeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags) hide_context_entries(menu, items, disabled_items); } +//== SuggestedFriendsContextMenu =============================================================== + +LLContextMenu* SuggestedFriendsContextMenu::createMenu() +{ + // set up the callbacks for all of the avatar menu items + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLContextMenu* menu; + + // Set up for one person selected menu + const LLUUID& id = mUUIDs.front(); + registrar.add("Avatar.Profile", boost::bind(&LLAvatarActions::showProfile, id)); + registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, id)); + + // create the context menu from the XUI + menu = createFromFile("menu_people_nearby.xml"); + buildContextMenu(*menu, 0x0); + + return menu; +} + +void SuggestedFriendsContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + items.push_back(std::string("view_profile")); + items.push_back(std::string("add_friend")); + + hide_context_entries(menu, items, disabled_items); +} + } // namespace LLPanelPeopleMenus diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h index 0a1dcef303..5367eca0d3 100755 --- a/indra/newview/llpanelpeoplemenus.h +++ b/indra/newview/llpanelpeoplemenus.h @@ -58,8 +58,21 @@ protected: /*virtual*/ void buildContextMenu(class LLMenuGL& menu, U32 flags); }; +/** + * Menu used in the suggested friends list. + */ +class SuggestedFriendsContextMenu : public PeopleContextMenu +{ +public: + /*virtual*/ LLContextMenu * createMenu(); + +protected: + /*virtual*/ void buildContextMenu(class LLMenuGL& menu, U32 flags); +}; + extern PeopleContextMenu gPeopleContextMenu; extern NearbyPeopleContextMenu gNearbyPeopleContextMenu; +extern SuggestedFriendsContextMenu gSuggestedFriendsContextMenu; } // namespace LLPanelPeopleMenus diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 76d38f067d..9504f22a1d 100755 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -94,7 +94,8 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mZoomObjectFace(0), mVolumeSliderVisible(0), mWindowShade(NULL), - mHideImmediately(false) + mHideImmediately(false), + mSecureURL(false) { mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this)); mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this)); @@ -345,7 +346,7 @@ void LLPanelPrimMediaControls::updateShape() // Disable zoom if HUD mZoomCtrl->setEnabled(!is_hud); mUnzoomCtrl->setEnabled(!is_hud); - mSecureLockIcon->setVisible(false); + mSecureURL = false; mCurrentURL = media_impl->getCurrentMediaURL(); mBackCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateBack() && can_navigate); @@ -382,7 +383,7 @@ void LLPanelPrimMediaControls::updateShape() mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); mWhitelistIcon->setVisible(false); - mSecureLockIcon->setVisible(false); + mSecureURL = false; if (mMediaPanelScroll) { mMediaPanelScroll->setVisible(false); @@ -416,7 +417,7 @@ void LLPanelPrimMediaControls::updateShape() mMediaPlaySliderCtrl->setEnabled(true); } - // video vloume + // video volume if(volume <= 0.0) { mMuteBtn->setToggleState(true); @@ -492,10 +493,8 @@ void LLPanelPrimMediaControls::updateShape() std::string prefix = std::string("https://"); std::string test_prefix = mCurrentURL.substr(0, prefix.length()); LLStringUtil::toLower(test_prefix); - if(test_prefix == prefix) - { - mSecureLockIcon->setVisible(has_focus); - } + mSecureURL = has_focus && (test_prefix == prefix); + mCurrentURL = (mSecureURL ? " " + mCurrentURL : mCurrentURL); if(mCurrentURL!=mPreviousURL) { @@ -746,6 +745,9 @@ void LLPanelPrimMediaControls::draw() clearFaceOnFade(); } } + + // Show/hide the lock icon for secure browsing + mSecureLockIcon->setVisible(mSecureURL && !mMediaAddress->hasFocus()); // Build rect for icon area in coord system of this panel // Assumes layout_stack is a direct child of this panel diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h index eeb433e306..6d2eb3430e 100755 --- a/indra/newview/llpanelprimmediacontrols.h +++ b/indra/newview/llpanelprimmediacontrols.h @@ -191,6 +191,7 @@ private: bool mUpdateSlider; bool mClearFaceOnFade; bool mHideImmediately; + bool mSecureURL; LLMatrix4 mLastCameraMat; EZoomLevel mCurrentZoom; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index c53760bca1..ee6893907e 100755 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llavatarnamecache.h" +#include "llerror.h" #include "llimview.h" #include "llfloaterimcontainer.h" #include "llparticipantlist.h" @@ -401,6 +402,8 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) adjustParticipant(avatar_id); } +static LLFastTimer::DeclareTimer FTM_FOLDERVIEW_TEST("add test avatar agents"); + void LLParticipantList::adjustParticipant(const LLUUID& speaker_id) { LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id); diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp new file mode 100644 index 0000000000..7532ebfc57 --- /dev/null +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -0,0 +1,874 @@ +/** +* @file llsnapshotlivepreview.cpp +* @brief Implementation of llsnapshotlivepreview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llagentcamera.h" +#include "llagentui.h" +#include "llcombobox.h" +#include "lleconomy.h" +#include "llfloaterperms.h" +#include "llfloaterreg.h" +#include "llfloatersocial.h" +#include "llimagebmp.h" +#include "llimagej2c.h" +#include "llimagejpeg.h" +#include "llimagepng.h" +#include "lllandmarkactions.h" +#include "lllocalcliprect.h" +#include "llnotificationsutil.h" +#include "llslurl.h" +#include "llsnapshotlivepreview.h" +#include "lltoolfocus.h" +#include "llviewercontrol.h" +#include "llviewermenufile.h" // upload_new_resource() +#include "llviewerstats.h" +#include "llvfile.h" +#include "llvfs.h" +#include "llwebsharing.h" +#include "llwindow.h" +#include "llworld.h" + +const F32 AUTO_SNAPSHOT_TIME_DELAY = 1.f; + +F32 SHINE_TIME = 0.5f; +F32 SHINE_WIDTH = 0.6f; +F32 SHINE_OPACITY = 0.3f; +F32 FALL_TIME = 0.6f; +S32 BORDER_WIDTH = 6; + +const S32 MAX_TEXTURE_SIZE = 512 ; //max upload texture size 512 * 512 + +std::set<LLSnapshotLivePreview*> LLSnapshotLivePreview::sList; + +LLSnapshotLivePreview::LLSnapshotLivePreview (const LLSnapshotLivePreview::Params& p) + : LLView(p), + mColor(1.f, 0.f, 0.f, 0.5f), + mCurImageIndex(0), + mPreviewImage(NULL), + mThumbnailImage(NULL) , + mThumbnailWidth(0), + mThumbnailHeight(0), + mPreviewImageEncoded(NULL), + mFormattedImage(NULL), + mShineCountdown(0), + mFlashAlpha(0.f), + mNeedsFlash(TRUE), + mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")), + mDataSize(0), + mSnapshotType(SNAPSHOT_POSTCARD), + mSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))), + mSnapshotUpToDate(FALSE), + mCameraPos(LLViewerCamera::getInstance()->getOrigin()), + mCameraRot(LLViewerCamera::getInstance()->getQuaternion()), + mSnapshotActive(FALSE), + mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR) +{ + setSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")); + mSnapshotDelayTimer.setTimerExpirySec(0.0f); + mSnapshotDelayTimer.start(); + // gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this ); + sList.insert(this); + setFollowsAll(); + mWidth[0] = gViewerWindow->getWindowWidthRaw(); + mWidth[1] = gViewerWindow->getWindowWidthRaw(); + mHeight[0] = gViewerWindow->getWindowHeightRaw(); + mHeight[1] = gViewerWindow->getWindowHeightRaw(); + mImageScaled[0] = FALSE; + mImageScaled[1] = FALSE; + + mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ; + mKeepAspectRatio = gSavedSettings.getBOOL("KeepAspectForSnapshot") ; + mThumbnailUpdateLock = FALSE ; + mThumbnailUpToDate = FALSE ; +} + +LLSnapshotLivePreview::~LLSnapshotLivePreview() +{ + // delete images + mPreviewImage = NULL; + mPreviewImageEncoded = NULL; + mFormattedImage = NULL; + + // gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this ); + sList.erase(this); +} + +void LLSnapshotLivePreview::setMaxImageSize(S32 size) +{ + if(size < MAX_SNAPSHOT_IMAGE_SIZE) + { + mMaxImageSize = size; + } + else + { + mMaxImageSize = MAX_SNAPSHOT_IMAGE_SIZE ; + } +} + +LLViewerTexture* LLSnapshotLivePreview::getCurrentImage() +{ + return mViewerImage[mCurImageIndex]; +} + +F32 LLSnapshotLivePreview::getAspect() +{ + F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight()); + F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()); + + if (!mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot")) + { + return image_aspect_ratio; + } + else + { + return window_aspect_ratio; + } +} + +F32 LLSnapshotLivePreview::getImageAspect() +{ + if (!getCurrentImage()) + { + return 0.f; + } + + return getAspect() ; +} + +void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay) +{ + // Invalidate current image. + lldebugs << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << llendl; + if (getSnapshotUpToDate()) + { + S32 old_image_index = mCurImageIndex; + mCurImageIndex = (mCurImageIndex + 1) % 2; + setSize(mWidth[old_image_index], mHeight[old_image_index]); + mFallAnimTimer.start(); + } + mSnapshotUpToDate = FALSE; + + // Update snapshot source rect depending on whether we keep the aspect ratio. + LLRect& rect = mImageRect[mCurImageIndex]; + rect.set(0, getRect().getHeight(), getRect().getWidth(), 0); + + F32 image_aspect_ratio = ((F32)getWidth()) / ((F32)getHeight()); + F32 window_aspect_ratio = ((F32)getRect().getWidth()) / ((F32)getRect().getHeight()); + + if (mKeepAspectRatio)//gSavedSettings.getBOOL("KeepAspectForSnapshot")) + { + if (image_aspect_ratio > window_aspect_ratio) + { + // trim off top and bottom + S32 new_height = llround((F32)getRect().getWidth() / image_aspect_ratio); + rect.mBottom += (getRect().getHeight() - new_height) / 2; + rect.mTop -= (getRect().getHeight() - new_height) / 2; + } + else if (image_aspect_ratio < window_aspect_ratio) + { + // trim off left and right + S32 new_width = llround((F32)getRect().getHeight() * image_aspect_ratio); + rect.mLeft += (getRect().getWidth() - new_width) / 2; + rect.mRight -= (getRect().getWidth() - new_width) / 2; + } + } + + // Stop shining animation. + mShineAnimTimer.stop(); + + // Update snapshot if requested. + if (new_snapshot) + { + mSnapshotDelayTimer.start(); + mSnapshotDelayTimer.setTimerExpirySec(delay); + LLFloaterSnapshot::preUpdate(); + LLFloaterSocial::preUpdate(); + } + + // Update thumbnail if requested. + if(new_thumbnail) + { + mThumbnailUpToDate = FALSE ; + } +} + +void LLSnapshotLivePreview::setSnapshotQuality(S32 quality) +{ + llclamp(quality, 0, 100); + if (quality != mSnapshotQuality) + { + mSnapshotQuality = quality; + gSavedSettings.setS32("SnapshotQuality", quality); + mSnapshotUpToDate = FALSE; + } +} + +void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y) +{ + F32 line_width ; + glGetFloatv(GL_LINE_WIDTH, &line_width) ; + glLineWidth(2.0f * line_width) ; + LLColor4 color(0.0f, 0.0f, 0.0f, 1.0f) ; + gl_rect_2d( mPreviewRect.mLeft + offset_x, mPreviewRect.mTop + offset_y, + mPreviewRect.mRight + offset_x, mPreviewRect.mBottom + offset_y, color, FALSE ) ; + glLineWidth(line_width) ; + + //draw four alpha rectangles to cover areas outside of the snapshot image + if(!mKeepAspectRatio) + { + LLColor4 alpha_color(0.5f, 0.5f, 0.5f, 0.8f) ; + S32 dwl = 0, dwr = 0 ; + if(mThumbnailWidth > mPreviewRect.getWidth()) + { + dwl = (mThumbnailWidth - mPreviewRect.getWidth()) >> 1 ; + dwr = mThumbnailWidth - mPreviewRect.getWidth() - dwl ; + + gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y, + mPreviewRect.mLeft + offset_x, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ; + gl_rect_2d( mPreviewRect.mRight + offset_x, mPreviewRect.mTop + offset_y, + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y, alpha_color, TRUE ) ; + } + + if(mThumbnailHeight > mPreviewRect.getHeight()) + { + S32 dh = (mThumbnailHeight - mPreviewRect.getHeight()) >> 1 ; + gl_rect_2d(mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mBottom + offset_y , + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mBottom + offset_y - dh, alpha_color, TRUE ) ; + + dh = mThumbnailHeight - mPreviewRect.getHeight() - dh ; + gl_rect_2d( mPreviewRect.mLeft + offset_x - dwl, mPreviewRect.mTop + offset_y + dh, + mPreviewRect.mRight + offset_x + dwr, mPreviewRect.mTop + offset_y, alpha_color, TRUE ) ; + } + } +} + +//called when the frame is frozen. +void LLSnapshotLivePreview::draw() +{ + if (getCurrentImage() && + mPreviewImageEncoded.notNull() && + getSnapshotUpToDate()) + { + LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f); + gl_rect_2d(getRect(), bg_color); + const LLRect& rect = getImageRect(); + LLRect shadow_rect = rect; + shadow_rect.stretch(BORDER_WIDTH); + gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10); + + LLColor4 image_color(1.f, 1.f, 1.f, 1.f); + gGL.color4fv(image_color.mV); + gGL.getTexUnit(0)->bind(getCurrentImage()); + // calculate UV scale + F32 uv_width = isImageScaled() ? 1.f : llmin((F32)getWidth() / (F32)getCurrentImage()->getWidth(), 1.f); + F32 uv_height = isImageScaled() ? 1.f : llmin((F32)getHeight() / (F32)getCurrentImage()->getHeight(), 1.f); + gGL.pushMatrix(); + { + gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom, 0.f); + gGL.begin(LLRender::QUADS); + { + gGL.texCoord2f(uv_width, uv_height); + gGL.vertex2i(rect.getWidth(), rect.getHeight() ); + + gGL.texCoord2f(0.f, uv_height); + gGL.vertex2i(0, rect.getHeight() ); + + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(0, 0); + + gGL.texCoord2f(uv_width, 0.f); + gGL.vertex2i(rect.getWidth(), 0); + } + gGL.end(); + } + gGL.popMatrix(); + + gGL.color4f(1.f, 1.f, 1.f, mFlashAlpha); + gl_rect_2d(getRect()); + if (mNeedsFlash) + { + if (mFlashAlpha < 1.f) + { + mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f)); + } + else + { + mNeedsFlash = FALSE; + } + } + else + { + mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f)); + } + + // Draw shining animation if appropriate. + if (mShineCountdown > 0) + { + mShineCountdown--; + if (mShineCountdown == 0) + { + mShineAnimTimer.start(); + } + } + else if (mShineAnimTimer.getStarted()) + { + lldebugs << "Drawing shining animation" << llendl; + F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME); + + // draw "shine" effect + LLLocalClipRect clip(getLocalRect()); + { + // draw diagonal stripe with gradient that passes over screen + S32 x1 = gViewerWindow->getWindowWidthScaled() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f))); + S32 x2 = x1 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); + S32 x3 = x2 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); + S32 y1 = 0; + S32 y2 = gViewerWindow->getWindowHeightScaled(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.begin(LLRender::QUADS); + { + gGL.color4f(1.f, 1.f, 1.f, 0.f); + gGL.vertex2i(x1, y1); + gGL.vertex2i(x1 + gViewerWindow->getWindowWidthScaled(), y2); + gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY); + gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2); + gGL.vertex2i(x2, y1); + + gGL.color4f(1.f, 1.f, 1.f, SHINE_OPACITY); + gGL.vertex2i(x2, y1); + gGL.vertex2i(x2 + gViewerWindow->getWindowWidthScaled(), y2); + gGL.color4f(1.f, 1.f, 1.f, 0.f); + gGL.vertex2i(x3 + gViewerWindow->getWindowWidthScaled(), y2); + gGL.vertex2i(x3, y1); + } + gGL.end(); + } + + // if we're at the end of the animation, stop + if (shine_interp >= 1.f) + { + mShineAnimTimer.stop(); + } + } + } + + // draw framing rectangle + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4f(1.f, 1.f, 1.f, 1.f); + const LLRect& outline_rect = getImageRect(); + gGL.begin(LLRender::QUADS); + { + gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH); + gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH); + gGL.vertex2i(outline_rect.mRight, outline_rect.mTop); + gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop); + + gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom); + gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom); + gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH); + gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH); + + gGL.vertex2i(outline_rect.mLeft, outline_rect.mTop); + gGL.vertex2i(outline_rect.mLeft, outline_rect.mBottom); + gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH); + gGL.vertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH); + + gGL.vertex2i(outline_rect.mRight, outline_rect.mBottom); + gGL.vertex2i(outline_rect.mRight, outline_rect.mTop); + gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH); + gGL.vertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH); + } + gGL.end(); + } + + // draw old image dropping away + if (mFallAnimTimer.getStarted()) + { + S32 old_image_index = (mCurImageIndex + 1) % 2; + if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME) + { + lldebugs << "Drawing fall animation" << llendl; + F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME; + F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f); + LLColor4 image_color(1.f, 1.f, 1.f, alpha); + gGL.color4fv(image_color.mV); + gGL.getTexUnit(0)->bind(mViewerImage[old_image_index]); + // calculate UV scale + // *FIX get this to work with old image + BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull(); + F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f; + F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f; + gGL.pushMatrix(); + { + LLRect& rect = mImageRect[old_image_index]; + gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - llround(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f); + gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f); + gGL.begin(LLRender::QUADS); + { + gGL.texCoord2f(uv_width, uv_height); + gGL.vertex2i(rect.getWidth(), rect.getHeight() ); + + gGL.texCoord2f(0.f, uv_height); + gGL.vertex2i(0, rect.getHeight() ); + + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(0, 0); + + gGL.texCoord2f(uv_width, 0.f); + gGL.vertex2i(rect.getWidth(), 0); + } + gGL.end(); + } + gGL.popMatrix(); + } + } +} + +/*virtual*/ +void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLRect old_rect = getRect(); + LLView::reshape(width, height, called_from_parent); + if (old_rect.getWidth() != width || old_rect.getHeight() != height) + { + lldebugs << "window reshaped, updating thumbnail" << llendl; + updateSnapshot(FALSE, TRUE); + } +} + +BOOL LLSnapshotLivePreview::setThumbnailImageSize() +{ + if(getWidth() < 10 || getHeight() < 10) + { + return FALSE ; + } + S32 window_width = gViewerWindow->getWindowWidthRaw() ; + S32 window_height = gViewerWindow->getWindowHeightRaw() ; + + F32 window_aspect_ratio = ((F32)window_width) / ((F32)window_height); + + // UI size for thumbnail + // *FIXME: the rect does not change, so maybe there's no need to recalculate max w/h. + const LLRect& thumbnail_rect = mThumbnailPlaceholderRect; + S32 max_width = thumbnail_rect.getWidth(); + S32 max_height = thumbnail_rect.getHeight(); + + if (window_aspect_ratio > (F32)max_width / max_height) + { + // image too wide, shrink to width + mThumbnailWidth = max_width; + mThumbnailHeight = llround((F32)max_width / window_aspect_ratio); + } + else + { + // image too tall, shrink to height + mThumbnailHeight = max_height; + mThumbnailWidth = llround((F32)max_height * window_aspect_ratio); + } + + if(mThumbnailWidth > window_width || mThumbnailHeight > window_height) + { + return FALSE ;//if the window is too small, ignore thumbnail updating. + } + + S32 left = 0 , top = mThumbnailHeight, right = mThumbnailWidth, bottom = 0 ; + if(!mKeepAspectRatio) + { + F32 ratio_x = (F32)getWidth() / window_width ; + F32 ratio_y = (F32)getHeight() / window_height ; + + //if(getWidth() > window_width || + // getHeight() > window_height ) + { + if(ratio_x > ratio_y) + { + top = (S32)(top * ratio_y / ratio_x) ; + } + else + { + right = (S32)(right * ratio_x / ratio_y) ; + } + } + //else + //{ + // right = (S32)(right * ratio_x) ; + // top = (S32)(top * ratio_y) ; + //} + left = (S32)((mThumbnailWidth - right) * 0.5f) ; + bottom = (S32)((mThumbnailHeight - top) * 0.5f) ; + top += bottom ; + right += left ; + } + mPreviewRect.set(left - 1, top + 1, right + 1, bottom - 1) ; + + return TRUE ; +} + +void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) +{ + if(mThumbnailUpdateLock) //in the process of updating + { + return ; + } + if(getThumbnailUpToDate() && !force_update)//already updated + { + return ; + } + if(getWidth() < 10 || getHeight() < 10) + { + return ; + } + + ////lock updating + mThumbnailUpdateLock = TRUE ; + + if(!setThumbnailImageSize()) + { + mThumbnailUpdateLock = FALSE ; + mThumbnailUpToDate = TRUE ; + return ; + } + + if(mThumbnailImage) + { + resetThumbnailImage() ; + } + + LLPointer<LLImageRaw> raw = new LLImageRaw; + if(!gViewerWindow->thumbnailSnapshot(raw, + mThumbnailWidth, mThumbnailHeight, + gSavedSettings.getBOOL("RenderUIInSnapshot"), + FALSE, + mSnapshotBufferType) ) + { + raw = NULL ; + } + + if(raw) + { + raw->expandToPowerOfTwo(); + mThumbnailImage = LLViewerTextureManager::getLocalTexture(raw.get(), FALSE); + mThumbnailUpToDate = TRUE ; + } + + //unlock updating + mThumbnailUpdateLock = FALSE ; +} + + +// Called often. Checks whether it's time to grab a new snapshot and if so, does it. +// Returns TRUE if new snapshot generated, FALSE otherwise. +//static +BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) +{ + LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview; + if (previewp->getWidth() == 0 || previewp->getHeight() == 0) + { + llwarns << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << llendl; + return FALSE; + } + + // If we're in freeze-frame mode and camera has moved, update snapshot. + LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin(); + LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion(); + if (gSavedSettings.getBOOL("FreezeTime") && + (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f)) + { + previewp->mCameraPos = new_camera_pos; + previewp->mCameraRot = new_camera_rot; + // request a new snapshot whenever the camera moves, with a time delay + BOOL autosnap = gSavedSettings.getBOOL("AutoSnapshot"); + lldebugs << "camera moved, updating thumbnail" << llendl; + previewp->updateSnapshot( + autosnap, // whether a new snapshot is needed or merely invalidate the existing one + FALSE, // or if 1st arg is false, whether to produce a new thumbnail image. + autosnap ? AUTO_SNAPSHOT_TIME_DELAY : 0.f); // shutter delay if 1st arg is true. + } + + // see if it's time yet to snap the shot and bomb out otherwise. + previewp->mSnapshotActive = + (previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired()) + && !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active + if ( ! previewp->mSnapshotActive) + { + return FALSE; + } + + // time to produce a snapshot + previewp->setThumbnailImageSize(); + + lldebugs << "producing snapshot" << llendl; + if (!previewp->mPreviewImage) + { + previewp->mPreviewImage = new LLImageRaw; + } + + if (!previewp->mPreviewImageEncoded) + { + previewp->mPreviewImageEncoded = new LLImageRaw; + } + + previewp->setVisible(FALSE); + previewp->setEnabled(FALSE); + + previewp->getWindow()->incBusyCount(); + previewp->setImageScaled(FALSE); + + // grab the raw image and encode it into desired format + if(gViewerWindow->rawSnapshot( + previewp->mPreviewImage, + previewp->getWidth(), + previewp->getHeight(), + previewp->mKeepAspectRatio,//gSavedSettings.getBOOL("KeepAspectForSnapshot"), + previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE, + gSavedSettings.getBOOL("RenderUIInSnapshot"), + FALSE, + previewp->mSnapshotBufferType, + previewp->getMaxImageSize())) + { + previewp->mPreviewImageEncoded->resize( + previewp->mPreviewImage->getWidth(), + previewp->mPreviewImage->getHeight(), + previewp->mPreviewImage->getComponents()); + + if(previewp->getSnapshotType() == SNAPSHOT_TEXTURE) + { + lldebugs << "Encoding new image of format J2C" << llendl; + LLPointer<LLImageJ2C> formatted = new LLImageJ2C; + LLPointer<LLImageRaw> scaled = new LLImageRaw( + previewp->mPreviewImage->getData(), + previewp->mPreviewImage->getWidth(), + previewp->mPreviewImage->getHeight(), + previewp->mPreviewImage->getComponents()); + + scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); + previewp->setImageScaled(TRUE); + if (formatted->encode(scaled, 0.f)) + { + previewp->mDataSize = formatted->getDataSize(); + formatted->decode(previewp->mPreviewImageEncoded, 0); + } + } + else + { + // delete any existing image + previewp->mFormattedImage = NULL; + // now create the new one of the appropriate format. + LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotFormat(); + lldebugs << "Encoding new image of format " << format << llendl; + + switch(format) + { + case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG: + previewp->mFormattedImage = new LLImagePNG(); + break; + case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG: + previewp->mFormattedImage = new LLImageJPEG(previewp->mSnapshotQuality); + break; + case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP: + previewp->mFormattedImage = new LLImageBMP(); + break; + } + if (previewp->mFormattedImage->encode(previewp->mPreviewImage, 0)) + { + previewp->mDataSize = previewp->mFormattedImage->getDataSize(); + // special case BMP to copy instead of decode otherwise decode will crash. + if(format == LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP) + { + previewp->mPreviewImageEncoded->copy(previewp->mPreviewImage); + } + else + { + previewp->mFormattedImage->decode(previewp->mPreviewImageEncoded, 0); + } + } + } + + LLPointer<LLImageRaw> scaled = new LLImageRaw( + previewp->mPreviewImageEncoded->getData(), + previewp->mPreviewImageEncoded->getWidth(), + previewp->mPreviewImageEncoded->getHeight(), + previewp->mPreviewImageEncoded->getComponents()); + + if(!scaled->isBufferInvalid()) + { + // leave original image dimensions, just scale up texture buffer + if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024) + { + // go ahead and shrink image to appropriate power of 2 for display + scaled->biasedScaleToPowerOfTwo(1024); + previewp->setImageScaled(TRUE); + } + else + { + // expand image but keep original image data intact + scaled->expandToPowerOfTwo(1024, FALSE); + } + + previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE); + LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex]; + gGL.getTexUnit(0)->bind(curr_preview_image); + if (previewp->getSnapshotType() != SNAPSHOT_TEXTURE) + { + curr_preview_image->setFilteringOption(LLTexUnit::TFO_POINT); + } + else + { + curr_preview_image->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC); + } + curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP); + + previewp->mSnapshotUpToDate = TRUE; + previewp->generateThumbnailImage(TRUE) ; + + previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal(); + previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame + } + } + previewp->getWindow()->decBusyCount(); + // only show fullscreen preview when in freeze frame mode + previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame")); + previewp->mSnapshotDelayTimer.stop(); + previewp->mSnapshotActive = FALSE; + + if(!previewp->getThumbnailUpToDate()) + { + previewp->generateThumbnailImage() ; + } + lldebugs << "done creating snapshot" << llendl; + LLFloaterSnapshot::postUpdate(); + LLFloaterSocial::postUpdate(); + + return TRUE; +} + +void LLSnapshotLivePreview::setSize(S32 w, S32 h) +{ + lldebugs << "setSize(" << w << ", " << h << ")" << llendl; + setWidth(w); + setHeight(h); +} + +void LLSnapshotLivePreview::getSize(S32& w, S32& h) const +{ + w = getWidth(); + h = getHeight(); +} + +void LLSnapshotLivePreview::saveTexture() +{ + lldebugs << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << llendl; + // gen a new uuid for this asset + LLTransactionID tid; + tid.generate(); + LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + + LLPointer<LLImageJ2C> formatted = new LLImageJ2C; + LLPointer<LLImageRaw> scaled = new LLImageRaw(mPreviewImage->getData(), + mPreviewImage->getWidth(), + mPreviewImage->getHeight(), + mPreviewImage->getComponents()); + + scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); + lldebugs << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << llendl; + + if (formatted->encode(scaled, 0.0f)) + { + LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE); + std::string pos_string; + LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); + std::string who_took_it; + LLAgentUI::buildFullname(who_took_it); + LLAssetStorage::LLStoreAssetCallback callback = NULL; + S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + void *userdata = NULL; + upload_new_resource(tid, // tid + LLAssetType::AT_TEXTURE, + "Snapshot : " + pos_string, + "Taken by " + who_took_it + " at " + pos_string, + 0, + LLFolderType::FT_SNAPSHOT_CATEGORY, + LLInventoryType::IT_SNAPSHOT, + PERM_ALL, // Note: Snapshots to inventory is a special case of content upload + LLFloaterPerms::getGroupPerms(), // that is more permissive than other uploads + LLFloaterPerms::getEveryonePerms(), + "Snapshot : " + pos_string, + callback, expected_upload_cost, userdata); + gViewerWindow->playSnapshotAnimAndSound(); + } + else + { + LLNotificationsUtil::add("ErrorEncodingSnapshot"); + llwarns << "Error encoding snapshot" << llendl; + } + + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_SNAPSHOT_COUNT ); + + mDataSize = 0; +} + +BOOL LLSnapshotLivePreview::saveLocal() +{ + BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage); + + if(success) + { + gViewerWindow->playSnapshotAnimAndSound(); + } + return success; +} + +void LLSnapshotLivePreview::saveWeb() +{ + // *FIX: Will break if the window closes because of CloseSnapshotOnKeep! + // Needs to pass on ownership of the image. + LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get()); + if(!jpg) + { + llwarns << "Formatted image not a JPEG" << llendl; + return; + } + + LLSD metadata; + metadata["description"] = getChild<LLLineEditor>("description")->getText(); + + LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(gAgentCamera.getCameraPositionGlobal(), + boost::bind(&LLSnapshotLivePreview::regionNameCallback, this, jpg, metadata, _1, _2, _3, _4)); + + gViewerWindow->playSnapshotAnimAndSound(); +} + +void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z) +{ + metadata["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString(); + + LLWebSharing::instance().shareSnapshot(snapshot, metadata); +} diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h new file mode 100644 index 0000000000..fe3d257b02 --- /dev/null +++ b/indra/newview/llsnapshotlivepreview.h @@ -0,0 +1,164 @@ +/** +* @file llsnapshotlivepreview.h +* @brief Header file for llsnapshotlivepreview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLSNAPSHOTLIVEPREVIEW_H +#define LL_LLSNAPSHOTLIVEPREVIEW_H + +#include "llpanelsnapshot.h" +#include "llviewerwindow.h" + +class LLImageJPEG; + +///---------------------------------------------------------------------------- +/// Class LLSnapshotLivePreview +///---------------------------------------------------------------------------- +class LLSnapshotLivePreview : public LLView +{ + LOG_CLASS(LLSnapshotLivePreview); +public: + enum ESnapshotType + { + SNAPSHOT_POSTCARD, + SNAPSHOT_TEXTURE, + SNAPSHOT_LOCAL, + SNAPSHOT_WEB + }; + + + struct Params : public LLInitParam::Block<Params, LLView::Params> + { + Params() + { + name = "snapshot_live_preview"; + mouse_opaque = false; + } + }; + + + LLSnapshotLivePreview(const LLSnapshotLivePreview::Params& p); + ~LLSnapshotLivePreview(); + + /*virtual*/ void draw(); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent); + + void setSize(S32 w, S32 h); + void setWidth(S32 w) { mWidth[mCurImageIndex] = w; } + void setHeight(S32 h) { mHeight[mCurImageIndex] = h; } + void getSize(S32& w, S32& h) const; + S32 getWidth() const { return mWidth[mCurImageIndex]; } + S32 getHeight() const { return mHeight[mCurImageIndex]; } + S32 getDataSize() const { return mDataSize; } + void setMaxImageSize(S32 size) ; + S32 getMaxImageSize() {return mMaxImageSize ;} + + ESnapshotType getSnapshotType() const { return mSnapshotType; } + LLFloaterSnapshot::ESnapshotFormat getSnapshotFormat() const { return mSnapshotFormat; } + BOOL getSnapshotUpToDate() const { return mSnapshotUpToDate; } + BOOL isSnapshotActive() { return mSnapshotActive; } + LLViewerTexture* getThumbnailImage() const { return mThumbnailImage ; } + S32 getThumbnailWidth() const { return mThumbnailWidth ; } + S32 getThumbnailHeight() const { return mThumbnailHeight ; } + BOOL getThumbnailLock() const { return mThumbnailUpdateLock ; } + BOOL getThumbnailUpToDate() const { return mThumbnailUpToDate ;} + LLViewerTexture* getCurrentImage(); + F32 getImageAspect(); + F32 getAspect() ; + const LLRect& getImageRect() const { return mImageRect[mCurImageIndex]; } + BOOL isImageScaled() const { return mImageScaled[mCurImageIndex]; } + void setImageScaled(BOOL scaled) { mImageScaled[mCurImageIndex] = scaled; } + const LLVector3d& getPosTakenGlobal() const { return mPosTakenGlobal; } + + void setSnapshotType(ESnapshotType type) { mSnapshotType = type; } + void setSnapshotFormat(LLFloaterSnapshot::ESnapshotFormat type) { mSnapshotFormat = type; } + void setSnapshotQuality(S32 quality); + void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; } + void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f); + void saveWeb(); + void saveTexture(); + BOOL saveLocal(); + + LLPointer<LLImageFormatted> getFormattedImage() const { return mFormattedImage; } + LLPointer<LLImageRaw> getEncodedImage() const { return mPreviewImageEncoded; } + + /// Sets size of preview thumbnail image and thhe surrounding rect. + void setThumbnailPlaceholderRect(const LLRect& rect) {mThumbnailPlaceholderRect = rect; } + BOOL setThumbnailImageSize() ; + void generateThumbnailImage(BOOL force_update = FALSE) ; + void resetThumbnailImage() { mThumbnailImage = NULL ; } + void drawPreviewRect(S32 offset_x, S32 offset_y) ; + + // Returns TRUE when snapshot generated, FALSE otherwise. + static BOOL onIdle( void* snapshot_preview ); + + // callback for region name resolve + void regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z); + +private: + LLColor4 mColor; + LLPointer<LLViewerTexture> mViewerImage[2]; //used to represent the scene when the frame is frozen. + LLRect mImageRect[2]; + S32 mWidth[2]; + S32 mHeight[2]; + BOOL mImageScaled[2]; + S32 mMaxImageSize ; + + //thumbnail image + LLPointer<LLViewerTexture> mThumbnailImage ; + S32 mThumbnailWidth ; + S32 mThumbnailHeight ; + LLRect mPreviewRect ; + BOOL mThumbnailUpdateLock ; + BOOL mThumbnailUpToDate ; + LLRect mThumbnailPlaceholderRect; + + S32 mCurImageIndex; + LLPointer<LLImageRaw> mPreviewImage; + LLPointer<LLImageRaw> mPreviewImageEncoded; + LLPointer<LLImageFormatted> mFormattedImage; + LLFrameTimer mSnapshotDelayTimer; + S32 mShineCountdown; + LLFrameTimer mShineAnimTimer; + F32 mFlashAlpha; + BOOL mNeedsFlash; + LLVector3d mPosTakenGlobal; + S32 mSnapshotQuality; + S32 mDataSize; + ESnapshotType mSnapshotType; + LLFloaterSnapshot::ESnapshotFormat mSnapshotFormat; + BOOL mSnapshotUpToDate; + LLFrameTimer mFallAnimTimer; + LLVector3 mCameraPos; + LLQuaternion mCameraRot; + BOOL mSnapshotActive; + LLViewerWindow::ESnapshotType mSnapshotBufferType; + +public: + static std::set<LLSnapshotLivePreview*> sList; + BOOL mKeepAspectRatio ; +}; + +#endif // LL_LLSNAPSHOTLIVEPREVIEW_H + diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index 09ab31df36..bdbd8f1f83 100755 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -93,7 +93,7 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif if(!mIsGroupMsg) { - mAvatarName->setValue(p.from); + mAvatarName->setValue(p.from); } mTime->setValue(p.time); mSessionID = p.session_id; @@ -164,7 +164,7 @@ void LLToastIMPanel::spawnNameToolTip() params.background_visible(false); if(!mIsGroupMsg) { - params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE)); + params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE)); } else { diff --git a/indra/newview/llurlhistory.cpp b/indra/newview/llurlhistory.cpp index dd17068be5..a80b9da13c 100755 --- a/indra/newview/llurlhistory.cpp +++ b/indra/newview/llurlhistory.cpp @@ -103,22 +103,29 @@ LLSD LLURLHistory::getURLHistory(const std::string& collection) // static void LLURLHistory::addURL(const std::string& collection, const std::string& url) { - if(! url.empty()) + if(!url.empty()) { - sHistorySD[collection].insert(0, url); + LLURI u(url); + std::string simplified_url = u.scheme() + "://" + u.authority() + u.path(); + sHistorySD[collection].insert(0, simplified_url); LLURLHistory::limitSize(collection); } } // static void LLURLHistory::removeURL(const std::string& collection, const std::string& url) { - for(int index = 0; index < sHistorySD[collection].size(); index++) + if(!url.empty()) { - if(sHistorySD[collection].get(index).asString() == url) - { - sHistorySD[collection].erase(index); - } - } + LLURI u(url); + std::string simplified_url = u.scheme() + "://" + u.authority() + u.path(); + for(int index = 0; index < sHistorySD[collection].size(); index++) + { + if(sHistorySD[collection].get(index).asString() == simplified_url) + { + sHistorySD[collection].erase(index); + } + } + } } // static diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index c6b28b9e5e..4ce049df03 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -103,6 +103,7 @@ #include "llfloatersettingsdebug.h" #include "llfloatersidepanelcontainer.h" #include "llfloatersnapshot.h" +#include "llfloatersocial.h" #include "llfloatersounddevices.h" #include "llfloaterspellchecksettings.h" #include "llfloatertelehub.h" @@ -303,6 +304,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("sell_land", "floater_sell_land.xml", &LLFloaterSellLand::buildFloater); LLFloaterReg::add("settings_debug", "floater_settings_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSettingsDebug>); LLFloaterReg::add("sound_devices", "floater_sound_devices.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundDevices>); + LLFloaterReg::add("social", "floater_social.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSocial>); LLFloaterReg::add("stats", "floater_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>); LLFloaterReg::add("start_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRunQueue>); LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>); @@ -311,7 +313,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("my_profile", "floater_my_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create); LLFloaterReg::add("profile", "floater_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create); LLFloaterReg::add("how_to", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create); - + LLFloaterReg::add("fbc_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create); LLFloaterUIPreviewUtil::registerFloater(); LLFloaterReg::add("upload_anim_bvh", "floater_animation_bvh_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBvhPreview>, "upload"); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 2df028de69..13483790ed 100755 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2000,7 +2000,12 @@ void LLViewerMediaImpl::loadURI() "<>#%" ";/?:@&=", false); - llinfos << "Asking media source to load URI: " << uri << llendl; + { + // Do not log the query parts + LLURI u(uri); + std::string sanitized_uri = (u.query().empty() ? uri : u.scheme() + "://" + u.authority() + u.path()); + llinfos << "Asking media source to load URI: " << sanitized_uri << llendl; + } mMediaSource->loadURI( uri ); @@ -2567,7 +2572,12 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi if(mPriority == LLPluginClassMedia::PRIORITY_UNLOADED) { // Helpful to have media urls in log file. Shouldn't be spammy. - llinfos << "NOT LOADING media id= " << mTextureId << " url=" << url << " mime_type=" << mime_type << llendl; + { + // Do not log the query parts + LLURI u(url); + std::string sanitized_url = (u.query().empty() ? url : u.scheme() + "://" + u.authority() + u.path()); + llinfos << "NOT LOADING media id= " << mTextureId << " url=" << sanitized_url << ", mime_type=" << mime_type << llendl; + } // This impl should not be loaded at this time. LL_DEBUGS("PluginPriority") << this << "Not loading (PRIORITY_UNLOADED)" << LL_ENDL; @@ -2582,7 +2592,12 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi void LLViewerMediaImpl::navigateInternal() { // Helpful to have media urls in log file. Shouldn't be spammy. - llinfos << "media id= " << mTextureId << " url=" << mMediaURL << " mime_type=" << mMimeType << llendl; + { + // Do not log the query parts + LLURI u(mMediaURL); + std::string sanitized_url = (u.query().empty() ? mMediaURL : u.scheme() + "://" + u.authority() + u.path()); + llinfos << "media id= " << mTextureId << " url=" << sanitized_url << ", mime_type=" << mMimeType << llendl; + } if(mNavigateSuspended) { diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 9468a2d542..c096ef3f54 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -45,6 +45,7 @@ #include "llagent.h" #include "llagentaccess.h" #include "llagentcamera.h" +#include "llagentui.h" #include "llagentwearables.h" #include "llagentpilot.h" #include "llcompilequeue.h" @@ -52,6 +53,7 @@ #include "lldaycyclemanager.h" #include "lldebugview.h" #include "llenvmanager.h" +#include "llfacebookconnect.h" #include "llfilepicker.h" #include "llfirstuse.h" #include "llfloaterbuy.h" diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 678f24fb3c..02402fa876 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1595,6 +1595,9 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("EnvironmentSettings"); capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); + capabilityNames.append("FacebookConnect"); + capabilityNames.append("FlickrConnect"); + capabilityNames.append("TwitterConnect"); if (gSavedSettings.getBOOL("UseHTTPInventory")) { diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index 641f338f2c..69255af179 100755 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -148,9 +148,6 @@ public: LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << llendl; LLWebProfile::reportImageUploadStatus(true); } - -private: - LLPointer<LLImageFormatted> mImagep; }; @@ -172,7 +169,7 @@ public: headers["Cookie"] = LLWebProfile::getAuthCookie(); const std::string& redir_url = content["location"]; LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << llendl; - LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers); + LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder(), headers); } else { diff --git a/indra/newview/skins/default/textures/icons/Facebook.png b/indra/newview/skins/default/textures/icons/Facebook.png Binary files differnew file mode 100644 index 0000000000..8287d56f88 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Facebook.png diff --git a/indra/newview/skins/default/textures/icons/map_placeholder.png b/indra/newview/skins/default/textures/icons/map_placeholder.png Binary files differnew file mode 100644 index 0000000000..31e457aa75 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/map_placeholder.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 54f60f4441..94c187e21a 100755 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -148,6 +148,7 @@ with the same filename but different name <texture name="Command_Preferences_Icon" file_name="toolbar_icons/preferences.png" preload="true" /> <texture name="Command_Profile_Icon" file_name="toolbar_icons/profile.png" preload="true" /> <texture name="Command_Search_Icon" file_name="toolbar_icons/search.png" preload="true" /> + <texture name="Command_Social_Icon" file_name="toolbar_icons/facebook.png" preload="true" /> <texture name="Command_Snapshot_Icon" file_name="toolbar_icons/snapshot.png" preload="true" /> <texture name="Command_Speak_Icon" file_name="toolbar_icons/speak.png" preload="true" /> <texture name="Command_View_Icon" file_name="toolbar_icons/view.png" preload="true" /> @@ -199,6 +200,8 @@ with the same filename but different name <texture name="ExternalBrowser_Off" file_name="icons/ExternalBrowser_Off.png" preload="false" /> <texture name="Edit_Wrench" file_name="icons/Edit_Wrench.png" preload="false" /> + <texture name="Facebook_Icon" file_name="icons/Facebook.png" preload="false" /> + <texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" /> <texture name="Favorite_Star_Off" file_name="navbar/Favorite_Star_Off.png" preload="false" /> <texture name="Favorite_Star_Press" file_name="navbar/Favorite_Star_Press.png" preload="false" /> @@ -323,6 +326,8 @@ with the same filename but different name <texture name="Locked_Icon" file_name="icons/Locked_Icon.png" preload="false" /> + <texture name="Map_Placeholder_Icon" file_name="icons/map_placeholder.png" preload="true" /> + <texture name="MarketplaceBtn_Off" file_name="widgets/MarketplaceBtn_Off.png" preload="true" scale.left="30" scale.top="19" scale.right="35" scale.bottom="4" /> <texture name="MarketplaceBtn_Selected" file_name="widgets/MarketplaceBtn_Selected.png" preload="true" scale.left="30" scale.top="19" scale.right="35" scale.bottom="4" /> @@ -565,6 +570,7 @@ with the same filename but different name <texture name="Snapshot_Email" file_name="snapshot_email.png" preload="false" /> <texture name="Snapshot_Inventory" file_name="toolbar_icons/inventory.png" preload="false" /> <texture name="Snapshot_Profile" file_name="toolbar_icons/profile.png" preload="false" /> + <texture name="Snapshot_Facebook" file_name="toolbar_icons/facebook.png" preload="false" /> <texture name="startup_logo" file_name="windows/startup_logo.png" preload="true" /> diff --git a/indra/newview/skins/default/textures/toolbar_icons/facebook.png b/indra/newview/skins/default/textures/toolbar_icons/facebook.png Binary files differnew file mode 100644 index 0000000000..b960b834dc --- /dev/null +++ b/indra/newview/skins/default/textures/toolbar_icons/facebook.png diff --git a/indra/newview/skins/default/xui/en/floater_fbc_web.xml b/indra/newview/skins/default/xui/en/floater_fbc_web.xml new file mode 100644 index 0000000000..0d35e22a19 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_fbc_web.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater name="floater_fbc_web" + help_topic="fbc_web" + width="780" + height="775" + save_rect="true" + single_instance="true" + reuse_instance="false" + filename="floater_web_content.xml"/> diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml index 49d64767cc..853c209bca 100755 --- a/indra/newview/skins/default/xui/en/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml @@ -21,7 +21,11 @@ Sending Email </string> <string - name="profile_progress_str"> + name="facebook_progress_str"> + Posting to Facebook + </string> + <string + name="profile_progress_str"> Posting </string> <string @@ -33,7 +37,11 @@ Saving to Computer </string> <string - name="profile_succeeded_str"> + name="facebook_succeeded_str"> + Image uploaded + </string> + <string + name="profile_succeeded_str"> Image uploaded </string> <string @@ -49,7 +57,11 @@ Saved to Computer! </string> <string - name="profile_failed_str"> + name="facebook_failed_str"> + Failed to upload image to your Facebook timeline. + </string> + <string + name="profile_failed_str"> Failed to upload image to your Profile Feed. </string> <string diff --git a/indra/newview/skins/default/xui/en/floater_social.xml b/indra/newview/skins/default/xui/en/floater_social.xml new file mode 100644 index 0000000000..b7ff374d5f --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_social.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<floater + positioning="cascading" + can_close="true" + can_resize="false" + help_topic="floater_social" + layout="topleft" + name="floater_social" + save_rect="true" + single_instance="true" + reuse_instance="true" + title="POST TO FACEBOOK" + height="482" + width="304"> + <panel + height="482" + width="304" + visible="true" + name="background" + follows="all" + top="0" + left="0"> + <tab_container + name="tabs" + tab_group="1" + tab_min_width="70" + tab_height="30" + tab_position="top" + top="7" + height="437" + halign="center"> + <panel + filename="panel_social_status.xml" + class="llsocialstatuspanel" + follows="all" + label="STATUS" + name="panel_social_status"/> + <panel + filename="panel_social_photo.xml" + class="llsocialphotopanel" + follows="all" + label="PHOTO" + name="panel_social_photo"/> + <panel + filename="panel_social_place.xml" + class="llsocialcheckinpanel" + follows="all" + label="CHECK IN" + name="panel_social_place"/> + <panel + filename="panel_social_account.xml" + class="llsocialaccountpanel" + follows="all" + label="ACCOUNT" + name="panel_social_account"/> + </tab_container> + <panel + name="connection_status_panel" + follows="left|bottom|right" + height="24"> + <text + name="connection_error_text" + type="string" + follows="left|bottom|right" + top="5" + left="9" + width="250" + height="20" + wrap="true" + halign="left" + valign="center" + text_color="DrYellow" + font="SansSerif"> + Error + </text> + <loading_indicator + follows="left|bottom|right" + height="24" + width="24" + name="connection_loading_indicator" + top="2" + left="9" + visible="true"/> + <text + name="connection_loading_text" + type="string" + follows="left|bottom|right" + top="5" + left_pad="5" + width="250" + height="20" + wrap="true" + halign="left" + valign="center" + text_color="EmphasisColor" + font="SansSerif"> + Loading... + </text> + </panel> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_web_content.xml b/indra/newview/skins/default/xui/en/floater_web_content.xml index cea10adca8..a80440e844 100755 --- a/indra/newview/skins/default/xui/en/floater_web_content.xml +++ b/indra/newview/skins/default/xui/en/floater_web_content.xml @@ -125,10 +125,10 @@ <icon name="media_secure_lock_flag" height="16" - follows="top|right" + follows="top|left" image_name="Lock2" layout="topleft" - left_delta="620" + left_delta="2" top_delta="2" visible="false" tool_tip="Secured Browsing" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 2d65052def..a131d4b8e0 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -16,6 +16,14 @@ parameter="agent" /> </menu_item_call> <menu_item_call + label="Post to Facebook..." + name="PostToFacebook"> + <menu_item_call.on_click + function="Floater.Toggle" + parameter="social"/> + </menu_item_call> + <menu_item_separator/> + <menu_item_call label="Appearance..." name="ChangeOutfit"> <menu_item_call.on_click @@ -3049,6 +3057,13 @@ parameter="http://google.com"/> </menu_item_call> <menu_item_call + label="FB Connect Test" + name="FB Connect Test"> + <menu_item_call.on_click + function="Advanced.WebContentTest" + parameter="https://cryptic-ridge-1632.herokuapp.com/"/> + </menu_item_call> + <menu_item_call label="Dump SelectMgr" name="Dump SelectMgr"> <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 69b7fe5a75..dff8335efc 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6017,6 +6017,13 @@ Please select at least one type of content to search (General, Moderate, or Adul [MESSAGE] </notification> + <notification + icon="notify.tga" + name="FacebookConnect" + type="notifytip"> +[MESSAGE] + </notification> + <notification icon="notify.tga" name="PaymentReceived" diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index ed274d0233..dc0e4a5947 100755 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -365,6 +365,23 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M top="0" width="307" /> </accordion_tab> + <accordion_tab + layout="topleft" + height="173" + name="tab_suggested_friends" + title="People you may want to friend"> + <avatar_list + ignore_online_status="true" + allow_select="true" + follows="all" + height="173" + layout="topleft" + left="0" + name="suggested_friends" + show_permissions_granted="true" + top="0" + width="307" /> + </accordion_tab> </accordion> <text follows="all" diff --git a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml index 198ccd6e2f..8f90521bb2 100755 --- a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml +++ b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml @@ -315,7 +315,18 @@ <line_editor.commit_callback function="MediaCtrl.CommitURL"/> </line_editor> - <layout_stack + <icon + name="media_secure_lock_flag" + height="16" + follows="top|left" + image_name="Lock2" + layout="topleft" + left_delta="2" + top_delta="2" + visible="false" + tool_tip="Secured Browsing" + width="16" /> + <layout_stack name="media_address_url_icons" animate="false" follows="top|right" @@ -340,19 +351,6 @@ tool_tip="White List enabled" width="16" /> </layout_panel> - <layout_panel - layout="topleft" - width="16" - mouse_opaque="false" - auto_resize="false"> - <icon - name="media_secure_lock_flag" - height="16" - image_name="Lock2" - layout="topleft" - tool_tip="Secured Browsing" - width="16" /> - </layout_panel> </layout_stack> </layout_panel> <layout_panel diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml index d2f29ade44..61c8c971c2 100755 --- a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml +++ b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml @@ -16,11 +16,11 @@ imgoverlay_label_space="10" label="Post to My Profile Feed" layout="topleft" - left="10" + left_delta="0" name="save_to_profile_btn" pad_left="10" right="-10" - top="5"> + top_pad="10"> <button.commit_callback function="Snapshot.SaveToProfile" /> </button> diff --git a/indra/newview/skins/default/xui/en/panel_social_account.xml b/indra/newview/skins/default/xui/en/panel_social_account.xml new file mode 100644 index 0000000000..d7235396fe --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_social_account.xml @@ -0,0 +1,75 @@ +<panel + height="400" + width="304" + layout="topleft" + name="panel_social_account"> + <string + name="facebook_connected" + value="You are connected to Facebook as:" /> + <string + name="facebook_disconnected" + value="Not connected to Facebook" /> + <text + layout="topleft" + length="1" + follows="top|left" + font="SansSerif" + height="16" + left="9" + name="account_caption_label" + top="21" + type="string"> + Not connected to Facebook. + </text> + <text + layout="topleft" + top_pad="2" + length="1" + follows="top|left" + font="SansSerif" + height="16" + left="9" + name="account_name_label" + parse_urls="true" + type="string"/> + <panel + layout="topleft" + name="panel_buttons" + height="345" + left="9"> + <button + layout="topleft" + follows="left|top" + top_pad="9" + visible="true" + height="23" + label="Connect..." + name="connect_btn" + width="210"> + <commit_callback function="SocialSharing.Connect"/> + </button> + + <button + layout="topleft" + follows="left|top" + top_delta="0" + height="23" + label="Disconnect" + name="disconnect_btn" + width="210" + visible="false"> + <commit_callback function="SocialSharing.Disconnect"/> + </button> + <text + layout="topleft" + length="1" + follows="top|left" + height="16" + left="0" + name="account_learn_more_label" + top_pad="20" + type="string"> + [http://community.secondlife.com/t5/English-Knowledge-Base/Second-Life-Share/ta-p/2149711 Learn about posting to Facebook] + </text> + </panel> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_social_photo.xml b/indra/newview/skins/default/xui/en/panel_social_photo.xml new file mode 100644 index 0000000000..a55613b52a --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_social_photo.xml @@ -0,0 +1,152 @@ + <panel + height="400" + width="304" + layout="topleft" + name="panel_social_photo"> + <layout_stack + layout="topleft" + border_size="0" + height="392" + follows="all" + orientation="vertical" + name="stack_photo" + top="8"> + <layout_panel + name="snapshot_panel" + height="367"> + <combo_box + control_name="SocialPhotoResolution" + follows="left|top" + top="6" + left="9" + name="resolution_combobox" + tool_tip="Image resolution" + height="21" + width="135"> + <combo_box.item + label="Current Window" + name="CurrentWindow" + value="[i0,i0]" /> + <combo_box.item + label="640x480" + name="640x480" + value="[i640,i480]" /> + <combo_box.item + label="800x600" + name="800x600" + value="[i800,i600]" /> + <combo_box.item + label="1024x768" + name="1024x768" + value="[i1024,i768]" /> + </combo_box> + <text + follows="left|top" + font="SansSerifSmall" + height="14" + left="208" + length="1" + halign="right" + name="file_size_label" + top="9" + type="string" + width="50"> + [SIZE] KB + </text> + <panel + height="150" + width="250" + visible="true" + name="thumbnail_placeholder" + top="33" + follows="left|top" + left="9"> + </panel> + <button + follows="left|top" + height="23" + label="Refresh" + left="9" + top_pad="5" + name="new_snapshot_btn" + tool_tip="Click to refresh" + visible="true" + width="100" > + <button.commit_callback + function="SocialSharing.RefreshPhoto" /> + </button> + <text + follows="left|top" + font="SansSerif" + text_color="EmphasisColor" + height="14" + top_pad="-19" + left_pad="-20" + length="1" + halign="center" + name="working_lbl" + translate="false" + type="string" + visible="true" + width="150"> + Refreshing... + </text> + <text + length="1" + follows="top|left|right" + font="SansSerif" + height="16" + left="9" + name="caption_label" + top_pad="20" + type="string"> + Comment (optional): + </text> + <text_editor + follows="left|top" + height="87" + width="250" + left="9" + length="1" + max_length="700" + name="photo_caption" + type="string" + word_wrap="true"> + </text_editor> + <check_box + follows="left|top" + initial_value="true" + label="Include location in posting" + name="add_location_cb" + left="9" + height="16" + top_pad="8"/> + </layout_panel> + <layout_panel + name="photo_button_panel" + height="25"> + <button + follows="left|top" + top="0" + left="9" + height="23" + label="Post" + name="post_photo_btn" + width="100"> + <button.commit_callback + function="SocialSharing.SendPhoto" /> + </button> + <button + follows="left|top" + height="23" + label="Cancel" + name="cancel_photo_btn" + left_pad="15" + top_delta="0" + width="100"> + <button.commit_callback + function="SocialSharing.Cancel" /> + </button> + </layout_panel> + </layout_stack> + </panel> diff --git a/indra/newview/skins/default/xui/en/panel_social_place.xml b/indra/newview/skins/default/xui/en/panel_social_place.xml new file mode 100644 index 0000000000..13e94f6998 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_social_place.xml @@ -0,0 +1,132 @@ + <panel + height="400" + width="304" + layout="topleft" + name="panel_social_place"> + <layout_stack + layout="topleft" + border_size="0" + height="392" + follows="all" + orientation="vertical" + name="stack_place" + top="8"> + <layout_panel + name="place_detail_panel" + height="181"> + <text + length="1" + follows="top|left|right" + font="SansSerif" + height="16" + left="9" + name="place_caption_label" + top="13" + type="string"> + Say something about where you are: + </text> + <text_editor + follows="top|left" + height="150" + width="250" + left="9" + length="1" + max_length="700" + name="place_caption" + type="string" + word_wrap="true"> + </text_editor> + </layout_panel> + <layout_panel + name="place_map_panel" + height="186"> + <panel + follows="left|top" + height="128" + width="128" + background_visible="true" + bg_opaque_color="Black" + bg_alpha_color="Black" + top="20" + left="9" + visible="true" + name="map_border"> + </panel> + <loading_indicator + follows="left|top" + height="24" + width="24" + name="map_loading_indicator" + top="77" + left="61" + visible="true"/> + <icon + follows="left|top" + height="128" + width="128" + image_name="Map_Placeholder_Icon" + layout="topleft" + top="20" + left="9" + visible="true" + name="map_placeholder"> + </icon> + <icon + follows="left|top" + height="128" + width="128" + image_name="Map_Placeholder_Icon" + layout="topleft" + top="20" + left="9" + visible="true" + name="map_default"> + </icon> + <check_box + follows="left|top" + initial_value="false" + top_delta="8" + width="8" + label="" + name="add_place_view_cb" + left_pad="5"/> + <text + follows="left|top" + font="SansSerif" + height="32" + width="130" + word_wrap="true" + left_pad="12" + top_delta="-8" + type="string"> + Include overhead view of location + </text> + </layout_panel> + <layout_panel + name="place_button_panel" + height="25"> + <button + follows="left|top" + top="0" + left="9" + height="23" + label="Post" + name="post_place_btn" + width="100"> + <button.commit_callback + function="SocialSharing.SendCheckin" /> + </button> + <button + follows="left|top" + height="23" + label="Cancel" + name="cancel_place_btn" + left_pad="15" + top_delta="0" + width="100"> + <button.commit_callback + function="SocialSharing.Cancel" /> + </button> + </layout_panel> + </layout_stack> + </panel> diff --git a/indra/newview/skins/default/xui/en/panel_social_status.xml b/indra/newview/skins/default/xui/en/panel_social_status.xml new file mode 100644 index 0000000000..54cfa3f524 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_social_status.xml @@ -0,0 +1,67 @@ + <panel + height="400" + width="304" + layout="topleft" + name="panel_social_status"> + <layout_stack + layout="topleft" + border_size="0" + height="392" + follows="all" + orientation="vertical" + name="stack_status" + top="8"> + <layout_panel + name="status_detail_panel" + height="367"> + <text + length="1" + follows="top|left|right" + font="SansSerif" + height="16" + left="9" + name="status_caption_label" + top="13" + type="string"> + What's on your mind? + </text> + <text_editor + follows="left|top" + height="150" + width="250" + left="9" + length="1" + max_length="700" + name="status_message" + type="string" + word_wrap="true"> + </text_editor> + </layout_panel> + <layout_panel + name="status_button_panel" + height="25"> + <button + follows="left|top" + top="0" + left="9" + height="23" + label="Post" + name="post_status_btn" + width="100"> + <button.commit_callback + function="SocialSharing.SendStatus" /> + </button> + <button + follows="left|top" + height="23" + label="Cancel" + name="cancel_status_btn" + left_pad="15" + top_delta="0" + width="100"> + <button.commit_callback + function="SocialSharing.Cancel" /> + </button> + </layout_panel> + </layout_stack> + </panel> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 1c46cec479..5c23ffa1df 100755 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -148,6 +148,14 @@ Please try logging in again in a minute.</string> <string name="SentToInvalidRegion">You were sent to an invalid region.</string> <string name="TestingDisconnect">Testing viewer disconnect</string> + <!-- Facebook Connect and, eventually, other Social Network --> + <string name="SocialFacebookConnecting">Connecting to Facebook...</string> + <string name="SocialFacebookPosting">Posting...</string> + <string name="SocialFacebookDisconnecting">Disconnecting from Facebook...</string> + <string name="SocialFacebookErrorConnecting">Problem connecting to Facebook</string> + <string name="SocialFacebookErrorPosting">Problem posting to Facebook</string> + <string name="SocialFacebookErrorDisconnecting">Problem disconnecting from Facebook</string> + <!-- Tooltip --> <string name="TooltipPerson">Person</string><!-- Object under mouse pointer is an avatar --> <string name="TooltipNoName">(no name)</string> <!-- No name on an object --> @@ -3427,6 +3435,9 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. Drag items from inventory here </string> + <string name="facebook_post_success"> + You posted to Facebook. + </string> <string name="no_session_message"> (IM Session Doesn't Exist) @@ -3859,6 +3870,7 @@ Try enclosing path to the editor with double quotes. <string name="Command_Profile_Label">Profile</string> <string name="Command_Search_Label">Search</string> <string name="Command_Snapshot_Label">Snapshot</string> + <string name="Command_Social_Label">Facebook</string> <string name="Command_Speak_Label">Speak</string> <string name="Command_View_Label">Camera controls</string> <string name="Command_Voice_Label">Voice settings</string> @@ -3886,6 +3898,7 @@ Try enclosing path to the editor with double quotes. <string name="Command_Profile_Tooltip">Edit or view your profile</string> <string name="Command_Search_Tooltip">Find places, events, people</string> <string name="Command_Snapshot_Tooltip">Take a picture</string> + <string name="Command_Social_Tooltip">Post to Facebook</string> <string name="Command_Speak_Tooltip">Speak with people nearby using your microphone</string> <string name="Command_View_Tooltip">Changing camera angle</string> <string name="Command_Voice_Tooltip">Volume controls for calls and people near you in world</string> diff --git a/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml new file mode 100644 index 0000000000..af5aec2c34 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<person_tab_view + folder_arrow_image="Folder_Arrow" + folder_indentation="5" + item_height="24" + item_top_pad="3" + mouse_opaque="true" + follows="left|top|right" + text_pad="6" + text_pad_left="4" + text_pad_right="4" + arrow_size="10" + max_folder_item_overlap="2"/> diff --git a/indra/newview/skins/default/xui/en/widgets/person_view.xml b/indra/newview/skins/default/xui/en/widgets/person_view.xml new file mode 100644 index 0000000000..46c1b7ff75 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/person_view.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<person_view + folder_arrow_image="Folder_Arrow" + folder_indentation="5" + item_height="24" + item_top_pad="3" + mouse_opaque="true" + follows="left|top|right" + icon_pad="4" + icon_width="20" + text_pad="6" + text_pad_left="4" + text_pad_right="4" + arrow_size="10" + max_folder_item_overlap="2"> + <facebook_icon + follows="left" + height="14" + image_name="Facebook_Icon" + left="5" + bottom="6" + name="facebook_icon" + tool_tip="Facebook User" + visible="false" + width="14" /> + <avatar_icon + follows="left" + layout="topleft" + height="20" + default_icon_name="Generic_Person" + left="5" + top="2" + visible="false" + width="20" /> + <last_interaction_time_textbox + layout="topleft" + follows="right" + font="SansSerifSmall" + height="15" + left_pad="5" + right="-164" + name="last_interaction_time_textbox" + text_color="LtGray_50" + value="0s" + visible="false" + width="35" /> + <permission_edit_theirs_icon + layout="topleft" + height="16" + follows="right" + image_name="Permission_Edit_Objects_Theirs" + left_pad="3" + right="-129" + name="permission_edit_theirs_icon" + tool_tip="You can edit this friend's objects" + top="4" + visible="false" + width="16" /> + <permission_edit_mine_icon + layout="topleft" + height="16" + follows="right" + image_name="Permission_Edit_Objects_Mine" + left_pad="3" + right="-110" + name="permission_edit_mine_icon" + tool_tip="This friend can edit, delete or take your objects" + top="4" + visible="false" + width="16" /> + <permission_map_icon + height="16" + follows="right" + image_name="Permission_Visible_Map" + left_pad="3" + tool_tip="This friend can locate you on the map" + right="-91" + name="permission_map_icon" + visible="false" + width="16" /> + <permission_online_icon + height="16" + follows="right" + image_name="Permission_Visible_Online" + left_pad="3" + right="-72" + name="permission_online_icon" + tool_tip="This friend can see when you're online" + visible="false" + width="16" /> + <info_btn + follows="right" + height="16" + image_pressed="Info_Press" + image_unselected="Info_Over" + left_pad="3" + right="-53" + name="info_btn" + tool_tip="More info" + tab_stop="false" + visible="false" + width="16" /> + <profile_btn + layout="topleft" + follows="right" + height="20" + image_overlay="Web_Profile_Off" + left_pad="5" + right="-28" + name="profile_btn" + tab_stop="false" + tool_tip="View profile" + top="2" + visible="false" + width="20" /> + <output_monitor + auto_update="true" + follows="right" + draw_border="false" + height="16" + right="-3" + mouse_opaque="true" + name="speaking_indicator" + visible="false" + width="20" /> + </person_view> + diff --git a/indra/newview/tests/lltranslate_test.cpp b/indra/newview/tests/lltranslate_test.cpp index fd9527d631..8ce56326d8 100755 --- a/indra/newview/tests/lltranslate_test.cpp +++ b/indra/newview/tests/lltranslate_test.cpp @@ -308,8 +308,8 @@ void LLCurl::Responder::errorWithContent(U32, std::string const&, LLSD const&) { void LLCurl::Responder::result(LLSD const&) {} LLCurl::Responder::~Responder() {} -void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32) {} -void LLHTTPClient::get(const std::string&, LLPointer<LLCurl::Responder>, const LLSD&, const F32) {} +void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32, bool) {} +void LLHTTPClient::get(const std::string&, LLPointer<LLCurl::Responder>, const LLSD&, const F32, bool) {} LLBufferStream::LLBufferStream(const LLChannelDescriptors& channels, LLBufferArray* buffer) : std::iostream(&mStreamBuf), mStreamBuf(channels, buffer) {} diff --git a/indra/test_apps/llfbconnecttest/CMakeLists.txt b/indra/test_apps/llfbconnecttest/CMakeLists.txt new file mode 100644 index 0000000000..f56329a010 --- /dev/null +++ b/indra/test_apps/llfbconnecttest/CMakeLists.txt @@ -0,0 +1,304 @@ +# -*- cmake -*- +project(llfbconnecttest) + +include(00-Common) +include(FindOpenGL) +include(LLCommon) +include(LLPlugin) +include(Linking) +include(LLSharedLibs) +include(PluginAPI) +include(LLImage) +include(LLMath) +include(LLMessage) +include(LLRender) +include(LLWindow) +include(Glut) +include(Glui) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLMESSAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} +) + +if (DARWIN) + include(CMakeFindFrameworks) + find_library(COREFOUNDATION_LIBRARY CoreFoundation) +endif (DARWIN) + +### llfbconnecttest + +set(llfbconnecttest_SOURCE_FILES + llfbconnecttest.cpp + llfbconnecttest.h + bookmarks.txt + ) + +add_executable(llfbconnecttest + WIN32 + MACOSX_BUNDLE + ${llfbconnecttest_SOURCE_FILES} +) + +set_target_properties(llfbconnecttest + PROPERTIES + WIN32_EXECUTABLE + FALSE +) + +target_link_libraries(llfbconnecttest + ${GLUT_LIBRARY} + ${GLUI_LIBRARY} + ${OPENGL_LIBRARIES} + ${LLPLUGIN_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +if (DARWIN) + # The testbed needs to use a couple of CoreFoundation calls now, to deal with being a bundled app. + target_link_libraries(llfbconnecttest + ${COREFOUNDATION_LIBRARY} + ) +endif (DARWIN) + +add_dependencies(llfbconnecttest + stage_third_party_libs + SLPlugin + media_plugin_webkit + ${LLPLUGIN_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + +# turn off weird GLUI pragma +add_definitions(-DGLUI_NO_LIB_PRAGMA) + +if (DARWIN OR LINUX) + # glui.h contains code that triggers the "overloaded-virtual" warning in gcc. + set_source_files_properties(llfbconnecttest.cpp PROPERTIES COMPILE_FLAGS "-Wno-overloaded-virtual") +endif (DARWIN OR LINUX) + +# Gather build products of the various dependencies into the build directory for the testbed. + +if (DARWIN) + # path inside the app bundle where we'll need to copy plugins and other related files + set(PLUGINS_DESTINATION_DIR + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llfbconnecttest.app/Contents/Resources + ) + + # create the Contents/Resources directory + add_custom_command( + TARGET llfbconnecttest POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + make_directory + ${PLUGINS_DESTINATION_DIR} + COMMENT "Creating Resources directory in app bundle." + ) +else (DARWIN) + set(PLUGINS_DESTINATION_DIR + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + ) +endif (DARWIN) + +get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION) +add_custom_command(TARGET llfbconnecttest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_SLPLUGIN} ${PLUGINS_DESTINATION_DIR} + DEPENDS ${BUILT_SLPLUGIN} +) + +get_target_property(BUILT_LLCOMMON llcommon LOCATION) +add_custom_command(TARGET llfbconnecttest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_LLCOMMON} ${PLUGINS_DESTINATION_DIR} + DEPENDS ${BUILT_LLCOMMON} +) + + +if (DARWIN OR WINDOWS) + get_target_property(BUILT_WEBKIT_PLUGIN media_plugin_webkit LOCATION) + add_custom_command(TARGET llfbconnecttest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_WEBKIT_PLUGIN} ${PLUGINS_DESTINATION_DIR} + DEPENDS ${BUILT_WEBKIT_PLUGIN} + ) + + # copy over bookmarks file if llfbconnecttest gets built + get_target_property(BUILT_LLFBCONNECTTEST llfbconnecttest LOCATION) + add_custom_command(TARGET llfbconnecttest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${CMAKE_CURRENT_BINARY_DIR}/ + DEPENDS ${BUILT_LLFBCONNECTTEST} + ) + # also copy it to the same place as SLPlugin, which is what the mac wants... + add_custom_command(TARGET llfbconnecttest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${PLUGINS_DESTINATION_DIR} + DEPENDS ${BUILT_LLFBCONNECTTEST} + ) +endif (DARWIN OR WINDOWS) + +if (DARWIN) + add_custom_command(TARGET llfbconnecttest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib ${PLUGINS_DESTINATION_DIR} + DEPENDS ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib + ) +endif (DARWIN) + +if(WINDOWS) + #******************** + # Plugin test library deploy + # + # Debug config runtime files required for the FB connect test + set(fbconnecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}") + set(fbconnecttest_debug_files + libeay32.dll + libglib-2.0-0.dll + libgmodule-2.0-0.dll + libgobject-2.0-0.dll + libgthread-2.0-0.dll + qtcored4.dll + qtguid4.dll + qtnetworkd4.dll + qtopengld4.dll + qtwebkitd4.dll + ssleay32.dll + ) + copy_if_different( + ${fbconnecttest_debug_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Debug" + out_targets + ${fbconnecttest_debug_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + # Debug config runtime files required for the FB connect test (Qt image format plugins) + set(fbconecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}/imageformats") + set(fbconecttest_debug_files + qgifd4.dll + qicod4.dll + qjpegd4.dll + qmngd4.dll + qsvgd4.dll + qtiffd4.dll + ) + copy_if_different( + ${fbconecttest_debug_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Debug/imageformats" + out_targets + ${fbconecttest_debug_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + # Debug config runtime files required for the FB connect test (Qt codec plugins) + set(fbconnecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}/codecs") + set(fbconnecttest_debug_files + qcncodecsd4.dll + qjpcodecsd4.dll + qkrcodecsd4.dll + qtwcodecsd4.dll + ) + copy_if_different( + ${fbconnecttest_debug_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Debug/codecs" + out_targets + ${fbconnecttest_debug_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + # Release & ReleaseDebInfo config runtime files required for the FB connect test + set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}") + set(fbconnecttest_release_files + libeay32.dll + libglib-2.0-0.dll + libgmodule-2.0-0.dll + libgobject-2.0-0.dll + libgthread-2.0-0.dll + qtcore4.dll + qtgui4.dll + qtnetwork4.dll + qtopengl4.dll + qtwebkit4.dll + qtxmlpatterns4.dll + ssleay32.dll + ) + copy_if_different( + ${fbconnecttest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Release" + out_targets + ${fbconnecttest_release_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + copy_if_different( + ${fbconnecttest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" + out_targets + ${fbconnecttest_release_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + # Release & ReleaseDebInfo config runtime files required for the FB connect test (Qt image format plugins) + set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}/imageformats") + set(fbconnecttest_release_files + qgif4.dll + qico4.dll + qjpeg4.dll + qmng4.dll + qsvg4.dll + qtiff4.dll + ) + copy_if_different( + ${fbconnecttest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Release/imageformats" + out_targets + ${fbconnecttest_release_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + copy_if_different( + ${fbconnecttest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/imageformats" + out_targets + ${fbconnecttest_release_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + # Release & ReleaseDebInfo config runtime files required for the FB connect test (Qt codec plugins) + set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}/codecs") + set(fbconnecttest_release_files + qcncodecs4.dll + qjpcodecs4.dll + qkrcodecs4.dll + qtwcodecs4.dll + ) + copy_if_different( + ${fbconnecttest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/Release/codecs" + out_targets + ${fbconnecttest_release_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + copy_if_different( + ${fbconnecttest_release_src_dir} + "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/codecs" + out_targets + ${fbconnecttest_release_files} + ) + set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + + add_custom_target(copy_fbconnecttest_libs ALL + DEPENDS + ${fbconnect_test_targets} + ) + + add_dependencies(llfbconnecttest copy_fbconnecttest_libs) + +endif(WINDOWS) + +ll_deploy_sharedlibs_command(llfbconnecttest) diff --git a/indra/test_apps/llfbconnecttest/bookmarks.txt b/indra/test_apps/llfbconnecttest/bookmarks.txt new file mode 100644 index 0000000000..3995627ea9 --- /dev/null +++ b/indra/test_apps/llfbconnecttest/bookmarks.txt @@ -0,0 +1,4 @@ +# format is description, url (don't put ',' chars in description :) +# if no ',' found, whole line is used for both description and url +Google Home Page,http://www.google.com +Facebook Home Page,http://www.facebook.com diff --git a/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp b/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp new file mode 100644 index 0000000000..483a15c468 --- /dev/null +++ b/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp @@ -0,0 +1,2394 @@ +/** + * @file LLFBConnectTest.cpp + * @brief Facebook Connect Test App + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "indra_constants.h" + +#include "llapr.h" +#include "llerrorcontrol.h" + +#include <math.h> +#include <iomanip> +#include <sstream> +#include <ctime> + +#include "llfbconnecttest.h" + +#if __APPLE__ + #include <GLUT/glut.h> + #include <CoreFoundation/CoreFoundation.h> +#else + #define FREEGLUT_STATIC + #include "GL/freeglut.h" + #define GLUI_FREEGLUT +#endif + +#if LL_WINDOWS +#pragma warning(disable: 4263) +#pragma warning(disable: 4264) +#endif +#include "glui.h" + + +LLFBConnectTest* gApplication = 0; +static void gluiCallbackWrapper( int control_id ); + +//////////////////////////////////////////////////////////////////////////////// +// +static bool isTexture( GLuint texture ) +{ + bool result = false; + + // glIsTexture will sometimes return false for real textures... do this instead. + if(texture != 0) + result = true; + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel::mediaPanel() +{ + mMediaTextureHandle = 0; + mPickTextureHandle = 0; + mMediaSource = NULL; + mPickTexturePixels = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel::~mediaPanel() +{ + // delete OpenGL texture handles + if ( isTexture( mPickTextureHandle ) ) + { + std::cerr << "remMediaPanel: deleting pick texture " << mPickTextureHandle << std::endl; + glDeleteTextures( 1, &mPickTextureHandle ); + mPickTextureHandle = 0; + } + + if ( isTexture( mMediaTextureHandle ) ) + { + std::cerr << "remMediaPanel: deleting media texture " << mMediaTextureHandle << std::endl; + glDeleteTextures( 1, &mMediaTextureHandle ); + mMediaTextureHandle = 0; + } + + if(mPickTexturePixels) + { + delete mPickTexturePixels; + } + + if(mMediaSource) + { + delete mMediaSource; + } + +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLFBConnectTest::LLFBConnectTest( int app_window, int window_width, int window_height ) : + mVersionMajor( 2 ), + mVersionMinor( 0 ), + mVersionPatch( 0 ), + mMaxPanels( 25 ), + mViewportAspect( 0 ), + mAppWindow( app_window ), + mCurMouseX( 0 ), + mCurMouseY( 0 ), + mFuzzyMedia( true ), + mSelectedPanel( 0 ), + mDistanceCameraToSelectedGeometry( 0.0f ), + //mMediaBrowserControlEnableCookies( 0 ), + mMediaBrowserControlBackButton( 0 ), + mMediaBrowserControlForwardButton( 0 ), + //mMediaTimeControlVolume( 100 ), + //mMediaTimeControlSeekSeconds( 0 ), + //mGluiMediaTimeControlWindowFlag( true ), + mGluiMediaBrowserControlWindowFlag( true ), + mMediaBrowserControlBackButtonFlag( true ), + mMediaBrowserControlForwardButtonFlag( true ), + mHomeWebUrl( "https://cryptic-ridge-1632.herokuapp.com/" ) +{ + // debugging spam + std::cout << std::endl << " GLUT version: " << "3.7.6" << std::endl; // no way to get real version from GLUT + std::cout << std::endl << " GLUI version: " << GLUI_Master.get_version() << std::endl; + std::cout << std::endl << "Media Plugin Test version: " << mVersionMajor << "." << mVersionMinor << "." << mVersionPatch << std::endl; + + // bookmark title + mBookmarks.push_back( std::pair< std::string, std::string >( "--- Bookmarks ---", "" ) ); + + // insert hardcoded URLs here as required for testing + //mBookmarks.push_back( std::pair< std::string, std::string >( "description", "url" ) ); + + // read bookmarks from file. + // note: uses command in ./CmakeLists.txt which copies bookmmarks file from source directory + // to app directory (WITHOUT build configuration dir) (this is cwd in Windows within MSVC) + // For example, test_apps\llplugintest and not test_apps\llplugintest\Release + // This may need to be changed for Mac/Linux builds. + // See https://jira.lindenlab.com/browse/DEV-31350 for large list of media URLs from AGNI + const std::string bookmarks_filename( "bookmarks.txt" ); + std::ifstream file_handle( bookmarks_filename.c_str() ); + if ( file_handle.is_open() ) + { + std::cout << "Reading bookmarks for test" << std::endl; + while( ! file_handle.eof() ) + { + std::string line; + std::getline( file_handle, line ); + if ( file_handle.eof() ) + break; + + if ( line.substr( 0, 1 ) != "#" ) + { + size_t comma_pos = line.find_first_of( ',' ); + if ( comma_pos != std::string::npos ) + { + std::string description = line.substr( 0, comma_pos ); + std::string url = line.substr( comma_pos + 1 ); + mBookmarks.push_back( std::pair< std::string, std::string >( description, url ) ); + } + else + { + mBookmarks.push_back( std::pair< std::string, std::string >( line, line ) ); + }; + }; + }; + std::cout << "Read " << mBookmarks.size() << " bookmarks" << std::endl; + } + else + { + std::cout << "Unable to read bookmarks from file: " << bookmarks_filename << std::endl; + }; + + // initialize linden lab APR module + ll_init_apr(); + + // Set up llerror logging + { + LLError::initForApplication("."); + LLError::setDefaultLevel(LLError::LEVEL_INFO); + //LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG); + } + + // lots of randomness in this app + srand( ( unsigned int )time( 0 ) ); + + // build GUI + makeChrome(); + + // OpenGL initialilzation + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glClearDepth( 1.0f ); + glEnable( GL_DEPTH_TEST ); + glEnable( GL_COLOR_MATERIAL ); + glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); + glDepthFunc( GL_LEQUAL ); + glEnable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + glColor3f( 1.0f, 1.0f, 1.0f ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + + // start with a sane view + resetView(); + + // initial media panel + const int num_initial_panels = 1; + for( int i = 0; i < num_initial_panels; ++i ) + { + //addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + addMediaPanel( mHomeWebUrl ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLFBConnectTest::~LLFBConnectTest() +{ + // delete all media panels + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + remMediaPanel( mMediaPanels[ i ] ); + }; + + // Stop the plugin read thread if it's running. + LLPluginProcessParent::setUseReadThread(false); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::reshape( int width, int height ) +{ + // update viewport (the active window inside the chrome) + int viewport_x, viewport_y; + int viewport_height, viewport_width; + GLUI_Master.get_viewport_area( &viewport_x, &viewport_y, &viewport_width, &viewport_height ); + mViewportAspect = (float)( viewport_width ) / (float)( viewport_height ); + glViewport( viewport_x, viewport_y, viewport_width, viewport_height ); + + // save these as we'll need them later + mWindowWidth = width; + mWindowHeight = height; + + // adjust size of URL bar so it doesn't get clipped + mUrlEdit->set_w( mWindowWidth - 360 ); + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // trigger re-display + glutPostRedisplay(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::bindTexture(GLuint texture, GLint row_length, GLint alignment) +{ + glEnable( GL_TEXTURE_2D ); + + glBindTexture( GL_TEXTURE_2D, texture ); + glPixelStorei( GL_UNPACK_ROW_LENGTH, row_length ); + glPixelStorei( GL_UNPACK_ALIGNMENT, alignment ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLFBConnectTest::checkGLError(const char *name) +{ + bool result = false; + GLenum error = glGetError(); + + if(error != GL_NO_ERROR) + { + // For some reason, glGenTextures is returning GL_INVALID_VALUE... + std::cout << name << " ERROR 0x" << std::hex << error << std::dec << std::endl; + result = true; + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +GLfloat LLFBConnectTest::distanceToCamera( GLfloat point_x, GLfloat point_y, GLfloat point_z ) +{ + GLdouble camera_pos_x = 0.0f; + GLdouble camera_pos_y = 0.0f; + GLdouble camera_pos_z = 0.0f; + + GLdouble modelMatrix[16]; + GLdouble projMatrix[16]; + GLint viewport[4]; + + glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); + glGetDoublev(GL_PROJECTION_MATRIX, projMatrix); + glGetIntegerv(GL_VIEWPORT, viewport); + + gluUnProject( + (viewport[2]-viewport[0])/2 , (viewport[3]-viewport[1])/2, + 0.0, + modelMatrix, projMatrix, viewport, + &camera_pos_x, &camera_pos_y, &camera_pos_z ); + + GLfloat distance = + sqrt( ( camera_pos_x - point_x ) * ( camera_pos_x - point_x ) + + ( camera_pos_y - point_y ) * ( camera_pos_y - point_y ) + + ( camera_pos_z - point_z ) * ( camera_pos_z - point_z ) ); + + return distance; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::drawGeometry( int panel, bool selected ) +{ + // texture coordinates for each panel + GLfloat non_opengl_texture_coords[ 8 ] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }; + GLfloat opengl_texture_coords[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + + GLfloat *texture_coords = mMediaPanels[ panel ]->mAppTextureCoordsOpenGL?opengl_texture_coords:non_opengl_texture_coords; + + // base coordinates for each panel + GLfloat base_vertex_pos[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + + // calculate posiitons + const int num_panels = (int)mMediaPanels.size(); + const int num_rows = (int)sqrt( (float)num_panels ); + const int num_cols = num_panels / num_rows; + const int panel_x = ( panel / num_rows ); + const int panel_y = ( panel % num_rows ); + + // default spacing is small - make it larger if checkbox set - for testing positional audio + float spacing = 0.1f; + //if ( mLargePanelSpacing ) + // spacing = 2.0f; + + const GLfloat offset_x = num_cols * ( 1.0 + spacing ) / 2; + const GLfloat offset_y = num_rows * ( 1.0 + spacing ) / 2; + + // Adjust for media aspect ratios + { + float aspect = 1.0f; + + if(mMediaPanels[ panel ]->mMediaHeight != 0) + { + aspect = (float)mMediaPanels[ panel ]->mMediaWidth / (float)mMediaPanels[ panel ]->mMediaHeight; + } + + if(aspect > 1.0f) + { + // media is wider than it is high -- adjust the top and bottom in + for( int corner = 0; corner < 4; ++corner ) + { + float temp = base_vertex_pos[corner * 2 + 1]; + + if(temp < 0.5f) + temp += 0.5 - (0.5f / aspect); + else + temp -= 0.5 - (0.5f / aspect); + + base_vertex_pos[corner * 2 + 1] = temp; + } + } + else if(aspect < 1.0f) + { + // media is higher than it is wide -- adjust the left and right sides in + for( int corner = 0; corner < 4; ++corner ) + { + float temp = base_vertex_pos[corner * 2]; + + if(temp < 0.5f) + temp += 0.5f - (0.5f * aspect); + else + temp -= 0.5f - (0.5f * aspect); + + base_vertex_pos[corner * 2] = temp; + } + } + } + + glBegin( GL_QUADS ); + for( int corner = 0; corner < 4; ++corner ) + { + glTexCoord2f( texture_coords[ corner * 2 ], texture_coords[ corner * 2 + 1 ] ); + GLfloat x = base_vertex_pos[ corner * 2 ] + panel_x * ( 1.0 + spacing ) - offset_x + spacing / 2.0f; + GLfloat y = base_vertex_pos[ corner * 2 + 1 ] + panel_y * ( 1.0 + spacing ) - offset_y + spacing / 2.0f; + + glVertex3f( x, y, 0.0f ); + }; + glEnd(); + + // calculate distance to this panel if it's selected + if ( selected ) + { + GLfloat point_x = base_vertex_pos[ 0 ] + panel_x * ( 1.0 + spacing ) - offset_x + spacing / 2.0f; + GLfloat point_y = base_vertex_pos[ 0 + 1 ] + panel_y * ( 1.0 + spacing ) - offset_y + spacing / 2.0f; + GLfloat point_z = 0.0f; + mDistanceCameraToSelectedGeometry = distanceToCamera( point_x, point_y, point_z ); + }; +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::startPanelHighlight( float red, float green, float blue, float line_width ) +{ + glPushAttrib( GL_ALL_ATTRIB_BITS ); + glEnable( GL_POLYGON_OFFSET_FILL ); + glPolygonOffset( -2.5f, -2.5f ); + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + glLineWidth( line_width ); + glColor3f( red, green, blue ); + glDisable( GL_TEXTURE_2D ); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::endPanelHighlight() +{ + glPopAttrib(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::draw( int draw_type ) +{ + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + // drawing pick texture + if ( draw_type == DrawTypePickTexture ) + { + // only bother with pick if we have something to render + // Actually, we need to pick even if we're not ready to render. + // Otherwise you can't select and remove a panel which has gone bad. + //if ( mMediaPanels[ panel ]->mReadyToRender ) + { + glMatrixMode( GL_TEXTURE ); + glPushMatrix(); + + // pick texture is a power of 2 so no need to scale + glLoadIdentity(); + + // bind to media texture + glLoadIdentity(); + bindTexture( mMediaPanels[ panel ]->mPickTextureHandle ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + // draw geometry using pick texture + drawGeometry( panel, false ); + + glMatrixMode( GL_TEXTURE ); + glPopMatrix(); + }; + } + else + if ( draw_type == DrawTypeMediaTexture ) + { + bool texture_valid = false; + bool plugin_exited = false; + + if(mMediaPanels[ panel ]->mMediaSource) + { + texture_valid = mMediaPanels[ panel ]->mMediaSource->textureValid(); + plugin_exited = mMediaPanels[ panel ]->mMediaSource->isPluginExited(); + } + + // save texture matrix (changes for each panel) + glMatrixMode( GL_TEXTURE ); + glPushMatrix(); + + // only process texture if the media is ready to draw + // (we still want to draw the geometry) + if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid ) + { + // bind to media texture + bindTexture( mMediaPanels[ panel ]->mMediaTextureHandle ); + + if ( mFuzzyMedia ) + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + } + else + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + } + + // scale to fit panel + glScalef( mMediaPanels[ panel ]->mTextureScaleX, + mMediaPanels[ panel ]->mTextureScaleY, + 1.0f ); + }; + + float intensity = plugin_exited?0.25f:1.0f; + + // highlight the selected panel + if ( mSelectedPanel && ( mMediaPanels[ panel ]->mId == mSelectedPanel->mId ) ) + { + startPanelHighlight( intensity, intensity, 0.0f, 5.0f ); + drawGeometry( panel, true ); + endPanelHighlight(); + } + else + // this panel not able to render yet since it + // doesn't have enough information + if ( !mMediaPanels[ panel ]->mReadyToRender ) + { + startPanelHighlight( intensity, 0.0f, 0.0f, 2.0f ); + drawGeometry( panel, false ); + endPanelHighlight(); + } + else + // just display a border around the media + { + startPanelHighlight( 0.0f, intensity, 0.0f, 2.0f ); + drawGeometry( panel, false ); + endPanelHighlight(); + }; + + if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid ) + { + // draw visual geometry + drawGeometry( panel, false ); + } + + // restore texture matrix (changes for each panel) + glMatrixMode( GL_TEXTURE ); + glPopMatrix(); + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::display() +{ + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // start with a clean slate + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // set up OpenGL view + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glFrustum( -mViewportAspect * 0.04f, mViewportAspect * 0.04f, -0.04f, 0.04f, 0.1f, 50.0f ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glTranslatef( 0.0, 0.0, 0.0f ); + glTranslatef( mViewPos[ 0 ], mViewPos[ 1 ], -mViewPos[ 2 ] ); + glMultMatrixf( mViewRotation ); + + // draw pick texture + draw( DrawTypePickTexture ); + + // read colors and get coordinate values + glReadPixels( mCurMouseX, mCurMouseY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, mPixelReadColor ); + + // clear the pick render (otherwise it may depth-fight with the textures rendered later) + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // draw visible geometry + draw( DrawTypeMediaTexture ); + + glutSwapBuffers(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::idle() +{ +// checkGLError("LLFBConnectTest::idle"); + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // random creation/destruction of panels enabled? +/* + const time_t panel_timeout_time = 5; + if ( mRandomPanelCount ) + { + // time for a change + static time_t last_panel_time = 0; + if ( time( NULL ) - last_panel_time > panel_timeout_time ) + { + if ( rand() % 2 == 0 ) + { + if ( mMediaPanels.size() < 16 ) + { + std::cout << "Randomly adding new panel" << std::endl; + addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + }; + } + else + { + if ( mMediaPanels.size() > 0 ) + { + std::cout << "Deleting selected panel" << std::endl; + remMediaPanel( mSelectedPanel ); + }; + }; + time( &last_panel_time ); + }; + }; + + // random selection of bookmarks enabled? + const time_t bookmark_timeout_time = 5; + if ( mRandomBookmarks ) + { + // time for a change + static time_t last_bookmark_time = 0; + if ( time( NULL ) - last_bookmark_time > bookmark_timeout_time ) + { + // go to a different random bookmark on each panel + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + std::string uri = mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second; + + std::cout << "Random: navigating to : " << uri << std::endl; + + std::string mime_type = mimeTypeFromUrl( uri ); + + if ( mime_type != mMediaPanels[ panel ]->mMimeType ) + { + replaceMediaPanel( mMediaPanels[ panel ], uri ); + } + else + { + mMediaPanels[ panel ]->mMediaSource->loadURI( uri ); + mMediaPanels[ panel ]->mMediaSource->start(); + }; + }; + + time( &last_bookmark_time ); + }; + }; +*/ + // update UI + if ( mSelectedPanel ) + { + // set volume based on slider if we have time media + //if ( mGluiMediaTimeControlWindowFlag ) + //{ + // mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f ); + //}; + + // NOTE: it is absurd that we need cache the state of GLUI controls + // but enabling/disabling controls drags framerate from 500+ + // down to 15. Not a problem for plugin system - only this test + // enable/disable time based UI controls based on type of plugin + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + { + /* + if ( ! mGluiMediaTimeControlWindowFlag ) + { + mGluiMediaTimeControlWindow->enable(); + mGluiMediaTimeControlWindowFlag = true; + }; + */ + } + else + { + /* + if ( mGluiMediaTimeControlWindowFlag ) + { + mGluiMediaTimeControlWindow->disable(); + mGluiMediaTimeControlWindowFlag = false; + }; + */ + }; + + // enable/disable browser based UI controls based on type of plugin + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() ) + { + if ( ! mGluiMediaBrowserControlWindowFlag ) + { + mGluiMediaBrowserControlWindow->enable(); + mGluiMediaBrowserControlWindowFlag = true; + }; + } + else + { + if ( mGluiMediaBrowserControlWindowFlag ) + { + mGluiMediaBrowserControlWindow->disable(); + mGluiMediaBrowserControlWindowFlag = false; + }; + }; + + // enable/disable browser back button depending on browser history + if ( mSelectedPanel->mMediaSource->getHistoryBackAvailable() ) + { + if ( ! mMediaBrowserControlBackButtonFlag ) + { + mMediaBrowserControlBackButton->enable(); + mMediaBrowserControlBackButtonFlag = true; + }; + } + else + { + if ( mMediaBrowserControlBackButtonFlag ) + { + mMediaBrowserControlBackButton->disable(); + mMediaBrowserControlBackButtonFlag = false; + }; + }; + + // enable/disable browser forward button depending on browser history + if ( mSelectedPanel->mMediaSource->getHistoryForwardAvailable() ) + { + if ( ! mMediaBrowserControlForwardButtonFlag ) + { + mMediaBrowserControlForwardButton->enable(); + mMediaBrowserControlForwardButtonFlag = true; + }; + } + else + { + if ( mMediaBrowserControlForwardButtonFlag ) + { + mMediaBrowserControlForwardButton->disable(); + mMediaBrowserControlForwardButtonFlag = false; + }; + }; + + // NOTE: This is *very* slow and not worth optimising + updateStatusBar(); + }; + + // update all the panels + for( int panel_index = 0; panel_index < (int)mMediaPanels.size(); ++panel_index ) + { + mediaPanel *panel = mMediaPanels[ panel_index ]; + + // call plugins idle function so it can potentially update itself + panel->mMediaSource->idle(); + + // update each media panel + updateMediaPanel( panel ); + + LLRect dirty_rect; + if ( ! panel->mMediaSource->textureValid() ) + { + //std::cout << "texture invalid, skipping update..." << std::endl; + } + else + if ( panel && + ( panel->mMediaWidth != panel->mMediaSource->getWidth() || + panel->mMediaHeight != panel->mMediaSource->getHeight() ) ) + { + //std::cout << "Resize in progress, skipping update..." << std::endl; + } + else + if ( panel->mMediaSource->getDirty( &dirty_rect ) ) + { + const unsigned char* pixels = panel->mMediaSource->getBitsData(); + if ( pixels && isTexture(panel->mMediaTextureHandle)) + { + int x_offset = dirty_rect.mLeft; + int y_offset = dirty_rect.mBottom; + int width = dirty_rect.mRight - dirty_rect.mLeft; + int height = dirty_rect.mTop - dirty_rect.mBottom; + + if((dirty_rect.mRight <= panel->mTextureWidth) && (dirty_rect.mTop <= panel->mTextureHeight)) + { + // Offset the pixels pointer properly + pixels += ( y_offset * panel->mMediaSource->getTextureDepth() * panel->mMediaSource->getBitsWidth() ); + pixels += ( x_offset * panel->mMediaSource->getTextureDepth() ); + + // set up texture + bindTexture( panel->mMediaTextureHandle, panel->mMediaSource->getBitsWidth() ); + if ( mFuzzyMedia ) + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + } + else + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + }; + + checkGLError("glTexParameteri"); + + if(panel->mMediaSource->getTextureFormatSwapBytes()) + { + glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); + checkGLError("glPixelStorei"); + } + + // draw portion that changes into texture + glTexSubImage2D( GL_TEXTURE_2D, 0, + x_offset, + y_offset, + width, + height, + panel->mMediaSource->getTextureFormatPrimary(), + panel->mMediaSource->getTextureFormatType(), + pixels ); + + if(checkGLError("glTexSubImage2D")) + { + std::cerr << " panel ID=" << panel->mId << std::endl; + std::cerr << " texture size = " << panel->mTextureWidth << " x " << panel->mTextureHeight << std::endl; + std::cerr << " media size = " << panel->mMediaWidth << " x " << panel->mMediaHeight << std::endl; + std::cerr << " dirty rect = " << dirty_rect.mLeft << ", " << dirty_rect.mBottom << ", " << dirty_rect.mRight << ", " << dirty_rect.mTop << std::endl; + std::cerr << " texture width = " << panel->mMediaSource->getBitsWidth() << std::endl; + std::cerr << " format primary = 0x" << std::hex << panel->mMediaSource->getTextureFormatPrimary() << std::dec << std::endl; + std::cerr << " format type = 0x" << std::hex << panel->mMediaSource->getTextureFormatType() << std::dec << std::endl; + std::cerr << " pixels = " << (void*)pixels << std::endl; + } + + if(panel->mMediaSource->getTextureFormatSwapBytes()) + { + glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); + checkGLError("glPixelStorei"); + } + + panel->mMediaSource->resetDirty(); + + panel->mReadyToRender = true; + } + else + { + std::cerr << "dirty rect is outside current media size, skipping update" << std::endl; + } + }; + }; + }; + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // trigger re-display + glutPostRedisplay(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::windowPosToTexturePos( int window_x, int window_y, + int& media_x, int& media_y, + int& id ) +{ + if ( ! mSelectedPanel ) + { + media_x = 0; + media_y = 0; + id = 0; + return; + }; + + // record cursor poisiton for a readback next frame + mCurMouseX = window_x; + // OpenGL app == coordinate system this way + // NOTE: unrelated to settings in plugin - this + // is just for this app + mCurMouseY = mWindowHeight - window_y; + + // extract x (0..1023, y (0..1023) and id (0..15) from RGB components + unsigned long pixel_read_color_bits = ( mPixelReadColor[ 0 ] << 16 ) | ( mPixelReadColor[ 1 ] << 8 ) | mPixelReadColor[ 2 ]; + int texture_x = pixel_read_color_bits & 0x3ff; + int texture_y = ( pixel_read_color_bits >> 10 ) & 0x3ff; + id = ( pixel_read_color_bits >> 20 ) & 0x0f; + + // scale to size of media (1024 because we use 10 bits for X and Y from 24) + media_x = (int)( ( (float)mSelectedPanel->mMediaWidth * (float)texture_x ) / 1024.0f ); + media_y = (int)( ( (float)mSelectedPanel->mMediaHeight * (float)texture_y ) / 1024.0f ); + + // we assume the plugin uses an inverted coordinate scheme like OpenGL + // if not, the plugin code inverts the Y coordinate for us - we don't need to + media_y = mSelectedPanel->mMediaHeight - media_y; + + if ( media_x > 0 && media_y > 0 ) + { + //std::cout << " mouse coords: " << mCurMouseX << " x " << mCurMouseY << " and id = " << id << std::endl; + //std::cout << "raw texture coords: " << texture_x << " x " << texture_y << " and id = " << id << std::endl; + //std::cout << " media coords: " << media_x << " x " << media_y << " and id = " << id << std::endl; + //std::cout << std::endl; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::selectPanelById( int id ) +{ + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + if ( mMediaPanels[ panel ]->mId == id ) + { + selectPanel(mMediaPanels[ panel ]); + return; + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::selectPanel( mediaPanel* panel ) +{ + if( mSelectedPanel == panel ) + return; + + // turn off volume before we delete it + if( mSelectedPanel && mSelectedPanel->mMediaSource ) + { + mSelectedPanel->mMediaSource->setVolume( 0.0f ); + mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_LOW ); + }; + + mSelectedPanel = panel; + + if( mSelectedPanel && mSelectedPanel->mMediaSource ) + { + //mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f ); + mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_NORMAL ); + + if(!mSelectedPanel->mStartUrl.empty()) + { + mUrlEdit->set_text(const_cast<char*>(mSelectedPanel->mStartUrl.c_str()) ); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLFBConnectTest::findMediaPanel( LLPluginClassMedia* source ) +{ + mediaPanel *result = NULL; + + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + if ( mMediaPanels[ panel ]->mMediaSource == source ) + { + result = mMediaPanels[ panel ]; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLFBConnectTest::findMediaPanel( const std::string &target_name ) +{ + mediaPanel *result = NULL; + + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + if ( mMediaPanels[ panel ]->mTarget == target_name ) + { + result = mMediaPanels[ panel ]; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::navigateToNewURI( std::string uri ) +{ + if ( uri.length() ) + { + std::string mime_type = mimeTypeFromUrl( uri ); + + if ( !mSelectedPanel->mMediaSource->isPluginExited() && (mime_type == mSelectedPanel->mMimeType) ) + { + std::cout << "MIME type is the same" << std::endl; + mSelectedPanel->mMediaSource->loadURI( uri ); + mSelectedPanel->mMediaSource->start(); + mBookmarkList->do_selection( 0 ); + } + else + { + std::cout << "MIME type changed or plugin had exited" << std::endl; + replaceMediaPanel( mSelectedPanel, uri ); + mBookmarkList->do_selection( 0 ); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::initUrlHistory( std::string uris ) +{ + if ( uris.length() > 0 ) + { + std::cout << "init URL : " << uris << std::endl; + LLSD historySD; + + char *cstr, *p; + cstr = new char[uris.size()+1]; + strcpy(cstr, uris.c_str()); + const char *DELIMS = " ,;"; + p = strtok(cstr, DELIMS); + while (p != NULL) { + historySD.insert(0, p); + p = strtok(NULL, DELIMS); + } + mSelectedPanel->mMediaSource->initializeUrlHistory(historySD); + delete[] cstr; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::gluiCallback( int control_id ) +{ + if ( control_id == mIdBookmarks ) + { + std::string uri = mBookmarks[ mSelBookmark ].second; + + navigateToNewURI( uri ); + } + else + if ( control_id == mIdUrlEdit) + { + std::string uri = mUrlEdit->get_text(); + + navigateToNewURI( uri ); + } +/* + else + if ( control_id == mIdUrlInitHistoryEdit ) + { + std::string uri = mUrlInitHistoryEdit->get_text(); + + initUrlHistory( uri ); + } + else + if ( control_id == mIdControlAddPanel ) + { + addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + } + else + if ( control_id == mIdControlRemPanel ) + { + remMediaPanel( mSelectedPanel ); + } + else + if ( control_id == mIdDisableTimeout ) + { + // Set the "disable timeout" flag for all active plugins. + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + mMediaPanels[ i ]->mMediaSource->setDisableTimeout(mDisableTimeout); + } + } + else + if ( control_id == mIdUsePluginReadThread ) + { + LLPluginProcessParent::setUseReadThread(mUsePluginReadThread); + } + else + if ( control_id == mIdControlCrashPlugin ) + { + // send message to plugin and ask it to crash + // (switch out for ReleaseCandidate version :) ) + if(mSelectedPanel && mSelectedPanel->mMediaSource) + { + mSelectedPanel->mMediaSource->crashPlugin(); + } + } + else + if ( control_id == mIdControlHangPlugin ) + { + // send message to plugin and ask it to hang + // (switch out for ReleaseCandidate version :) ) + if(mSelectedPanel && mSelectedPanel->mMediaSource) + { + mSelectedPanel->mMediaSource->hangPlugin(); + } + } + else +*/ + if ( control_id == mIdControlExitApp ) + { + // text for exiting plugin system cleanly + delete this; // clean up + exit( 0 ); + } +/* + else + if ( control_id == mIdMediaTimeControlPlay ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlLoop ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( true ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlPause ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->pause(); + } + else + if ( control_id == mIdMediaTimeControlStop ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->stop(); + }; + } + else + if ( control_id == mIdMediaTimeControlSeek ) + { + if ( mSelectedPanel ) + { + // get value from spinner + float seconds_to_seek = mMediaTimeControlSeekSeconds; + mSelectedPanel->mMediaSource->seek( seconds_to_seek ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlRewind ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(-2.0f); + }; + } + else + if ( control_id == mIdMediaTimeControlFastForward ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(2.0f); + }; + } + else +*/ + if ( control_id == mIdMediaBrowserControlBack ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_back(); + } + else + if ( control_id == mIdMediaBrowserControlStop ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_stop(); + } + else + if ( control_id == mIdMediaBrowserControlForward ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_forward(); + } + else + if ( control_id == mIdMediaBrowserControlHome ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->loadURI( mHomeWebUrl ); + } + else + if ( control_id == mIdMediaBrowserControlReload ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_reload( true ); + } +/* + else + if ( control_id == mIdMediaBrowserControlClearCache ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->clear_cache(); + } + else + if ( control_id == mIdMediaBrowserControlClearCookies ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->clear_cookies(); + } + else + if ( control_id == mIdMediaBrowserControlEnableCookies ) + { + if ( mSelectedPanel ) + { + if ( mMediaBrowserControlEnableCookies ) + { + mSelectedPanel->mMediaSource->enable_cookies( true ); + } + else + { + mSelectedPanel->mMediaSource->enable_cookies( false ); + } + }; + }; +*/ +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::keyboard( int key ) +{ + //if ( key == 'a' || key == 'A' ) + // addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + //else + //if ( key == 'r' || key == 'R' ) + // remMediaPanel( mSelectedPanel ); + //else + //if ( key == 'd' || key == 'D' ) + // dumpPanelInfo(); + //else + if ( key == 27 ) + { + std::cout << "Application finished - exiting..." << std::endl; + delete this; + exit( 0 ); + }; + + mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_DOWN, key, 0 , LLSD()); + mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_UP, key, 0, LLSD()); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::mouseButton( int button, int state, int x, int y ) +{ + if ( button == GLUT_LEFT_BUTTON ) + { + if ( state == GLUT_DOWN ) + { + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_DOWN, 0, media_x, media_y, 0 ); + } + else + if ( state == GLUT_UP ) + { + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + // only select a panel if we're on a panel + // (HACK: strictly speaking this rules out clicking on + // the origin of a panel but that's very unlikely) + if ( media_x > 0 && media_y > 0 ) + { + selectPanelById( id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_UP, 0, media_x, media_y, 0 ); + }; + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::mousePassive( int x, int y ) +{ + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, 0, media_x, media_y, 0 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::mouseMove( int x, int y ) +{ + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, 0, media_x, media_y, 0 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::makeChrome() +{ + // IDs used by GLUI + int start_id = 0x1000; + + // right side window - geometry manipulators +#if __APPLE__ + // the Apple GLUT implementation doesn't seem to set the graphic offset of subwindows correctly when they overlap in certain ways. + // Use a separate controls window in this case. + // GLUI window at right containing manipulation controls and other buttons + int x = glutGet(GLUT_WINDOW_X) + glutGet(GLUT_WINDOW_WIDTH) + 4; + int y = glutGet(GLUT_WINDOW_Y); + GLUI* right_glui_window = GLUI_Master.create_glui( "", 0, x, y ); +#else + GLUI* right_glui_window = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_RIGHT ); +#endif + mViewRotationCtrl = right_glui_window->add_rotation( "Rotation", mViewRotation ); + mViewTranslationCtrl = right_glui_window->add_translation( "Translate", GLUI_TRANSLATION_XY, mViewPos ); + mViewTranslationCtrl->set_speed( 0.01f ); + mViewScaleCtrl = right_glui_window->add_translation( "Scale", GLUI_TRANSLATION_Z, &mViewPos[ 2 ] ); + mViewScaleCtrl->set_speed( 0.05f ); + right_glui_window->set_main_gfx_window( mAppWindow ); + + // right side window - app controls + /* + mIdControlAddPanel = start_id++; + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + right_glui_window->add_button( "Add panel", mIdControlAddPanel, gluiCallbackWrapper ); + right_glui_window->add_statictext( "" ); + mIdControlRemPanel = start_id++; + right_glui_window->add_button( "Rem panel", mIdControlRemPanel, gluiCallbackWrapper ); + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + mIdControlCrashPlugin = start_id++; + right_glui_window->add_button( "Crash plugin", mIdControlCrashPlugin, gluiCallbackWrapper ); + mIdControlHangPlugin = start_id++; + right_glui_window->add_button( "Hang plugin", mIdControlHangPlugin, gluiCallbackWrapper ); + */ + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + mIdControlExitApp = start_id++; + right_glui_window->add_button( "Exit app", mIdControlExitApp, gluiCallbackWrapper ); + + //// top window - holds bookmark UI + mIdBookmarks = start_id++; + mSelBookmark = 0; + GLUI* glui_window_top = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mBookmarkList = glui_window_top->add_listbox( "", &mSelBookmark, mIdBookmarks, gluiCallbackWrapper ); + // only add the first 50 bookmarks - list can be very long sometimes (30,000+) + // when testing list of media URLs from AGNI for example + for( unsigned int each = 0; each < mBookmarks.size() && each < 50; ++each ) + mBookmarkList->add_item( each, const_cast< char* >( mBookmarks[ each ].first.c_str() ) ); + glui_window_top->set_main_gfx_window( mAppWindow ); + + glui_window_top->add_column( false ); + mIdUrlEdit = start_id++; + mUrlEdit = glui_window_top->add_edittext( "Url:", GLUI_EDITTEXT_TEXT, 0, mIdUrlEdit, gluiCallbackWrapper ); + mUrlEdit->set_w( 600 ); + //GLUI* glui_window_top2 = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + //mIdUrlInitHistoryEdit = start_id++; + //mUrlInitHistoryEdit = glui_window_top2->add_edittext( "Init History (separate by commas or semicolons):", + // GLUI_EDITTEXT_TEXT, 0, mIdUrlInitHistoryEdit, gluiCallbackWrapper ); + //mUrlInitHistoryEdit->set_w( 800 ); + + // top window - media controls for "time" media types (e.g. movies) +/* + mGluiMediaTimeControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mGluiMediaTimeControlWindow->set_main_gfx_window( mAppWindow ); + mIdMediaTimeControlPlay = start_id++; + mGluiMediaTimeControlWindow->add_button( "PLAY", mIdMediaTimeControlPlay, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlLoop = start_id++; + mGluiMediaTimeControlWindow->add_button( "LOOP", mIdMediaTimeControlLoop, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlPause = start_id++; + mGluiMediaTimeControlWindow->add_button( "PAUSE", mIdMediaTimeControlPause, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + + GLUI_Button *button; + mIdMediaTimeControlRewind = start_id++; + button = mGluiMediaTimeControlWindow->add_button( "<<", mIdMediaTimeControlRewind, gluiCallbackWrapper ); + button->set_w(30); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlFastForward = start_id++; + button = mGluiMediaTimeControlWindow->add_button( ">>", mIdMediaTimeControlFastForward, gluiCallbackWrapper ); + button->set_w(30); + + mGluiMediaTimeControlWindow->add_column( true ); + + mIdMediaTimeControlStop = start_id++; + mGluiMediaTimeControlWindow->add_button( "STOP", mIdMediaTimeControlStop, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlVolume = start_id++; + GLUI_Spinner* spinner = mGluiMediaTimeControlWindow->add_spinner( "Volume", 2, &mMediaTimeControlVolume, mIdMediaTimeControlVolume, gluiCallbackWrapper); + spinner->set_float_limits( 0, 100 ); + mGluiMediaTimeControlWindow->add_column( true ); + mIdMediaTimeControlSeekSeconds = start_id++; + spinner = mGluiMediaTimeControlWindow->add_spinner( "", 2, &mMediaTimeControlSeekSeconds, mIdMediaTimeControlSeekSeconds, gluiCallbackWrapper); + spinner->set_float_limits( 0, 200 ); + spinner->set_w( 32 ); + spinner->set_speed( 0.025f ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlSeek = start_id++; + mGluiMediaTimeControlWindow->add_button( "SEEK", mIdMediaTimeControlSeek, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); +*/ + + // top window - media controls for "browser" media types (e.g. web browser) + mGluiMediaBrowserControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mGluiMediaBrowserControlWindow->set_main_gfx_window( mAppWindow ); + mIdMediaBrowserControlBack = start_id++; + mMediaBrowserControlBackButton = mGluiMediaBrowserControlWindow->add_button( "BACK", mIdMediaBrowserControlBack, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlStop = start_id++; + mGluiMediaBrowserControlWindow->add_button( "STOP", mIdMediaBrowserControlStop, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlForward = start_id++; + mMediaBrowserControlForwardButton = mGluiMediaBrowserControlWindow->add_button( "FORWARD", mIdMediaBrowserControlForward, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlHome = start_id++; + mGluiMediaBrowserControlWindow->add_button( "HOME", mIdMediaBrowserControlHome, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlReload = start_id++; + mGluiMediaBrowserControlWindow->add_button( "RELOAD", mIdMediaBrowserControlReload, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + /* + mIdMediaBrowserControlClearCache = start_id++; + mGluiMediaBrowserControlWindow->add_button( "CLEAR CACHE", mIdMediaBrowserControlClearCache, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlClearCookies = start_id++; + mGluiMediaBrowserControlWindow->add_button( "CLEAR COOKIES", mIdMediaBrowserControlClearCookies, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlEnableCookies = start_id++; + mMediaBrowserControlEnableCookies = 0; + mGluiMediaBrowserControlWindow->add_checkbox( "Enable Cookies", &mMediaBrowserControlEnableCookies, mIdMediaBrowserControlEnableCookies, gluiCallbackWrapper ); + + // top window - misc controls + GLUI* glui_window_misc_control = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mIdRandomPanelCount = start_id++; + mRandomPanelCount = 0; + glui_window_misc_control->add_checkbox( "Randomize panel count", &mRandomPanelCount, mIdRandomPanelCount, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); + mIdRandomBookmarks = start_id++; + mRandomBookmarks = 0; + glui_window_misc_control->add_checkbox( "Randomize bookmarks", &mRandomBookmarks, mIdRandomBookmarks, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); + + mIdDisableTimeout = start_id++; + mDisableTimeout = 0; + glui_window_misc_control->add_checkbox( "Disable plugin timeout", &mDisableTimeout, mIdDisableTimeout, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); + + mIdUsePluginReadThread = start_id++; + mUsePluginReadThread = 0; + glui_window_misc_control->add_checkbox( "Use plugin read thread", &mUsePluginReadThread, mIdUsePluginReadThread, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); + + mIdLargePanelSpacing = start_id++; + mLargePanelSpacing = 0; + glui_window_misc_control->add_checkbox( "Large Panel Spacing", &mLargePanelSpacing, mIdLargePanelSpacing, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); +*/ + // bottom window - status + mBottomGLUIWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_BOTTOM ); + mStatusText = mBottomGLUIWindow->add_statictext( "" ); + mBottomGLUIWindow->set_main_gfx_window( mAppWindow ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::resetView() +{ + mViewRotationCtrl->reset(); + + mViewScaleCtrl->set_x( 0.0f ); + mViewScaleCtrl->set_y( 0.0f ); + mViewScaleCtrl->set_z( 1.3f ); + + mViewTranslationCtrl->set_x( 0.0f ); + mViewTranslationCtrl->set_y( 0.0f ); + mViewTranslationCtrl->set_z( 0.0f ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels ) +{ + int pick_texture_width = 1024; + int pick_texture_height = 1024; + int pick_texture_depth = 3; + unsigned char* ptr = new unsigned char[ pick_texture_width * pick_texture_height * pick_texture_depth ]; + for( int y = 0; y < pick_texture_height; ++y ) + { + for( int x = 0; x < pick_texture_width * pick_texture_depth ; x += pick_texture_depth ) + { + unsigned long bits = 0L; + bits |= ( id << 20 ) | ( y << 10 ) | ( x / 3 ); + unsigned char r_component = ( bits >> 16 ) & 0xff; + unsigned char g_component = ( bits >> 8 ) & 0xff; + unsigned char b_component = bits & 0xff; + + ptr[ y * pick_texture_width * pick_texture_depth + x + 0 ] = r_component; + ptr[ y * pick_texture_width * pick_texture_depth + x + 1 ] = g_component; + ptr[ y * pick_texture_width * pick_texture_depth + x + 2 ] = b_component; + }; + }; + + glGenTextures( 1, texture_handle ); + + checkGLError("glGenTextures"); + std::cout << "glGenTextures returned " << *texture_handle << std::endl; + + bindTexture( *texture_handle ); + glTexImage2D( GL_TEXTURE_2D, 0, + GL_RGB, + pick_texture_width, pick_texture_height, + 0, GL_RGB, GL_UNSIGNED_BYTE, ptr ); + + *texture_pixels = ptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLFBConnectTest::mimeTypeFromUrl( std::string& url ) +{ + // default to web + std::string mime_type = "text/html"; + + // we may need a more advanced MIME type accessor later :-) + if ( url.find( ".mov" ) != std::string::npos ) // Movies + mime_type = "video/quicktime"; + else + if ( url.find( ".txt" ) != std::string::npos ) // Apple Text descriptors + mime_type = "video/quicktime"; + else + if ( url.find( ".mp3" ) != std::string::npos ) // Apple Text descriptors + mime_type = "video/quicktime"; + else + if ( url.find( "example://" ) != std::string::npos ) // Example plugin + mime_type = "example/example"; + + return mime_type; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLFBConnectTest::pluginNameFromMimeType( std::string& mime_type ) +{ +#if LL_DARWIN + std::string plugin_name( "media_plugin_null.dylib" ); + if ( mime_type == "video/quicktime" ) + plugin_name = "media_plugin_quicktime.dylib"; + else + if ( mime_type == "text/html" ) + plugin_name = "media_plugin_webkit.dylib"; + +#elif LL_WINDOWS + std::string plugin_name( "media_plugin_null.dll" ); + + if ( mime_type == "video/quicktime" ) + plugin_name = "media_plugin_quicktime.dll"; + else + if ( mime_type == "text/html" ) + plugin_name = "media_plugin_webkit.dll"; + else + if ( mime_type == "example/example" ) + plugin_name = "media_plugin_example.dll"; + +#elif LL_LINUX + std::string plugin_name( "libmedia_plugin_null.so" ); + + if ( mime_type == "video/quicktime" ) + plugin_name = "libmedia_plugin_quicktime.so"; + else + if ( mime_type == "text/html" ) + plugin_name = "libmedia_plugin_webkit.so"; +#endif + return plugin_name; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLFBConnectTest::addMediaPanel( std::string url ) +{ + // Get the plugin filename using the URL + std::string mime_type = mimeTypeFromUrl( url ); + std::string plugin_name = pluginNameFromMimeType( mime_type ); + + // create a random size for the new media + int media_width; + int media_height; + getRandomMediaSize( media_width, media_height, mime_type ); + media_width = 1024; + media_height = 1536; + + // make a new plugin + LLPluginClassMedia* media_source = new LLPluginClassMedia(this); + + // enable cookies so the FB login works + media_source->enable_cookies(true); + + // tell the plugin what size we asked for + media_source->setSize( media_width, media_height ); + + // Use the launcher start and initialize the plugin +#if LL_DARWIN || LL_LINUX + std::string launcher_name( "SLPlugin" ); +#elif LL_WINDOWS + std::string launcher_name( "SLPlugin.exe" ); +#endif + + // for this test app, use the cwd as the user data path (ugh). +#if LL_WINDOWS + std::string user_data_path = ".\\"; +#else + char cwd[ FILENAME_MAX ]; + if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) + { + std::cerr << "Couldn't get cwd - probably too long - failing to init." << std::endl; + return NULL; + } + std::string user_data_path = std::string( cwd ) + "/"; +#endif + media_source->setUserDataPath(user_data_path); + media_source->init( launcher_name, user_data_path, plugin_name, false ); + //media_source->setDisableTimeout(mDisableTimeout); + + // make a new panel and save parameters + mediaPanel* panel = new mediaPanel; + panel->mMediaSource = media_source; + panel->mStartUrl = url; + panel->mMimeType = mime_type; + panel->mMediaWidth = media_width; + panel->mMediaHeight = media_height; + panel->mTextureWidth = 0; + panel->mTextureHeight = 0; + panel->mTextureScaleX = 0; + panel->mTextureScaleY = 0; + panel->mMediaTextureHandle = 0; + panel->mPickTextureHandle = 0; + panel->mAppTextureCoordsOpenGL = false; // really need an 'undefined' state here too + panel->mReadyToRender = false; + + // look through current media panels to find an unused index number + bool id_exists = true; + for( int nid = 0; nid < mMaxPanels; ++nid ) + { + // does this id exist already? + id_exists = false; + for( int pid = 0; pid < (int)mMediaPanels.size(); ++pid ) + { + if ( nid == mMediaPanels[ pid ]->mId ) + { + id_exists = true; + break; + }; + }; + + // id wasn't found so we can use it + if ( ! id_exists ) + { + panel->mId = nid; + break; + }; + }; + + // if we get here and this flag is set, there is no room for any more panels + if ( id_exists ) + { + std::cout << "No room for any more panels" << std::endl; + } + else + { + // now we have the ID we can use it to make the + // pick texture (id is baked into texture pixels) + makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels ); + + // save this in the list of panels + mMediaPanels.push_back( panel ); + + // select the panel that was just created + selectPanel( panel ); + + // load and start the URL + panel->mMediaSource->loadURI( url ); + panel->mMediaSource->start(); + + std::cout << "Adding new media panel for " << url << "(" << media_width << "x" << media_height << ") with index " << panel->mId << " - total panels = " << mMediaPanels.size() << std::endl; + } + + return panel; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::updateMediaPanel( mediaPanel* panel ) +{ +// checkGLError("LLFBConnectTest::updateMediaPanel"); + + if ( ! panel ) + return; + + if(!panel->mMediaSource || !panel->mMediaSource->textureValid()) + { + panel->mReadyToRender = false; + return; + } + + // take a reference copy of the plugin values since they + // might change during this lifetime of this function + int plugin_media_width = panel->mMediaSource->getWidth(); + int plugin_media_height = panel->mMediaSource->getHeight(); + int plugin_texture_width = panel->mMediaSource->getBitsWidth(); + int plugin_texture_height = panel->mMediaSource->getBitsHeight(); + + // If the texture isn't created or the media or texture dimensions changed AND + // the sizes are valid then we need to delete the old media texture (if necessary) + // then make a new one. + if ((panel->mMediaTextureHandle == 0 || + panel->mMediaWidth != plugin_media_width || + panel->mMediaHeight != plugin_media_height || + panel->mTextureWidth != plugin_texture_width || + panel->mTextureHeight != plugin_texture_height) && + ( plugin_media_width > 0 && plugin_media_height > 0 && + plugin_texture_width > 0 && plugin_texture_height > 0 ) ) + { + std::cout << "Valid media size (" << plugin_media_width << " x " << plugin_media_height + << ") and texture size (" << plugin_texture_width << " x " << plugin_texture_height + << ") for panel with ID=" << panel->mId << " - making texture" << std::endl; + + // delete old GL texture + if ( isTexture( panel->mMediaTextureHandle ) ) + { + std::cerr << "updateMediaPanel: deleting texture " << panel->mMediaTextureHandle << std::endl; + glDeleteTextures( 1, &panel->mMediaTextureHandle ); + panel->mMediaTextureHandle = 0; + } + + std::cerr << "before: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl; + + // make a GL texture based on the dimensions the plugin told us + GLuint new_texture = 0; + glGenTextures( 1, &new_texture ); + + checkGLError("glGenTextures"); + + std::cout << "glGenTextures returned " << new_texture << std::endl; + + panel->mMediaTextureHandle = new_texture; + + bindTexture( panel->mMediaTextureHandle ); + + std::cout << "Setting texture size to " << plugin_texture_width << " x " << plugin_texture_height << std::endl; + glTexImage2D( GL_TEXTURE_2D, 0, + GL_RGB, + plugin_texture_width, plugin_texture_height, + 0, GL_RGB, GL_UNSIGNED_BYTE, + 0 ); + + + std::cerr << "after: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl; + }; + + // update our record of the media and texture dimensions + // NOTE: do this after we we check for sizes changes + panel->mMediaWidth = plugin_media_width; + panel->mMediaHeight = plugin_media_height; + panel->mTextureWidth = plugin_texture_width; + panel->mTextureHeight = plugin_texture_height; + if ( plugin_texture_width > 0 ) + { + panel->mTextureScaleX = (double)panel->mMediaWidth / (double)panel->mTextureWidth; + }; + if ( plugin_texture_height > 0 ) + { + panel->mTextureScaleY = (double)panel->mMediaHeight / (double)panel->mTextureHeight; + }; + + // update the flag which tells us if the media source uses OprnGL coords or not. + panel->mAppTextureCoordsOpenGL = panel->mMediaSource->getTextureCoordsOpenGL(); + + // Check to see if we have enough to render this panel. + // If we do, set a flag that the display functions use so + // they only render a panel with media if it's ready. + if ( panel->mMediaWidth < 0 || + panel->mMediaHeight < 0 || + panel->mTextureWidth < 1 || + panel->mTextureHeight < 1 || + panel->mMediaTextureHandle == 0 ) + { + panel->mReadyToRender = false; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLFBConnectTest::replaceMediaPanel( mediaPanel* panel, std::string url ) +{ + // no media panels so we can't change anything - have to add + if ( mMediaPanels.size() == 0 ) + return NULL; + + // sanity check + if ( ! panel ) + return NULL; + + int index; + for(index = 0; index < (int)mMediaPanels.size(); index++) + { + if(mMediaPanels[index] == panel) + break; + } + + if(index >= (int)mMediaPanels.size()) + { + // panel isn't in mMediaPanels + return NULL; + } + + std::cout << "Replacing media panel with index " << panel->mId << std::endl; + + int panel_id = panel->mId; + + if(mSelectedPanel == panel) + mSelectedPanel = NULL; + + delete panel; + + // Get the plugin filename using the URL + std::string mime_type = mimeTypeFromUrl( url ); + std::string plugin_name = pluginNameFromMimeType( mime_type ); + + // create a random size for the new media + int media_width; + int media_height; + getRandomMediaSize( media_width, media_height, mime_type ); + + // make a new plugin + LLPluginClassMedia* media_source = new LLPluginClassMedia(this); + + // tell the plugin what size we asked for + media_source->setSize( media_width, media_height ); + + // Use the launcher start and initialize the plugin +#if LL_DARWIN || LL_LINUX + std::string launcher_name( "SLPlugin" ); +#elif LL_WINDOWS + std::string launcher_name( "SLPlugin.exe" ); +#endif + + // for this test app, use the cwd as the user data path (ugh). +#if LL_WINDOWS + std::string user_data_path = ".\\"; +#else + char cwd[ FILENAME_MAX ]; + if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) + { + std::cerr << "Couldn't get cwd - probably too long - failing to init." << std::endl; + return NULL; + } + std::string user_data_path = std::string( cwd ) + "/"; +#endif + + media_source->setUserDataPath(user_data_path); + media_source->init( launcher_name, user_data_path, plugin_name, false ); + //media_source->setDisableTimeout(mDisableTimeout); + + // make a new panel and save parameters + panel = new mediaPanel; + panel->mMediaSource = media_source; + panel->mStartUrl = url; + panel->mMimeType = mime_type; + panel->mMediaWidth = media_width; + panel->mMediaHeight = media_height; + panel->mTextureWidth = 0; + panel->mTextureHeight = 0; + panel->mTextureScaleX = 0; + panel->mTextureScaleY = 0; + panel->mMediaTextureHandle = 0; + panel->mPickTextureHandle = 0; + panel->mAppTextureCoordsOpenGL = false; // really need an 'undefined' state here too + panel->mReadyToRender = false; + + panel->mId = panel_id; + + // Replace the entry in the panels array + mMediaPanels[index] = panel; + + // now we have the ID we can use it to make the + // pick texture (id is baked into texture pixels) + makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels ); + + // select the panel that was just created + selectPanel( panel ); + + // load and start the URL + panel->mMediaSource->loadURI( url ); + panel->mMediaSource->start(); + + return panel; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::getRandomMediaSize( int& width, int& height, std::string mime_type ) +{ + // Make a new media source with a random size which we'll either + // directly or the media plugin will tell us what it wants later. + // Use a random size so we can test support for weird media sizes. + // (Almost everything else will get filled in later once the + // plugin responds) + // NB. Do we need to enforce that width is on 4 pixel boundary? + width = ( ( rand() % 170 ) + 30 ) * 4; + height = ( ( rand() % 170 ) + 30 ) * 4; + + // adjust this random size if it's a browser so we get + // a more useful size for testing.. + if ( mime_type == "text/html" || mime_type == "example/example" ) + { + width = ( ( rand() % 100 ) + 100 ) * 4; + height = ( width * ( ( rand() % 400 ) + 1000 ) ) / 1000; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::remMediaPanel( mediaPanel* panel ) +{ + // always leave one panel + if ( mMediaPanels.size() == 1 ) + return; + + // sanity check - don't think this can happen but see above for a case where it might... + if ( ! panel ) + return; + + std::cout << "Removing media panel with index " << panel->mId << " - total panels = " << mMediaPanels.size() - 1 << std::endl; + + if(mSelectedPanel == panel) + mSelectedPanel = NULL; + + delete panel; + + // remove from storage list + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + if ( mMediaPanels[ i ] == panel ) + { + mMediaPanels.erase( mMediaPanels.begin() + i ); + break; + }; + }; + + // select the first panel + selectPanel( mMediaPanels[ 0 ] ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::updateStatusBar() +{ + if ( ! mSelectedPanel ) + return; + + // cache results - this is a very slow function + static int cached_id = -1; + static int cached_media_width = -1; + static int cached_media_height = -1; + static int cached_texture_width = -1; + static int cached_texture_height = -1; + static bool cached_supports_browser_media = true; + static bool cached_supports_time_media = false; + static int cached_movie_time = -1; + static GLfloat cached_distance = -1.0f; + + static std::string cached_plugin_version = ""; + if ( + cached_id == mSelectedPanel->mId && + cached_media_width == mSelectedPanel->mMediaWidth && + cached_media_height == mSelectedPanel->mMediaHeight && + cached_texture_width == mSelectedPanel->mTextureWidth && + cached_texture_height == mSelectedPanel->mTextureHeight && + cached_supports_browser_media == mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() && + cached_supports_time_media == mSelectedPanel->mMediaSource->pluginSupportsMediaTime() && + cached_plugin_version == mSelectedPanel->mMediaSource->getPluginVersion() && + cached_movie_time == (int)mSelectedPanel->mMediaSource->getCurrentTime() && + cached_distance == mDistanceCameraToSelectedGeometry + ) + { + // nothing changed so don't spend time here + return; + }; + + std::ostringstream stream( "" ); + + stream.str( "" ); + stream.clear(); + + stream << "Id: "; + stream << std::setw( 2 ) << std::setfill( '0' ); + stream << mSelectedPanel->mId; + stream << " | "; + stream << "Media: "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << mSelectedPanel->mMediaWidth; + stream << " x "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << mSelectedPanel->mMediaHeight; + stream << " | "; + stream << "Texture: "; + stream << std::setw( 4 ) << std::setfill( '0' ); + stream << mSelectedPanel->mTextureWidth; + stream << " x "; + stream << std::setw( 4 ) << std::setfill( '0' ); + stream << mSelectedPanel->mTextureHeight; + + stream << " | "; + stream << "Distance: "; + stream << std::setw( 6 ); + stream << std::setprecision( 3 ); + stream << std::setprecision( 3 ); + stream << mDistanceCameraToSelectedGeometry; + stream << " | "; + + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() ) + stream << "BROWSER"; + else + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + stream << "TIME "; + stream << " | "; + stream << mSelectedPanel->mMediaSource->getPluginVersion(); + stream << " | "; + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + { + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << (int)mSelectedPanel->mMediaSource->getCurrentTime(); + stream << " / "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << (int)mSelectedPanel->mMediaSource->getDuration(); + stream << " @ "; + stream << (int)mSelectedPanel->mMediaSource->getCurrentPlayRate(); + stream << " | "; + }; + + glutSetWindow( mBottomGLUIWindow->get_glut_window_id() ); + mStatusText->set_text( const_cast< char*>( stream.str().c_str() ) ); + glutSetWindow( mAppWindow ); + + // caching + cached_id = mSelectedPanel->mId; + cached_media_width = mSelectedPanel->mMediaWidth; + cached_media_height = mSelectedPanel->mMediaHeight; + cached_texture_width = mSelectedPanel->mTextureWidth; + cached_texture_height = mSelectedPanel->mTextureHeight; + cached_supports_browser_media = mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser(); + cached_supports_time_media = mSelectedPanel->mMediaSource->pluginSupportsMediaTime(); + cached_plugin_version = mSelectedPanel->mMediaSource->getPluginVersion(); + cached_movie_time = (int)mSelectedPanel->mMediaSource->getCurrentTime(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::dumpPanelInfo() +{ + std::cout << std::endl << "===== Media Panels =====" << std::endl; + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + std::cout << std::setw( 2 ) << std::setfill( '0' ); + std::cout << i + 1 << "> "; + std::cout << "Id: "; + std::cout << std::setw( 2 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mId; + std::cout << " | "; + std::cout << "Media: "; + std::cout << std::setw( 3 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mMediaWidth; + std::cout << " x "; + std::cout << std::setw( 3 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mMediaHeight; + std::cout << " | "; + std::cout << "Texture: "; + std::cout << std::setw( 4 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mTextureWidth; + std::cout << " x "; + std::cout << std::setw( 4 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mTextureHeight; + std::cout << " | "; + if ( mMediaPanels[ i ] == mSelectedPanel ) + std::cout << "(selected)"; + + std::cout << std::endl; + }; + std::cout << "========================" << std::endl; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + // Uncomment this to make things much, much quieter. +// return; + + switch(event) + { + case MEDIA_EVENT_CONTENT_UPDATED: + // too spammy -- don't log these +// std::cerr << "Media event: MEDIA_EVENT_CONTENT_UPDATED " << std::endl; + break; + + case MEDIA_EVENT_TIME_DURATION_UPDATED: + // too spammy -- don't log these +// std::cerr << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << std::endl; + break; + + case MEDIA_EVENT_SIZE_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_SIZE_CHANGED " << std::endl; + break; + + case MEDIA_EVENT_CURSOR_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << std::endl; + break; + + case MEDIA_EVENT_NAVIGATE_BEGIN: + std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN " << std::endl; + break; + + case MEDIA_EVENT_NAVIGATE_COMPLETE: + std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << std::endl; + break; + + case MEDIA_EVENT_PROGRESS_UPDATED: + std::cerr << "Media event: MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << std::endl; + break; + + case MEDIA_EVENT_STATUS_TEXT_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << std::endl; + break; + + case MEDIA_EVENT_NAME_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_NAME_CHANGED, new name is: " << self->getMediaName() << std::endl; + glutSetWindowTitle( self->getMediaName().c_str() ); + break; + + case MEDIA_EVENT_LOCATION_CHANGED: + { + std::cerr << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << std::endl; + mediaPanel* panel = findMediaPanel(self); + if(panel != NULL) + { + panel->mStartUrl = self->getLocation(); + if(panel == mSelectedPanel) + { + mUrlEdit->set_text(const_cast<char*>(panel->mStartUrl.c_str()) ); + } + } + } + break; + + case MEDIA_EVENT_NAVIGATE_ERROR_PAGE: + std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_ERROR_PAGE, uri is: " << self->getClickURL() << std::endl; + break; + + case MEDIA_EVENT_CLICK_LINK_HREF: + { + std::cerr << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, uri is " << self->getClickURL() << ", target is " << self->getClickTarget() << std::endl; + // retrieve the event parameters + std::string url = self->getClickURL(); + std::string target = self->getClickTarget(); + + if(target == "_external") + { + // this should open in an external browser, but since this is a test app we don't care. + } + else if(target == "_blank") + { + // Create a new panel with the specified URL. + addMediaPanel(url); + } + else // other named target + { + mediaPanel *target_panel = findMediaPanel(target); + if(target_panel) + { + target_panel = replaceMediaPanel(target_panel, url); + } + else + { + target_panel = addMediaPanel(url); + } + + if(target_panel) + { + target_panel->mTarget = target; + } + } + } + break; + + case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: + std::cerr << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << std::endl; + break; + + case MEDIA_EVENT_PLUGIN_FAILED: + std::cerr << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << std::endl; + break; + + case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH: + std::cerr << "Media event: MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << std::endl; + break; + + case MEDIA_EVENT_CLOSE_REQUEST: + std::cerr << "Media event: MEDIA_EVENT_CLOSE_REQUEST" << std::endl; + break; + + case MEDIA_EVENT_PICK_FILE_REQUEST: + std::cerr << "Media event: MEDIA_EVENT_PICK_FILE_REQUEST" << std::endl; + // TODO: display an actual file picker + self->sendPickFileResponse("cake"); + break; + + case MEDIA_EVENT_GEOMETRY_CHANGE: + std::cerr << "Media event: MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID() + << ", x = " << self->getGeometryX() + << ", y = " << self->getGeometryY() + << ", width = " << self->getGeometryWidth() + << ", height = " << self->getGeometryHeight() + << std::endl; + break; + + case MEDIA_EVENT_AUTH_REQUEST: + { + //std::cerr << "Media event: MEDIA_EVENT_AUTH_REQUEST, url " << self->getAuthURL() ", realm " << self->getAuthRealm() << std::endl; + + // TODO: display an auth dialog + self->sendAuthResponse(false, "", ""); + } + break; + + case MEDIA_EVENT_LINK_HOVERED: + { + std::cerr << "Media event: MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << std::endl; + } + break; + + default: + { + std::cerr << "Media event: <unknown>, code is: " << int(event) << std::endl; + } + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +static void gluiCallbackWrapper( int control_id ) +{ + if ( gApplication ) + gApplication->gluiCallback( control_id ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutReshape( int width, int height ) +{ + if ( gApplication ) + gApplication->reshape( width, height ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutDisplay() +{ + if ( gApplication ) + gApplication->display(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutIdle(int update_ms) +{ + GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms); + + if ( gApplication ) + gApplication->idle(); + +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutKeyboard( unsigned char key, int x, int y ) +{ + if ( gApplication ) + gApplication->keyboard( key ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMousePassive( int x, int y ) +{ + if ( gApplication ) + gApplication->mousePassive( x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseMove( int x , int y ) +{ + if ( gApplication ) + gApplication->mouseMove( x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseButton( int button, int state, int x, int y ) +{ + if ( gApplication ) + gApplication->mouseButton( button, state, x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int main( int argc, char* argv[] ) +{ +#if LL_DARWIN + // Set the current working directory to <application bundle>/Contents/Resources/ + CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()); + if(resources_url != NULL) + { + CFStringRef resources_string = CFURLCopyFileSystemPath(resources_url, kCFURLPOSIXPathStyle); + CFRelease(resources_url); + if(resources_string != NULL) + { + char buffer[PATH_MAX] = ""; + if(CFStringGetCString(resources_string, buffer, sizeof(buffer), kCFStringEncodingUTF8)) + { + chdir(buffer); + } + CFRelease(resources_string); + } + } +#endif + + glutInit( &argc, argv ); + glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB ); + + const int app_window_x = 80; + const int app_window_y = 0; + const int app_window_width = 960; + const int app_window_height = 960; + + glutInitWindowPosition( app_window_x, app_window_y ); + glutInitWindowSize( app_window_width, app_window_height ); + + int app_window_handle = glutCreateWindow( "LLFBConnectTest" ); + + glutDisplayFunc( glutDisplay ); + + GLUI_Master.set_glutReshapeFunc( glutReshape ); + GLUI_Master.set_glutKeyboardFunc( glutKeyboard ); + GLUI_Master.set_glutMouseFunc( glutMouseButton ); + + glutPassiveMotionFunc( glutMousePassive ); + glutMotionFunc( glutMouseMove ); + + glutSetWindow( app_window_handle ); + + gApplication = new LLFBConnectTest( app_window_handle, app_window_width, app_window_height ); + + // update at approximately 60hz + int update_ms = 1000 / 60; + + GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms); + + glutMainLoop(); + + delete gApplication; +} diff --git a/indra/test_apps/llfbconnecttest/llfbconnecttest.h b/indra/test_apps/llfbconnecttest/llfbconnecttest.h new file mode 100644 index 0000000000..6f442a55b3 --- /dev/null +++ b/indra/test_apps/llfbconnecttest/llfbconnecttest.h @@ -0,0 +1,207 @@ +/** + * @file LLFBConnectTest.cpp + * @brief Facebook Connect Test App + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_FB_CONNECT_H +#define LL_FB_CONNECT_H + +#include <vector> +#include <string> +#include "llpluginclassmedia.h" +#include "llgl.h" + +// Forward declarations +class GLUI_Rotation; +class GLUI_Translation; +class GLUI_Listbox; +class GLUI_EditText; +class GLUI_StaticText; +class GLUI; +class GLUI_Button; + +//////////////////////////////////////////////////////////////////////////////// +// +struct mediaPanel +{ + public: + mediaPanel(); + ~mediaPanel(); + int mId; + std::string mStartUrl; + std::string mMimeType; + std::string mTarget; + LLPluginClassMedia *mMediaSource; + int mMediaWidth; + int mMediaHeight; + int mTextureWidth; + int mTextureHeight; + double mTextureScaleX; + double mTextureScaleY; + GLuint mMediaTextureHandle; + GLuint mPickTextureHandle; + unsigned char* mPickTexturePixels; + bool mAppTextureCoordsOpenGL; + bool mReadyToRender; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +class LLFBConnectTest : public LLPluginClassMediaOwner +{ + public: + LLFBConnectTest( int app_window, int window_width, int window_height ); + ~LLFBConnectTest(); + + void reshape( int width, int height ); + void display(); + void idle(); + void gluiCallback( int control_id ); + void keyboard( int key ); + void mousePassive( int x, int y ); + void mouseButton( int button, int state, int x, int y ); + void mouseMove( int x, int y ); + + void bindTexture(GLuint texture, GLint row_length = 0, GLint alignment = 1); + bool checkGLError(const char *name = "OpenGL"); + void drawGeometry( int panel, bool selected ); + void startPanelHighlight( float red, float green, float blue, float line_width ); + void endPanelHighlight(); + enum { DrawTypePickTexture, DrawTypeMediaTexture }; + void draw( int draw_type ); + void windowPosToTexturePos( int window_x, int window_y, int& media_x, int& media_y, int& id ); + + mediaPanel* addMediaPanel( std::string url ); + void updateMediaPanel( mediaPanel* panel ); + void remMediaPanel( mediaPanel* panel ); + mediaPanel* replaceMediaPanel( mediaPanel* panel, std::string url ); + void getRandomMediaSize( int& width, int& height, std::string mime_type ); + void navigateToNewURI( std::string uri ); + void initUrlHistory( std::string uri ); + void selectPanelById( int id ); + void selectPanel( mediaPanel* panel ); + mediaPanel* findMediaPanel( LLPluginClassMedia* panel ); + mediaPanel* findMediaPanel( const std::string &target_name ); + void makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels ); + void makeChrome(); + void resetView(); + + void dumpPanelInfo(); + void updateStatusBar(); + + GLfloat distanceToCamera( GLfloat point_x, GLfloat point_y, GLfloat point_z ); + + + // Inherited from LLPluginClassMediaOwner + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent); + + private: + const int mVersionMajor; + const int mVersionMinor; + const int mVersionPatch; + const int mMaxPanels; + int mAppWindow; + int mWindowWidth; + int mWindowHeight; + int mCurMouseX; + int mCurMouseY; + unsigned char mPixelReadColor[ 3 ]; + bool mFuzzyMedia; + const std::string mHomeWebUrl; + + std::vector< mediaPanel* > mMediaPanels; + mediaPanel* mSelectedPanel; + std::string mimeTypeFromUrl( std::string& url ); + std::string pluginNameFromMimeType( std::string& mime_type ); + + GLUI_Rotation* mViewRotationCtrl; + GLUI_Translation* mViewScaleCtrl; + GLUI_Translation* mViewTranslationCtrl; + float mViewportAspect; + float mViewPos[ 3 ]; + float mViewRotation[ 16 ]; + + float mDistanceCameraToSelectedGeometry; + + int mIdControlAddPanel; + int mIdControlRemPanel; + + std::vector< std::pair< std::string, std::string > > mBookmarks; + GLUI_Listbox* mBookmarkList; + int mIdBookmarks; + int mIdUrlEdit; + GLUI_EditText* mUrlEdit; + //int mIdUrlInitHistoryEdit; + //GLUI_EditText* mUrlInitHistoryEdit; + int mSelBookmark; + //int mIdRandomPanelCount; + //int mRandomPanelCount; + //int mIdRandomBookmarks; + //int mRandomBookmarks; + //int mIdDisableTimeout; + //int mDisableTimeout; + //int mIdUsePluginReadThread; + //int mUsePluginReadThread; + //int mIdLargePanelSpacing; + //int mLargePanelSpacing; + //int mIdControlCrashPlugin; + //int mIdControlHangPlugin; + int mIdControlExitApp; + + //GLUI* mGluiMediaTimeControlWindow; + //int mIdMediaTimeControlPlay; + //int mIdMediaTimeControlLoop; + //int mIdMediaTimeControlPause; + //int mIdMediaTimeControlStop; + //int mIdMediaTimeControlSeek; + //int mIdMediaTimeControlVolume; + //int mMediaTimeControlVolume; + //int mIdMediaTimeControlSeekSeconds; + //int mMediaTimeControlSeekSeconds; + //int mIdMediaTimeControlRewind; + //int mIdMediaTimeControlFastForward; + + GLUI* mGluiMediaBrowserControlWindow; + int mIdMediaBrowserControlBack; + GLUI_Button* mMediaBrowserControlBackButton; + int mIdMediaBrowserControlStop; + int mIdMediaBrowserControlForward; + GLUI_Button* mMediaBrowserControlForwardButton; + bool mGluiMediaTimeControlWindowFlag; + bool mGluiMediaBrowserControlWindowFlag; + bool mMediaBrowserControlBackButtonFlag; + bool mMediaBrowserControlForwardButtonFlag; + int mIdMediaBrowserControlHome; + int mIdMediaBrowserControlReload; + int mIdMediaBrowserControlClearCache; + int mIdMediaBrowserControlClearCookies; + int mIdMediaBrowserControlEnableCookies; + int mMediaBrowserControlEnableCookies; + + GLUI* mBottomGLUIWindow; + GLUI_StaticText* mStatusText; +}; + +#endif // LL_FB_CONNECT_H + |