diff options
Diffstat (limited to 'indra/newview')
62 files changed, 3652 insertions, 412 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 227644f14f..477600ca01 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -188,6 +188,7 @@ set(viewer_SOURCE_FILES llexpandabletextbox.cpp llexternaleditor.cpp llface.cpp + llfacebookconnect.cpp llfasttimerview.cpp llfavoritesbar.cpp llfeaturemanager.cpp @@ -461,6 +462,9 @@ set(viewer_SOURCE_FILES llpathfindingobjectlist.cpp llpathfindingpathtool.cpp llpersistentnotificationstorage.cpp + llpersonfolderview.cpp + llpersonmodelcommon.cpp + llpersontabview.cpp llphysicsmotion.cpp llphysicsshapebuilderutil.cpp llpipelinelistener.cpp @@ -503,6 +507,7 @@ set(viewer_SOURCE_FILES llsidetraypanelcontainer.cpp llsky.cpp llslurl.cpp + llsociallist.cpp llspatialpartition.cpp llspeakers.cpp llspeakingindicatormanager.cpp @@ -769,6 +774,7 @@ set(viewer_HEADER_FILES llexpandabletextbox.h llexternaleditor.h llface.h + llfacebookconnect.h llfasttimerview.h llfavoritesbar.h llfeaturemanager.h @@ -1030,6 +1036,9 @@ set(viewer_HEADER_FILES llpathfindingobjectlist.h llpathfindingpathtool.h llpersistentnotificationstorage.h + llpersonfolderview.h + llpersonmodelcommon.h + llpersontabview.h llphysicsmotion.h llphysicsshapebuilderutil.h llpipelinelistener.h @@ -1073,6 +1082,7 @@ set(viewer_HEADER_FILES llsidetraypanelcontainer.h llsky.h llslurl.h + llsociallist.h llspatialpartition.h llspeakers.h llspeakingindicatormanager.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index f356cff9d8..5019f632be 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3468,16 +3468,27 @@ <key>Value</key> <real>10.0</real> </map> - <key>FilterItemsPerFrame</key> + <key>FilterItemsMaxTimePerFrameVisible</key> <map> - <key>Comment</key> - <string>Maximum number of inventory items to match against search filter every frame (lower to increase framerate while searching, higher to improve search speed)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>500</integer> + <key>Comment</key> + <string>Max time devoted to items filtering per frame for visible inventory listings (in milliseconds)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>10</integer> + </map> + <key>FilterItemsMaxTimePerFrameUnvisible</key> + <map> + <key>Comment</key> + <string>Max time devoted to items filtering per frame for non visible inventory listings (in milliseconds)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>1</integer> </map> <key>FindLandArea</key> <map> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 8c42defa73..d16945070a 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -46,6 +46,7 @@ #include "llenvmanager.h" #include "llfirstuse.h" #include "llfloatercamera.h" +#include "llfloaterimcontainer.h" #include "llfloaterreg.h" #include "llfloatertools.h" #include "llgroupactions.h" @@ -91,6 +92,7 @@ #include "llworld.h" #include "llworldmap.h" #include "stringize.h" +#include "boost/foreach.hpp" using namespace LLAvatarAppearanceDefines; @@ -2037,7 +2039,16 @@ void LLAgent::endAnimationUpdateUI() { skip_list.insert(LLFloaterReg::findInstance("mini_map")); } - + + LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); + LLFloaterIMContainer::floater_list_t conversations; + im_box->getDetachedConversationFloaters(conversations); + BOOST_FOREACH(LLFloater* conversation, conversations) + { + llinfos << "skip_list.insert(session_floater): " << conversation->getTitle() << llendl; + skip_list.insert(conversation); + } + gFloaterView->popVisibleAll(skip_list); #endif mViewsPushed = FALSE; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index fdc2cdb78d..dd52050ed4 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2634,20 +2634,38 @@ 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 option_slurl; + if(clp.hasOption("url")) { - LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); + option_slurl = LLSLURL(clp.getOption("url")[0]); + LLStartUp::setStartSLURL(option_slurl); if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) { LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); - - } + } } else if(clp.hasOption("slurl")) { - LLSLURL start_slurl(clp.getOption("slurl")[0]); - LLStartUp::setStartSLURL(start_slurl); + option_slurl = LLSLURL(clp.getOption("slurl")[0]); + LLStartUp::setStartSLURL(option_slurl); } + + //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 (option_slurl.isValid() && + (gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) + { + if (sendURLToOtherInstance(option_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()) diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index c74ce24872..6e95df8383 100755 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -386,6 +386,10 @@ void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags) addVoiceOptions(items); items.push_back(std::string("chat_history")); } + else if(this->getType() == CONV_SESSION_NEARBY) + { + items.push_back(std::string("chat_history")); + } hide_context_entries(menu, items, disabled_items); } diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index 8766585049..d8cdcdfc97 100755 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -252,11 +252,10 @@ public: const std::string& getName() const { return mEmpty; } const std::string& getFilterText() { return mEmpty; } void setModified(EFilterModified behavior = FILTER_RESTART) { } - - void setFilterCount(S32 count) { } - S32 getFilterCount() const { return 0; } - void decrementFilterCount() { } - + + void resetTime(S32 timeout) { } + bool isTimedOut() { return false; } + bool isDefault() const { return true; } bool isNotDefault() const { return false; } void markDefault() { } diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index b6c53e5e30..42104ea20a 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -118,6 +118,13 @@ void LLConversationViewSession::setFlashState(bool flash_state) mFlashTimer->stopFlashing(); } +void LLConversationViewSession::setHighlightState(bool hihglight_state) +{ + mFlashStateOn = hihglight_state; + mFlashStarted = true; + mFlashTimer->stopFlashing(); +} + void LLConversationViewSession::startFlashing() { if (isInVisibleChain() && mFlashStateOn && !mFlashStarted) @@ -340,16 +347,20 @@ void LLConversationViewSession::setVisibleIfDetached(BOOL visible) { // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here - LLFolderViewModelItem* item = mViewModelItem; - LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID(); - LLFloater* session_floater = LLFloaterIMSessionTab::getConversation(session_uuid); - - if (session_floater && !session_floater->getHost() && !session_floater->isMinimized()) + LLFloater* session_floater = getSessionFloater(); + if (session_floater && session_floater->isDetachedAndNotMinimized()) { session_floater->setVisible(visible); } } +LLFloater* LLConversationViewSession::getSessionFloater() +{ + LLFolderViewModelItem* item = mViewModelItem; + LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID(); + return LLFloaterIMSessionTab::getConversation(session_uuid); +} + LLConversationViewParticipant* LLConversationViewSession::findParticipant(const LLUUID& participant_id) { // This is *not* a general tree parsing algorithm. We search only in the mItems list diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index 3eb2e63792..879d496dc7 100755 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -86,6 +86,9 @@ public: virtual void refresh(); /*virtual*/ void setFlashState(bool flash_state); + void setHighlightState(bool hihglight_state); + + LLFloater* getSessionFloater(); private: diff --git a/indra/newview/lldonotdisturbnotificationstorage.cpp b/indra/newview/lldonotdisturbnotificationstorage.cpp index 82affcf068..71bc4f15d2 100755 --- a/indra/newview/lldonotdisturbnotificationstorage.cpp +++ b/indra/newview/lldonotdisturbnotificationstorage.cpp @@ -115,7 +115,8 @@ void LLDoNotDisturbNotificationStorage::saveNotifications() { LLNotificationPtr notificationPtr = historyIter->second; - if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() && !notificationPtr->isExpired()) + if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() && + !notificationPtr->isExpired() && !notificationPtr->isPersistent()) { data.append(notificationPtr->asLLSD(true)); } @@ -210,12 +211,8 @@ void LLDoNotDisturbNotificationStorage::loadNotifications() } - if(imToastExists) - { - LLFloaterReg::showInstance("im_container"); - } - - if(group_ad_hoc_toast_exists) + bool isConversationLoggingAllowed = gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0; + if(group_ad_hoc_toast_exists && isConversationLoggingAllowed) { LLFloaterReg::showInstance("conversation"); } @@ -266,11 +263,6 @@ void LLDoNotDisturbNotificationStorage::updateNotifications() } } - if(imToastExists) - { - LLFloaterReg::showInstance("im_container"); - } - if(imToastExists || offerExists) { make_ui_sound("UISndNewIncomingIMSession"); diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp new file mode 100644 index 0000000000..86da748f0f --- /dev/null +++ b/indra/newview/llfacebookconnect.cpp @@ -0,0 +1,322 @@ +/** + * @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 "llurlaction.h" + +/////////////////////////////////////////////////////////////////////////////// +// + +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") + { + if (query_map.has("code")) + { + LLFacebookConnect::instance().connectToFacebook(query_map["code"]); + } + return true; + } + } + return false; + } +}; +LLFacebookConnectHandler gFacebookConnectHandler; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookConnectResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookConnectResponder); +public: + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + LL_DEBUGS("FacebookConnect") << "Connect successful. content: " << content << LL_ENDL; + + // Grab some graph data now that we are connected + LLFacebookConnect::instance().setConnected(true); + LLFacebookConnect::instance().loadFacebookFriends(); + } + else + { + LL_WARNS("FacebookConnect") << "Failed to get a response. reason: " << reason << " status: " << status << LL_ENDL; + } + } + + void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + if (status == 302) + { + LLFacebookConnect::instance().openFacebookWeb(content["location"]); + } + } + +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookPostResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookPostResponder); +public: + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + LL_DEBUGS("FacebookConnect") << "Post successful. content: " << content << LL_ENDL; + } + else + { + LL_WARNS("FacebookConnect") << "Failed to get a post response. reason: " << reason << " status: " << status << LL_ENDL; + } + } + + 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: + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + LL_DEBUGS("FacebookConnect") << "Disconnect successful. content: " << content << LL_ENDL; + + // Clear all facebook stuff + LLFacebookConnect::instance().setConnected(false); + LLFacebookConnect::instance().clearContent(); + } + else + { + LL_WARNS("FacebookConnect") << "Failed to get a response. reason: " << reason << " status: " << status << LL_ENDL; + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +class LLFacebookConnectedResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLFacebookConnectedResponder); +public: + + LLFacebookConnectedResponder(bool show_login_if_not_connected) : mShowLoginIfNotConnected(show_login_if_not_connected) {} + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + if (isGoodStatus(status)) + { + LL_DEBUGS("FacebookConnect") << "Connect successful. content: " << content << LL_ENDL; + + // Grab some graph data if already connected + LLFacebookConnect::instance().setConnected(true); + LLFacebookConnect::instance().loadFacebookFriends(); + } + else + { + LL_WARNS("FacebookConnect") << "Failed to get a response. reason: " << reason << " status: " << status << LL_ENDL; + + // show the facebook login page if not connected yet + if ((status == 404) && mShowLoginIfNotConnected) + { + LLFacebookConnect::instance().connectToFacebook(); + } + } + } + +private: + bool mShowLoginIfNotConnected; +}; + +/////////////////////////////////////////////////////////////////////////////// +// +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 + { + LL_WARNS("FacebookConnect") << "Failed to get a response. reason: " << reason << " status: " << status << LL_ENDL; + } + } + + void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + if (status == 302) + { + LLFacebookConnect::instance().openFacebookWeb(content["location"]); + } + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// +LLFacebookConnect::LLFacebookConnect() +: mConnectedToFbc(false), + mContent(), + mGeneration(0) +{ +} + +void LLFacebookConnect::openFacebookWeb(std::string url) +{ + LLUrlAction::openURLExternal(url); +} + +std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route) +{ + //static std::string sFacebookConnectUrl = gAgent.getRegion()->getCapability("FacebookConnect"); + static std::string sFacebookConnectUrl = "https://pdp15.lindenlab.com/fbc/agent/" + gAgentID.asString(); // TEMPORARY HACK FOR FB DEMO - Cho + std::string url = sFacebookConnectUrl + route; + llinfos << url << llendl; + return url; +} + +void LLFacebookConnect::connectToFacebook(const std::string& auth_code) +{ + LLSD body; + if (!auth_code.empty()) + body["code"] = auth_code; + + LLHTTPClient::put(getFacebookConnectURL("/connection"), body, new LLFacebookConnectResponder()); +} + +void LLFacebookConnect::disconnectFromFacebook() +{ + LLHTTPClient::del(getFacebookConnectURL("/connection"), new LLFacebookDisconnectResponder()); +} + +void LLFacebookConnect::tryToReconnectToFacebook() +{ + if (!mConnectedToFbc) + { + const bool follow_redirects=false; + const F32 timeout=HTTP_REQUEST_EXPIRY_SECS; + LLHTTPClient::get(getFacebookConnectURL("/connection"), new LLFacebookConnectedResponder(false), + LLSD(), timeout, follow_redirects); + } +} + +void LLFacebookConnect::getConnectionToFacebook() +{ + const bool follow_redirects=false; + const F32 timeout=HTTP_REQUEST_EXPIRY_SECS; + LLHTTPClient::get(getFacebookConnectURL("/connection"), new LLFacebookConnectedResponder(true), + LLSD(), timeout, follow_redirects); +} + +void LLFacebookConnect::loadFacebookFriends() +{ + const bool follow_redirects=false; + const F32 timeout=HTTP_REQUEST_EXPIRY_SECS; + LLHTTPClient::get(getFacebookConnectURL("/friend"), new LLFacebookFriendsResponder(), + LLSD(), timeout, follow_redirects); +} + +void LLFacebookConnect::postCheckinMessage(const std::string& message, const std::string& link, const std::string& name, const std::string& caption, const std::string& description, const std::string& picture) +{ + LLSD body; + if (!message.empty()) + body["message"] = message; + if (!link.empty()) + body["link"] = link; + if (!name.empty()) + body["name"] = name; + if (!caption.empty()) + body["caption"] = caption; + if (!description.empty()) + body["description"] = description; + if (!picture.empty()) + body["picture"] = picture; + + // Note: we can use that route for different publish action. We should be able to use the same responder. + LLHTTPClient::post(getFacebookConnectURL("/share/wall"), body, new LLFacebookPostResponder()); +} + +void LLFacebookConnect::storeContent(const LLSD& content) +{ + mGeneration++; + mContent = content; +} + +const LLSD& LLFacebookConnect::getContent() const +{ + return mContent; +} + +void LLFacebookConnect::clearContent() +{ + mGeneration++; + mContent = LLSD(); +} + + + + + + + + diff --git a/indra/newview/llfacebookconnect.h b/indra/newview/llfacebookconnect.h new file mode 100644 index 0000000000..8886630222 --- /dev/null +++ b/indra/newview/llfacebookconnect.h @@ -0,0 +1,73 @@ +/** + * @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" + +/** + * @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: + void connectToFacebook(const std::string& auth_code = ""); + void disconnectFromFacebook(); + void tryToReconnectToFacebook(); + void getConnectionToFacebook(); + + void loadFacebookFriends(); + void postCheckinMessage(const std::string& message, const std::string& link, const std::string& name, const std::string& caption, const std::string& description, const std::string& picture); + + void clearContent(); + void storeContent(const LLSD& content); + const LLSD& getContent() const; + + void setConnected(bool connected) { mConnectedToFbc = connected; } + bool getConnected() { return mConnectedToFbc; } + S32 generation() { return mGeneration; } + + void openFacebookWeb(std::string url); +private: + + friend class LLSingleton<LLFacebookConnect>; + + LLFacebookConnect(); + ~LLFacebookConnect() {}; + std::string getFacebookConnectURL(const std::string& route = ""); + + bool mConnectedToFbc; + LLSD mContent; + S32 mGeneration; +}; + +#endif // LL_LLFACEBOOKCONNECT_H diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 58817485fb..21ba3a444b 100755 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -54,6 +54,7 @@ #include "llworld.h" #include "llsdserialize.h" #include "llviewerobjectlist.h" +#include "boost/foreach.hpp" // // LLFloaterIMContainer @@ -63,7 +64,8 @@ LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& param mExpandCollapseBtn(NULL), mConversationsRoot(NULL), mConversationsEventStream("ConversationsEvents"), - mInitialized(false) + mInitialized(false), + mIsFirstLaunch(true) { mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLFloaterIMContainer::isActionChecked, this, _2)); mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLFloaterIMContainer::onCustomAction, this, _2)); @@ -204,6 +206,7 @@ BOOL LLFloaterIMContainer::postBuild() // a scroller for folder view LLRect scroller_view_rect = mConversationsListPanel->getRect(); scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + scroller_view_rect.mBottom += getChild<LLLayoutStack>("conversations_pane_buttons_stack")->getRect().getHeight(); LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>()); scroller_params.rect(scroller_view_rect); @@ -221,7 +224,8 @@ BOOL LLFloaterIMContainer::postBuild() mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onExpandCollapseButtonClicked, this)); mStubCollapseBtn = getChild<LLButton>("stub_collapse_btn"); mStubCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onStubCollapseButtonClicked, this)); - getChild<LLButton>("speak_btn")->setClickedCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonClicked, this)); + mSpeakBtn = getChild<LLButton>("speak_btn"); + mSpeakBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonClicked, this)); childSetAction("add_btn", boost::bind(&LLFloaterIMContainer::onAddButtonClicked, this)); @@ -342,8 +346,11 @@ void LLFloaterIMContainer::onStubCollapseButtonClicked() void LLFloaterIMContainer::onSpeakButtonClicked() { - LLAgent::toggleMicrophone("speak"); - updateSpeakBtnState(); + //LLAgent::toggleMicrophone("speak"); + //updateSpeakBtnState(); + + LLParticipantList* session_model = dynamic_cast<LLParticipantList*>(mConversationsItems[LLUUID(NULL)]); + session_model->addTestAvatarAgents(); } void LLFloaterIMContainer::onExpandCollapseButtonClicked() { @@ -659,10 +666,32 @@ void LLFloaterIMContainer::setVisible(BOOL visible) LLMultiFloater::setVisible(visible); } +void LLFloaterIMContainer::getDetachedConversationFloaters(floater_list_t& floaters) +{ + typedef conversations_widgets_map::value_type conv_pair; + BOOST_FOREACH(conv_pair item, mConversationsWidgets) + { + LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(item.second); + if (widget) + { + LLFloater* session_floater = widget->getSessionFloater(); + if (session_floater && session_floater->isDetachedAndNotMinimized()) + { + floaters.push_back(session_floater); + } + } + } +} + void LLFloaterIMContainer::setVisibleAndFrontmost(BOOL take_focus, const LLSD& key) { LLMultiFloater::setVisibleAndFrontmost(take_focus, key); selectConversationPair(getSelectedSession(), false, take_focus); + if (mInitialized && mIsFirstLaunch) + { + collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); + mIsFirstLaunch = false; + } } void LLFloaterIMContainer::updateResizeLimits() @@ -779,13 +808,6 @@ void LLFloaterIMContainer::reshapeFloaterAndSetResizeLimits(bool collapse, S32 d setCanMinimize(at_least_one_panel_is_expanded); assignResizeLimits(); - - // force set correct size for the title after show/hide minimize button - LLRect cur_rect = getRect(); - LLRect force_rect = cur_rect; - force_rect.mRight = cur_rect.mRight + 1; - setRect(force_rect); - setRect(cur_rect); } void LLFloaterIMContainer::assignResizeLimits() @@ -793,15 +815,12 @@ void LLFloaterIMContainer::assignResizeLimits() bool is_conv_pane_expanded = !mConversationsPane->isCollapsed(); bool is_msg_pane_expanded = !mMessagesPane->isCollapsed(); - // With two panels visible number of borders is three, because the borders - // between the panels are merged into one - S32 number_of_visible_borders = llmin((is_conv_pane_expanded? 2 : 0) + (is_msg_pane_expanded? 2 : 0), 3); - S32 summary_width_of_visible_borders = number_of_visible_borders * LLPANEL_BORDER_WIDTH; - S32 conv_pane_target_width = is_conv_pane_expanded? - (is_msg_pane_expanded? - mConversationsPane->getRect().getWidth() - : mConversationsPane->getExpandedMinDim()) - : mConversationsPane->getMinDim(); + S32 summary_width_of_visible_borders = (is_msg_pane_expanded ? mConversationsStack->getPanelSpacing() : 0) + 1; + + S32 conv_pane_target_width = is_conv_pane_expanded + ? ( is_msg_pane_expanded?mConversationsPane->getRect().getWidth():mConversationsPane->getExpandedMinDim() ) + : 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; @@ -1143,7 +1162,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()); } @@ -1156,6 +1175,17 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, } } } + //if there is no LLFloaterIMSession* instance for selected conversation it might be Nearby chat + else + { + if(conversationItem->getType() == LLConversationItem::CONV_SESSION_NEARBY) + { + if("chat_history" == command) + { + LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true); + } + } + } } void LLFloaterIMContainer::doToSelected(const LLSD& userdata) @@ -1211,7 +1241,19 @@ bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata) //Enable Chat history item for ad-hoc and group conversations if ("can_chat_history" == item && uuids.size() > 0) { - return LLLogChat::isTranscriptExist(uuids.front()); + //Disable menu item if selected participant is user agent + if(uuids.front() != gAgentID) + { + if (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_NEARBY) + { + return LLLogChat::isNearbyTranscriptExist(); + } + else + { + bool is_group = (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP); + return LLLogChat::isTranscriptExist(uuids.front(),is_group); + } + } } // If nothing is selected(and selected item is not group chat), everything needs to be disabled @@ -1904,7 +1946,6 @@ void LLFloaterIMContainer::reSelectConversation() void LLFloaterIMContainer::updateSpeakBtnState() { - LLButton* mSpeakBtn = getChild<LLButton>("speak_btn"); mSpeakBtn->setToggleState(LLVoiceClient::getInstance()->getUserPTTState()); mSpeakBtn->setEnabled(LLAgent::isActionAllowed("speak")); } @@ -1925,6 +1966,17 @@ void LLFloaterIMContainer::flashConversationItemWidget(const LLUUID& session_id, } } +void LLFloaterIMContainer::highlightConversationItemWidget(const LLUUID& session_id, bool is_highlighted) +{ + //Finds the conversation line item to highlight using the session_id + LLConversationViewSession * widget = dynamic_cast<LLConversationViewSession *>(get_ptr_in_map(mConversationsWidgets,session_id)); + + if (widget) + { + widget->setHighlightState(is_highlighted); + } +} + bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget) { llassert(conversation_item_widget != NULL); @@ -1940,23 +1992,28 @@ bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conve BOOL LLFloaterIMContainer::handleKeyHere(KEY key, MASK mask ) { + BOOL handled = FALSE; + if(mask == MASK_ALT) { if (KEY_RETURN == key ) { expandConversation(); + handled = TRUE; } if ((KEY_DOWN == key ) || (KEY_RIGHT == key)) { selectNextorPreviousConversation(true); + handled = TRUE; } if ((KEY_UP == key) || (KEY_LEFT == key)) { selectNextorPreviousConversation(false); + handled = TRUE; } } - return TRUE; + return handled; } bool LLFloaterIMContainer::selectAdjacentConversation(bool focus_selected) @@ -2013,7 +2070,9 @@ void LLFloaterIMContainer::expandConversation() } } -void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/) +// For conversations, closeFloater() (linked to Ctrl-W) does not actually close the floater but the active conversation. +// This is intentional so it doesn't confuse the user. onClickCloseBtn() closes the whole floater. +void LLFloaterIMContainer::onClickCloseBtn() { // Always unminimize before trying to close. // Most of the time the user will never see this state. @@ -2022,7 +2081,31 @@ void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/) LLMultiFloater::setMinimized(FALSE); } - LLFloater::closeFloater(app_quitting); + LLFloater::closeFloater(); +} + +void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/) +{ + // Check for currently active session + LLUUID session_id = getSelectedSession(); + // If current session is Nearby Chat or there is only one session remaining, close the floater + if (mConversationsItems.size() == 1 || session_id == LLUUID() || app_quitting) + { + onClickCloseBtn(); + } + + // Otherwise, close current conversation + LLFloaterIMSessionTab* active_conversation = LLFloaterIMSessionTab::getConversation(session_id); + if (active_conversation) + { + active_conversation->closeFloater(); + } +} + +void LLFloaterIMContainer::handleReshape(const LLRect& rect, bool by_user) +{ + LLMultiFloater::handleReshape(rect, by_user); + storeRectControl(); } // EOF diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h index e39d20ec35..74c3640bad 100755 --- a/indra/newview/llfloaterimcontainer.h +++ b/indra/newview/llfloaterimcontainer.h @@ -63,6 +63,8 @@ public: /*virtual*/ void setVisible(BOOL visible); /*virtual*/ void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD()); /*virtual*/ void updateResizeLimits(); + /*virtual*/ void handleReshape(const LLRect& rect, bool by_user); + void onCloseFloater(LLUUID& id); /*virtual*/ void addFloater(LLFloater* floaterp, @@ -130,6 +132,7 @@ private: void onStubCollapseButtonClicked(); void processParticipantsStyleUpdate(); void onSpeakButtonClicked(); + /*virtual*/ void onClickCloseBtn(); void collapseConversationsPane(bool collapse, bool save_is_allowed=true); @@ -169,6 +172,7 @@ private: LLButton* mExpandCollapseBtn; LLButton* mStubCollapseBtn; + LLButton* mSpeakBtn; LLPanel* mStubPanel; LLTextBox* mStubTextBox; LLLayoutPanel* mMessagesPane; @@ -176,6 +180,7 @@ private: LLLayoutStack* mConversationsStack; bool mInitialized; + bool mIsFirstLaunch; LLUUID mSelectedSession; std::string mGeneralTitle; @@ -190,9 +195,12 @@ public: void updateSpeakBtnState(); static bool isConversationLoggingAllowed(); void flashConversationItemWidget(const LLUUID& session_id, bool is_flashes); + void highlightConversationItemWidget(const LLUUID& session_id, bool is_highlighted); bool isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget); boost::signals2::connection mMicroChangedSignal; S32 getConversationListItemSize() { return mConversationsWidgets.size(); } + typedef std::list<LLFloater*> floater_list_t; + void getDetachedConversationFloaters(floater_list_t& floaters); private: LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 49f36a2f32..7e472466ed 100755 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -568,7 +568,10 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type ) if (0 == channel) { // discard returned "found" boolean - LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); + if(!LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text)) + { + utf8_revised_text = utf8text; + } } else { diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index 8ec85e1160..848d5c34d2 100755 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -442,8 +442,11 @@ void LLFloaterIMSession::addSessionParticipants(const uuid_vec_t& uuids) } else { - // remember whom we have invited, to notify others later, when the invited ones actually join - mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + if(findInstance(mSessionID)) + { + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + } inviteToSession(uuids); } @@ -469,13 +472,18 @@ void LLFloaterIMSession::addP2PSessionParticipants(const LLSD& notification, con temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); // then we can close the current session - onClose(false); + if(findInstance(mSessionID)) + { + onClose(false); + + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + } // we start a new session so reset the initialization flag mSessionInitialized = false; - // remember whom we have invited, to notify others later, when the invited ones actually join - mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + // Start a new ad hoc voice call if we invite new participants to a P2P call, // or start a text chat otherwise. diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index ce6e639305..cc2859c099 100755 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -212,7 +212,7 @@ void LLFloaterIMSessionTab::assignResizeLimits() mRightPartPanel->setIgnoreReshape(is_participants_pane_collapsed); S32 participants_pane_target_width = is_participants_pane_collapsed? - 0 : (mParticipantListPanel->getRect().getWidth() + LLPANEL_BORDER_WIDTH); + 0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing()); S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth; @@ -241,7 +241,10 @@ BOOL LLFloaterIMSessionTab::postBuild() mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this)); mGearBtn = getChild<LLButton>("gear_btn"); - + mAddBtn = getChild<LLButton>("add_btn"); + mVoiceButton = getChild<LLButton>("voice_call_btn"); + mTranslationCheckBox = getChild<LLUICtrl>("translate_chat_checkbox_lp"); + mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel"); mRightPartPanel = getChild<LLLayoutPanel>("right_part_holder"); @@ -372,7 +375,7 @@ void LLFloaterIMSessionTab::draw() void LLFloaterIMSessionTab::enableDisableCallBtn() { - getChildView("voice_call_btn")->setEnabled( + mVoiceButton->setEnabled( mSessionID.notNull() && mSession && mSession->mSessionInitialized @@ -758,7 +761,7 @@ void LLFloaterIMSessionTab::reshapeChatLayoutPanel() void LLFloaterIMSessionTab::showTranslationCheckbox(BOOL show) { - getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE); + mTranslationCheckBox->setVisible(mIsNearbyChat && show); } // static @@ -805,15 +808,10 @@ void LLFloaterIMSessionTab::reloadEmptyFloaters() void LLFloaterIMSessionTab::updateCallBtnState(bool callIsActive) { - LLButton* voiceButton = getChild<LLButton>("voice_call_btn"); - voiceButton->setImageOverlay( - callIsActive? getString("call_btn_stop") : getString("call_btn_start")); - - voiceButton->setToolTip( - callIsActive? getString("end_call_button_tooltip") : getString("start_call_button_tooltip")); + mVoiceButton->setImageOverlay(callIsActive? getString("call_btn_stop") : getString("call_btn_start")); + mVoiceButton->setToolTip(callIsActive? getString("end_call_button_tooltip") : getString("start_call_button_tooltip")); enableDisableCallBtn(); - } void LLFloaterIMSessionTab::onSlide(LLFloaterIMSessionTab* self) @@ -898,6 +896,7 @@ void LLFloaterIMSessionTab::restoreFloater() mExpandCollapseLineBtn->setImageOverlay(getString("expandline_icon")); setMessagePaneExpanded(true); saveCollapsedState(); + mInputEditor->enableSingleLineMode(false); enableResizeCtrls(true, true, true); } } @@ -953,8 +952,8 @@ void LLFloaterIMSessionTab::updateGearBtn() if(prevVisibility != mGearBtn->getVisible()) { LLRect gear_btn_rect = mGearBtn->getRect(); - LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect(); - LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect(); + LLRect add_btn_rect = mAddBtn->getRect(); + LLRect call_btn_rect = mVoiceButton->getRect(); S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight; S32 right_shift = gear_btn_rect.getWidth() + gap_width; if(mGearBtn->getVisible()) @@ -968,24 +967,24 @@ void LLFloaterIMSessionTab::updateGearBtn() add_btn_rect.translate(-right_shift,0); call_btn_rect.translate(-right_shift,0); } - getChild<LLButton>("add_btn")->setRect(add_btn_rect); - getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect); + mAddBtn->setRect(add_btn_rect); + mVoiceButton->setRect(call_btn_rect); } } void LLFloaterIMSessionTab::initBtns() { LLRect gear_btn_rect = mGearBtn->getRect(); - LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect(); - LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect(); + LLRect add_btn_rect = mAddBtn->getRect(); + LLRect call_btn_rect = mVoiceButton->getRect(); S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight; S32 right_shift = gear_btn_rect.getWidth() + gap_width; add_btn_rect.translate(-right_shift,0); call_btn_rect.translate(-right_shift,0); - getChild<LLButton>("add_btn")->setRect(add_btn_rect); - getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect); + mAddBtn->setRect(add_btn_rect); + mVoiceButton->setRect(call_btn_rect); } // static @@ -1083,21 +1082,26 @@ void LLFloaterIMSessionTab::saveCollapsedState() } BOOL LLFloaterIMSessionTab::handleKeyHere(KEY key, MASK mask ) { + BOOL handled = FALSE; + if(mask == MASK_ALT) { LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance(); if (KEY_RETURN == key && !isTornOff()) { floater_container->expandConversation(); + handled = TRUE; } if ((KEY_UP == key) || (KEY_LEFT == key)) { floater_container->selectNextorPreviousConversation(false); + handled = TRUE; } if ((KEY_DOWN == key ) || (KEY_RIGHT == key)) { floater_container->selectNextorPreviousConversation(true); + handled = TRUE; } } - return TRUE; + return handled; } diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index 302d5a8066..ba80d2369a 100755 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -182,6 +182,9 @@ protected: LLButton* mTearOffBtn; LLButton* mCloseBtn; LLButton* mGearBtn; + LLButton* mAddBtn; + LLButton* mVoiceButton; + LLUICtrl* mTranslationCheckBox; private: // Handling selection and contextual menu diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp index 586965e5a0..c28657dbcd 100755 --- a/indra/newview/llfolderviewmodelinventory.cpp +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -74,6 +74,7 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) it != end_it; ++it) { + // Recursive call to sort() on child (CHUI-849) LLFolderViewFolder* child_folderp = *it; sort(child_folderp); @@ -129,12 +130,12 @@ void LLFolderViewModelItemInventory::requestSort() void LLFolderViewModelItemInventory::setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset, std::string::size_type string_size) { LLFolderViewModelItemCommon::setPassedFilter(passed, filter_generation, string_offset, string_size); - - bool passed_filter_before = mPrevPassedAllFilters; + bool before = mPrevPassedAllFilters; mPrevPassedAllFilters = passedFilter(filter_generation); - if (passed_filter_before != mPrevPassedAllFilters) + if (before != mPrevPassedAllFilters) { + // Need to rearrange the folder if the filtered state of the item changed LLFolderViewFolder* parent_folder = mFolderViewItem->getParentFolder(); if (parent_folder) { @@ -150,11 +151,11 @@ bool LLFolderViewModelItemInventory::filterChildItem( LLFolderViewModelItem* ite bool continue_filtering = true; if (item->getLastFilterGeneration() < filter_generation) { - // recursive application of the filter for child items + // Recursive application of the filter for child items (CHUI-849) continue_filtering = item->filter( filter ); } - // track latest generation to pass any child items, for each folder up to root + // Update latest generation to pass filter in parent and propagate up to root if (item->passedFilter()) { LLFolderViewModelItemInventory* view_model = this; @@ -174,53 +175,61 @@ bool LLFolderViewModelItemInventory::filter( LLFolderViewFilter& filter) const S32 filter_generation = filter.getCurrentGeneration(); const S32 must_pass_generation = filter.getFirstRequiredGeneration(); - if (getLastFilterGeneration() >= must_pass_generation + if (getLastFilterGeneration() >= must_pass_generation && getLastFolderFilterGeneration() >= must_pass_generation && !passedFilter(must_pass_generation)) { // failed to pass an earlier filter that was a subset of the current one - // go ahead and flag this item as done + // go ahead and flag this item as not pass setPassedFilter(false, filter_generation); setPassedFolderFilter(false, filter_generation); return true; } - const bool passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY) - ? filter.checkFolder(this) - : true; + // *TODO : Revise the logic for fast pass on less restrictive filter case + /* + const S32 sufficient_pass_generation = filter.getFirstSuccessGeneration(); + if (getLastFilterGeneration() >= sufficient_pass_generation + && getLastFolderFilterGeneration() >= sufficient_pass_generation + && passedFilter(sufficient_pass_generation)) + { + // passed an earlier filter that was a superset of the current one + // go ahead and flag this item as pass + setPassedFilter(true, filter_generation); + setPassedFolderFilter(true, filter_generation); + return true; + } + */ + + const bool passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY) ? filter.checkFolder(this) : true; setPassedFolderFilter(passed_filter_folder, filter_generation); - if(!mChildren.empty() + bool continue_filtering = true; + + if (!mChildren.empty() && (getLastFilterGeneration() < must_pass_generation // haven't checked descendants against minimum required generation to pass - || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement + || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement { // now query children - for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); - iter != end_iter && filter.getFilterCount() > 0; - ++iter) + for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); iter != end_iter; ++iter) { - if (!filterChildItem((*iter), filter)) + continue_filtering = filterChildItem((*iter), filter); + if (!continue_filtering) { break; } } } - // if we didn't use all filter iterations - // that means we filtered all of our descendants - // so filter ourselves now - if (filter.getFilterCount() > 0) + // If we didn't use all the filter time that means we filtered all of our descendants so we can filter ourselves now + if (continue_filtering) { - filter.decrementFilterCount(); - + // This is where filter check on the item done (CHUI-849) const bool passed_filter = filter.check(this); setPassedFilter(passed_filter, filter_generation, filter.getStringMatchOffset(this), filter.getFilterStringSize()); - return true; - } - else - { - return false; + continue_filtering = !filter.isTimedOut(); } + return continue_filtering; } LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() @@ -307,8 +316,8 @@ bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a, } } -LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) - : LLFolderViewModelItemCommon(root_view_model), - mPrevPassedAllFilters(false) +LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) : + LLFolderViewModelItemCommon(root_view_model), + mPrevPassedAllFilters(false) { } diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h index 890d03d1c9..9dcfdfa185 100755 --- a/indra/newview/llfolderviewmodelinventory.h +++ b/indra/newview/llfolderviewmodelinventory.h @@ -59,9 +59,8 @@ public: virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; virtual LLToolDragAndDrop::ESource getDragSource() const = 0; - protected: - bool mPrevPassedAllFilters; + bool mPrevPassedAllFilters; }; class LLInventorySort diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index a0f2918bd7..302d21c2e4 100755 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -116,6 +116,80 @@ public: }; LLGroupHandler gGroupHandler; +// This object represents a pending request for specified group member information +// which is needed to check whether avatar can leave group +class LLFetchGroupMemberData : public LLGroupMgrObserver +{ +public: + LLFetchGroupMemberData(const LLUUID& group_id) : + mGroupId(group_id), + mRequestProcessed(false), + LLGroupMgrObserver(group_id) + { + llinfos << "Sending new group member request for group_id: "<< group_id << llendl; + LLGroupMgr* mgr = LLGroupMgr::getInstance(); + // register ourselves as an observer + mgr->addObserver(this); + // send a request + mgr->sendGroupPropertiesRequest(group_id); + mgr->sendCapGroupMembersRequest(group_id); + } + + ~LLFetchGroupMemberData() + { + if (!mRequestProcessed) + { + // Request is pending + llwarns << "Destroying pending group member request for group_id: " + << mGroupId << llendl; + } + // Remove ourselves as an observer + LLGroupMgr::getInstance()->removeObserver(this); + } + + void changed(LLGroupChange gc) + { + if (gc == GC_MEMBER_DATA && !mRequestProcessed) + { + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupId); + if (!gdatap) + { + llwarns << "LLGroupMgr::getInstance()->getGroupData() was NULL" << llendl; + } + else if (!gdatap->isMemberDataComplete()) + { + llwarns << "LLGroupMgr::getInstance()->getGroupData()->isMemberDataComplete() was FALSE" << llendl; + } + else + { + processGroupData(); + mRequestProcessed = true; + } + } + } + + LLUUID getGroupId() { return mGroupId; } + virtual void processGroupData() = 0; +protected: + LLUUID mGroupId; +private: + bool mRequestProcessed; +}; + +class LLFetchLeaveGroupData: public LLFetchGroupMemberData +{ +public: + LLFetchLeaveGroupData(const LLUUID& group_id) + : LLFetchGroupMemberData(group_id) + {} + void processGroupData() + { + LLGroupActions::processLeaveGroupDataResponse(mGroupId); + } +}; + +LLFetchLeaveGroupData* gFetchLeaveGroupData = NULL; + // static void LLGroupActions::search() { @@ -208,23 +282,52 @@ bool LLGroupActions::onJoinGroup(const LLSD& notification, const LLSD& response) void LLGroupActions::leave(const LLUUID& group_id) { if (group_id.isNull()) + { return; + } - S32 count = gAgent.mGroups.count(); - S32 i; - for (i = 0; i < count; ++i) + LLGroupData group_data; + if (gAgent.getGroupData(group_id, group_data)) { - if(gAgent.mGroups.get(i).mID == group_id) - break; + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id); + if (!gdatap || !gdatap->isMemberDataComplete()) + { + if (gFetchLeaveGroupData != NULL) + { + delete gFetchLeaveGroupData; + gFetchLeaveGroupData = NULL; + } + gFetchLeaveGroupData = new LLFetchLeaveGroupData(group_id); + } + else + { + processLeaveGroupDataResponse(group_id); + } } - if (i < count) +} + +//static +void LLGroupActions::processLeaveGroupDataResponse(const LLUUID group_id) +{ + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id); + LLUUID agent_id = gAgent.getID(); + LLGroupMgrGroupData::member_list_t::iterator mit = gdatap->mMembers.find(agent_id); + //get the member data for the group + if ( mit != gdatap->mMembers.end() ) { - LLSD args; - args["GROUP"] = gAgent.mGroups.get(i).mName; - LLSD payload; - payload["group_id"] = group_id; - LLNotificationsUtil::add("GroupLeaveConfirmMember", args, payload, onLeaveGroup); + LLGroupMemberData* member_data = (*mit).second; + + if ( member_data && member_data->isOwner() && gdatap->mMemberCount == 1) + { + LLNotificationsUtil::add("OwnerCannotLeaveGroup"); + return; + } } + LLSD args; + args["GROUP"] = gdatap->mName; + LLSD payload; + payload["group_id"] = group_id; + LLNotificationsUtil::add("GroupLeaveConfirmMember", args, payload, onLeaveGroup); } // static diff --git a/indra/newview/llgroupactions.h b/indra/newview/llgroupactions.h index 3f9852f194..afc4686dd7 100755 --- a/indra/newview/llgroupactions.h +++ b/indra/newview/llgroupactions.h @@ -114,6 +114,14 @@ public: private: static bool onJoinGroup(const LLSD& notification, const LLSD& response); static bool onLeaveGroup(const LLSD& notification, const LLSD& response); + + /** + * This function is called by LLFetchLeaveGroupData upon receiving a response to a group + * members data request. + */ + static void processLeaveGroupDataResponse(const LLUUID group_id); + + friend class LLFetchLeaveGroupData; }; #endif // LL_LLGROUPACTIONS_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 2c20409381..214b177a1b 100755 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -103,6 +103,7 @@ BOOL LLSessionTimeoutTimer::tick() } +void notify_of_message(const LLSD& msg, bool is_dnd_msg); void process_dnd_im(const LLSD& notification) { @@ -129,15 +130,9 @@ 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) + } - LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); - - if (im_box) - { - im_box->flashConversationItemWidget(sessionID, true); - } - - } + notify_of_message(data, true); } @@ -158,88 +153,89 @@ static void on_avatar_name_cache_toast(const LLUUID& agent_id, LLNotificationsUtil::add("IMToast", args, args, boost::bind(&LLFloaterIMContainer::showConversation, LLFloaterIMContainer::getInstance(), msg["session_id"].asUUID())); } -void on_new_message(const LLSD& msg) +void notify_of_message(const LLSD& msg, bool is_dnd_msg) { - std::string user_preferences; - LLUUID participant_id = msg["from_id"].asUUID(); - LLUUID session_id = msg["session_id"].asUUID(); - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); + 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); - // 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; + // 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; - 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 if (!LLFloater::isVisible(im_box) || im_box->isMinimized()) { 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()) - { - user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); - } - else if(session->isP2PSessionType()) - { - if (LLAvatarTracker::instance().isBuddy(participant_id)) - { - user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); - } - else - { - user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); - } - } - else if(session->isAdHocSessionType()) - { - user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); - } - else if(session->isGroupSessionType()) - { - user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); - } - - // actions: + // determine user prefs for this session + if (session_id.isNull()) + { + user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); + } + else if(session->isP2PSessionType()) + { + if (LLAvatarTracker::instance().isBuddy(participant_id)) + { + user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); + } + else + { + user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); + } + } + else if(session->isAdHocSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); + } + else if(session->isGroupSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); + } - // 0. nothing - exit - if (("none" == user_preferences || - ON_TOP_AND_ITEM_IS_SELECTED == conversations_floater_status) - && session_floater->isMessagePaneExpanded()) - { - return; - } + // actions: - // 1. open floater and [optional] surface it - if ("openconversations" == user_preferences && - (CLOSED == conversations_floater_status - || NOT_ON_TOP == conversations_floater_status)) - { - if(!gAgent.isDoNotDisturb()) - { + // 0. nothing - exit + if (("none" == user_preferences || + ON_TOP_AND_ITEM_IS_SELECTED == conversations_floater_status) + && session_floater->isMessagePaneExpanded()) + { + return; + } + + // 1. open floater and [optional] surface it + if (("openconversations" == user_preferences && + (CLOSED == conversations_floater_status + || NOT_ON_TOP == conversations_floater_status)) + || is_dnd_msg ) + { + if(!gAgent.isDoNotDisturb()) + { // Open conversations floater LLFloaterReg::showInstance("im_container"); im_box->collapseMessagesPane(false); @@ -261,57 +257,100 @@ void on_new_message(const LLSD& msg) } } } - else - { - //If in DND mode, allow notification to be stored so upon DND exit - //useMostItrusiveIMNotification will be called to notify user a message exists - if(session_id.notNull() - && participant_id.notNull() - && !session_floater->isShown()) - { - LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); - } - } - } + 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) - || ("flash" == user_preferences && CLOSED == conversations_floater_status)) - { - if(!LLMuteList::getInstance()->isMuted(participant_id)) - { - im_box->flashConversationItemWidget(session_id, true); - } - } + } - // 3. Flash FUI button - if (("toast" == user_preferences || "flash" == user_preferences) && - (CLOSED == conversations_floater_status - || NOT_ON_TOP == conversations_floater_status)) - { - if(!LLMuteList::getInstance()->isMuted(participant_id) - && !gAgent.isDoNotDisturb()) - { - gToolBarView->flashCommand(LLCommandId("chat"), true); - } - } + // 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) + || is_dnd_msg) + { + if(!LLMuteList::getInstance()->isMuted(participant_id)) + { + if(gAgent.isDoNotDisturb()) + { + store_dnd_message = true; + } + else + { + if (is_dnd_msg && (ON_TOP == conversations_floater_status || + NOT_ON_TOP == conversations_floater_status || + CLOSED == conversations_floater_status)) + { + im_box->highlightConversationItemWidget(session_id, true); + } + else + { + im_box->flashConversationItemWidget(session_id, true); + } + } + } + } - // 4. Toast - if ((("toast" == user_preferences) && - (CLOSED == conversations_floater_status - || NOT_ON_TOP == conversations_floater_status)) - || !session_floater->isMessagePaneExpanded()) + // 3. Flash FUI button + if (("toast" == user_preferences || "flash" == user_preferences) && + (CLOSED == conversations_floater_status + || NOT_ON_TOP == conversations_floater_status) + && !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); + } + else + { + store_dnd_message = true; + } + } + } - { - //Show IM toasts (upper right toasts) - // Skip toasting for system messages and for nearby chat - if(session_id.notNull() && participant_id.notNull()) - { - LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); - } - } + // 4. Toast + if ((("toast" == user_preferences) && + (ON_TOP_AND_ITEM_IS_SELECTED != conversations_floater_status)) + || !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()) + { + if(!is_dnd_msg) + { + if(gAgent.isDoNotDisturb()) + { + store_dnd_message = true; + } + else + { + LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); + } + } + } + } + if (store_dnd_message) + { + // If in DND mode, allow notification to be stored so upon DND exit + // the user will be notified with some limitations (see 'is_dnd_msg' flag checks) + if(session_id.notNull() + && participant_id.notNull() + && !session_floater->isShown()) + { + LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); + } + } +} + +void on_new_message(const LLSD& msg) +{ + notify_of_message(msg, false); } LLIMModel::LLIMModel() diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 92f2d33073..3c6974cf6d 100755 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -70,11 +70,8 @@ LLInventoryFilter::LLInventoryFilter(const Params& p) mFilterSubString(p.substring), mCurrentGeneration(0), mFirstRequiredGeneration(0), - mFirstSuccessGeneration(0), - mFilterCount(0) + mFirstSuccessGeneration(0) { - mNextFilterGeneration = mCurrentGeneration + 1; - // copy mFilterOps into mDefaultFilterOps markDefault(); } @@ -92,9 +89,7 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item) return passed_clipboard; } - std::string::size_type string_offset = mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : std::string::npos; - - BOOL passed = (mFilterSubString.size() == 0 || string_offset != std::string::npos); + bool passed = (mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) != std::string::npos : true); passed = passed && checkAgainstFilterType(listener); passed = passed && checkAgainstPermissions(listener); passed = passed && checkAgainstFilterLinks(listener); @@ -105,17 +100,12 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item) bool LLInventoryFilter::check(const LLInventoryItem* item) { - std::string::size_type string_offset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; - + const bool passed_string = (mFilterSubString.size() ? item->getName().find(mFilterSubString) != std::string::npos : true); const bool passed_filtertype = checkAgainstFilterType(item); const bool passed_permissions = checkAgainstPermissions(item); - const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID()); - const bool passed = (passed_filtertype - && passed_permissions - && passed_clipboard - && (mFilterSubString.size() == 0 || string_offset != std::string::npos)); + const bool passed_clipboard = checkAgainstClipboard(item->getUUID()); - return passed; + return passed_filtertype && passed_permissions && passed_clipboard && passed_string; } bool LLInventoryFilter::checkFolder(const LLFolderViewModelItem* item) const @@ -439,7 +429,7 @@ void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types) current_types = types; if (more_bits_set && fewer_bits_set) { - // neither less or more restrive, both simultaneously + // neither less or more restrictive, both simultaneously // so we need to filter from scratch setModified(FILTER_RESTART); } @@ -714,7 +704,7 @@ void LLInventoryFilter::resetDefault() void LLInventoryFilter::setModified(EFilterModified behavior) { mFilterText.clear(); - mCurrentGeneration = mNextFilterGeneration++; + mCurrentGeneration++; if (mFilterModified == FILTER_NONE) { @@ -1021,21 +1011,19 @@ LLInventoryFilter::EFolderShow LLInventoryFilter::getShowFolderState() const return mFilterOps.mShowFolderState; } -void LLInventoryFilter::setFilterCount(S32 count) -{ - mFilterCount = count; -} -S32 LLInventoryFilter::getFilterCount() const +bool LLInventoryFilter::isTimedOut() { - return mFilterCount; + return mFilterTime.hasExpired(); } -void LLInventoryFilter::decrementFilterCount() -{ - mFilterCount--; +void LLInventoryFilter::resetTime(S32 timeout) +{ + mFilterTime.reset(); + F32 time_in_sec = (F32)(timeout)/1000.0; + mFilterTime.setTimerExpirySec(time_in_sec); } -S32 LLInventoryFilter::getCurrentGeneration() const +S32 LLInventoryFilter::getCurrentGeneration() const { return mCurrentGeneration; } diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 4912b5ca91..ce516af0b9 100755 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -215,12 +215,11 @@ public: void setModified(EFilterModified behavior = FILTER_RESTART); // +-------------------------------------------------------------------+ - // + Count + // + Time // +-------------------------------------------------------------------+ - void setFilterCount(S32 count); - S32 getFilterCount() const; - void decrementFilterCount(); - + void resetTime(S32 timeout); + bool isTimedOut(); + // +-------------------------------------------------------------------+ // + Default // +-------------------------------------------------------------------+ @@ -262,13 +261,15 @@ private: const std::string mName; S32 mCurrentGeneration; + // The following makes checking for pass/no pass possible even if the item is not checked against the current generation + // Any item that *did not pass* the "required generation" will *not pass* the current one + // Any item that *passes* the "success generation" will *pass* the current one S32 mFirstRequiredGeneration; S32 mFirstSuccessGeneration; - S32 mNextFilterGeneration; - S32 mFilterCount; EFilterModified mFilterModified; - + LLTimer mFilterTime; + std::string mFilterText; std::string mEmptyLookupMessage; }; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index cf1fd4c0d0..e5b9e11d48 100755 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -192,7 +192,7 @@ LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id ) p.show_item_link_overlays = mShowItemLinkOverlays; p.root = NULL; p.options_menu = "menu_inventory.xml"; - + return LLUICtrlFactory::create<LLFolderView>(p); } @@ -396,6 +396,7 @@ LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState() return getFilter().getShowFolderState(); } +// Called when something changed in the global model (new item, item coming through the wire, rename, move, etc...) (CHUI-849) void LLInventoryPanel::modelChanged(U32 mask) { static LLFastTimer::DeclareTimer FTM_REFRESH("Inventory Refresh"); diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 2d7454b636..379bbc5f8d 100755 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -631,7 +631,7 @@ void LLLogChat::deleteTranscripts() } // static -bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id) +bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id, bool is_group) { std::vector<std::string> list_of_transcriptions; LLLogChat::getListOfTranscriptFiles(list_of_transcriptions); @@ -641,20 +641,53 @@ bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id) LLAvatarName avatar_name; LLAvatarNameCache::get(avatar_id, &avatar_name); std::string avatar_user_name = avatar_name.getAccountName(); - std::replace(avatar_user_name.begin(), avatar_user_name.end(), '.', '_'); - - BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + if(!is_group) { - if (std::string::npos != transcript_file_name.find(avatar_user_name)) + std::replace(avatar_user_name.begin(), avatar_user_name.end(), '.', '_'); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) { - return true; + if (std::string::npos != transcript_file_name.find(avatar_user_name)) + { + return true; + } } } + else + { + std::string file_name; + gCacheName->getGroupName(avatar_id, file_name); + file_name = makeLogFileName(file_name); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + { + if (transcript_file_name == file_name) + { + return true; + } + } + } + } return false; } +bool LLLogChat::isNearbyTranscriptExist() +{ + std::vector<std::string> list_of_transcriptions; + LLLogChat::getListOfTranscriptFiles(list_of_transcriptions); + + std::string file_name; + file_name = makeLogFileName("chat"); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + { + if (transcript_file_name == file_name) + { + return true; + } + } + return false; +} + //*TODO mark object's names in a special way so that they will be distinguishable form avatar name //which are more strict by its nature (only firstname and secondname) //Example, an object's name can be written like "Object <actual_object's_name>" diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index e819f00dd9..bd70dbaac9 100755 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -67,7 +67,8 @@ public: std::vector<std::string>& listOfFilesToMove); static void deleteTranscripts(); - static bool isTranscriptExist(const LLUUID& avatar_id); + static bool isTranscriptExist(const LLUUID& avatar_id, bool is_group=false); + static bool isNearbyTranscriptExist(); private: static std::string cleanFileName(std::string filename); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index d6535c88e9..53deded2f2 100755 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -130,6 +130,8 @@ BOOL LLPanelMainInventory::postBuild() mFilterTabs = getChild<LLTabContainer>("inventory filter tabs"); mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this)); + mCounterCtrl = getChild<LLUICtrl>("ItemcountText"); + //panel->getFilter().markDefault(); // Set up the default inv. panel/filter settings. @@ -566,7 +568,7 @@ void LLPanelMainInventory::draw() void LLPanelMainInventory::updateItemcountText() { // *TODO: Calling setlocale() on each frame may be inefficient. - LLLocale locale(LLStringUtil::getLocale()); + //LLLocale locale(LLStringUtil::getLocale()); std::string item_count_string; LLResMgr::getInstance()->getIntegerString(item_count_string, gInventory.getItemCount()); @@ -589,8 +591,7 @@ void LLPanelMainInventory::updateItemcountText() text = getString("ItemcountUnknown"); } - // *TODO: Cache the LLUICtrl* for the ItemcountText control - getChild<LLUICtrl>("ItemcountText")->setValue(text); + mCounterCtrl->setValue(text); } void LLPanelMainInventory::onFocusReceived() diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index 899931aa89..394b004e20 100755 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -121,6 +121,7 @@ private: LLFilterEditor* mFilterEditor; LLTabContainer* mFilterTabs; + LLUICtrl* mCounterCtrl; LLHandle<LLFloater> mFinderHandle; LLInventoryPanel* mActivePanel; bool mResortActivePanel; diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 4138558bad..69b6bdf737 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,34 +50,49 @@ #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 "llpersonfolderview.h" +#include "llpersonmodelcommon.h" +#include "llpersontabview.h" #include "llsidetraypanelcontainer.h" #include "llrecentpeople.h" #include "llviewercontrol.h" // for gSavedSettings #include "llviewermenu.h" // for gMenuHolder #include "llvoiceclient.h" #include "llworld.h" +#include "llsociallist.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 +#define FBCTEST_LIST_UPDATE_INTERVAL 0.25 static const std::string NEARBY_TAB_NAME = "nearby_panel"; 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 FBCTEST_TAB_NAME = "fbctest_panel"; +static const std::string FBCTESTTWO_TAB_NAME = "fbctesttwo_panel"; static const std::string COLLAPSED_BY_USER = "collapsed_by_user"; + /** Comparator for comparing avatar items by last interaction date */ class LLAvatarItemRecentComparator : public LLAvatarItemComparator { @@ -489,23 +506,73 @@ public: } }; +/** + * Periodically updates the FBC test list after a login is initiated. + * + * The period is defined by FBCTEST_LIST_UPDATE_INTERVAL constant. + */ +class LLFbcTestListUpdater : public LLAvatarListUpdater +{ + LOG_CLASS(LLFbcTestListUpdater); + +public: + LLFbcTestListUpdater(callback_t cb) + : LLAvatarListUpdater(cb, FBCTEST_LIST_UPDATE_INTERVAL) + { + setActive(false); + } + + /*virtual*/ void setActive(bool val) + { + if (val) + { + // update immediately and start regular updates + update(); + mEventTimer.start(); + } + else + { + // stop regular updates + mEventTimer.stop(); + } + } + + /*virtual*/ BOOL tick() + { + update(); + return FALSE; + } +private: +}; + //============================================================================= LLPanelPeople::LLPanelPeople() : LLPanel(), + mPersonFolderView(NULL), + mTryToConnectToFbc(true), mTabContainer(NULL), mOnlineFriendList(NULL), mAllFriendList(NULL), mNearbyList(NULL), mRecentList(NULL), mGroupList(NULL), - mMiniMap(NULL) + mMiniMap(NULL), + mFacebookListGeneration(0) { mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList, this)); mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList, this)); mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList, this)); + mFacebookListUpdater = new LLFbcTestListUpdater(boost::bind(&LLPanelPeople::updateFacebookList, this)); mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this)); + mCommitCallbackRegistrar.add("People.loginFBC", boost::bind(&LLPanelPeople::onLoginFbcButtonClicked, this)); + mCommitCallbackRegistrar.add("People.requestFBC", boost::bind(&LLPanelPeople::onFacebookAppRequestClicked, this)); + mCommitCallbackRegistrar.add("People.sendFBC", boost::bind(&LLPanelPeople::onFacebookAppSendClicked, this)); + mCommitCallbackRegistrar.add("People.testaddFBC", boost::bind(&LLPanelPeople::onFacebookTestAddClicked, this)); + mCommitCallbackRegistrar.add("People.testaddFBCFolderView", boost::bind(&LLPanelPeople::addTestParticipant, this)); + mCommitCallbackRegistrar.add("People.testFBCCheckin", boost::bind(&LLPanelPeople::onFacebookCheckinClicked, this)); + mCommitCallbackRegistrar.add("People.AddFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); mCommitCallbackRegistrar.add("People.AddFriendWizard", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); mCommitCallbackRegistrar.add("People.DelFriend", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); @@ -532,11 +599,14 @@ LLPanelPeople::~LLPanelPeople() delete mNearbyListUpdater; delete mFriendListUpdater; delete mRecentListUpdater; + delete mFacebookListUpdater; if(LLVoiceClient::instanceExists()) { LLVoiceClient::getInstance()->removeObserver(this); } + + if (mFbcTestBrowserHandle.get()) mFbcTestBrowserHandle.get()->die(); } void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list) @@ -571,6 +641,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)); @@ -616,6 +687,54 @@ BOOL LLPanelPeople::postBuild() mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); + //===Temporary ======================================================================== + + LLPanel * social_tab = getChild<LLPanel>(FBCTEST_TAB_NAME); + mFacebookFriends = social_tab->getChild<LLSocialList>("facebook_friends"); + // Note: we use the same updater for both test lists (brute force but OK since it's temporary) + social_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFacebookListUpdater, _2)); + + //===Test START======================================================================== + + LLPanel * socialtwo_tab = getChild<LLPanel>(FBCTESTTWO_TAB_NAME); + socialtwo_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFacebookListUpdater, _2)); + + //Create folder view + LLPersonModelCommon* base_item = new LLPersonModelCommon(mPersonFolderViewModel); + + LLPersonFolderView::Params folder_view_params(LLUICtrlFactory::getDefaultParams<LLPersonFolderView>()); + + folder_view_params.parent_panel = socialtwo_tab; + folder_view_params.listener = base_item; + folder_view_params.view_model = &mPersonFolderViewModel; + folder_view_params.root = NULL; + folder_view_params.use_ellipses = true; + folder_view_params.use_label_suffix = true; + folder_view_params.options_menu = "menu_conversation.xml"; + folder_view_params.name = "fbcfolderview"; + mPersonFolderView = LLUICtrlFactory::create<LLPersonFolderView>(folder_view_params); + + //Create scroller + LLRect scroller_view_rect = socialtwo_tab->getRect(); + scroller_view_rect.mTop -= 2+27; // 27 is the height of the top toolbar + scroller_view_rect.mRight -= 4; + scroller_view_rect.mLeft += 2; + LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>()); + scroller_params.rect(scroller_view_rect); + + LLScrollContainer* scroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); + socialtwo_tab->addChildInBack(scroller); + scroller->addChild(mPersonFolderView); + scroller->setFollowsAll(); + mPersonFolderView->setScrollContainer(scroller); + mPersonFolderView->setFollowsAll(); + + gIdleCallbacks.addFunction(idle, this); + + //===Test END======================================================================== + + + setSortOrder(mRecentList, (ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"), false); setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false); setSortOrder(mNearbyList, (ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"), false); @@ -664,6 +783,15 @@ BOOL LLPanelPeople::postBuild() // Must go after setting commit callback and initializing all pointers to children. mTabContainer->selectTabByName(NEARBY_TAB_NAME); + mFBCGearButton = getChild<LLMenuButton>("fbc_options_btn"); + + LLToggleableMenu* fbc_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_gear_fbc.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(fbc_menu) + { + mFBCMenuHandle = fbc_menu->getHandle(); + mFBCGearButton->setMenu(fbc_menu); + } + LLVoiceClient::getInstance()->addObserver(this); // call this method in case some list is empty and buttons can be in inconsistent state @@ -686,6 +814,12 @@ void LLPanelPeople::onChange(EStatusType status, const std::string &channelURI, updateButtons(); } +void LLPanelPeople::idle(void * user_data) +{ + LLPanelPeople * self = static_cast<LLPanelPeople *>(user_data); + self->mPersonFolderView->update(); +} + void LLPanelPeople::updateFriendListHelpText() { // show special help text for just created account to help finding friends. EXT-4836 @@ -786,6 +920,62 @@ void LLPanelPeople::updateRecentList() mRecentList->setDirty(); } +void LLPanelPeople::updateFacebookList() +{ + if (mTryToConnectToFbc) + { + // try to reconnect to facebook! + LLFacebookConnect::instance().tryToReconnectToFacebook(); + + // don't try again + mTryToConnectToFbc = false; + + // stop updating + mFacebookListUpdater->setActive(false); + } + + if (LLFacebookConnect::instance().generation() != mFacebookListGeneration) + { + mFacebookListGeneration = LLFacebookConnect::instance().generation(); + LLSD friends = LLFacebookConnect::instance().getContent(); + + mFacebookFriends->clear(); + LLPersonTabModel::tab_type tab_type; + LLAvatarTracker& avatar_tracker = LLAvatarTracker::instance(); + + for (LLSD::map_const_iterator i = friends.beginMap(); i != friends.endMap(); ++i) + { + std::string name = i->second["name"].asString(); + LLUUID agent_id = i->second.has("agent_id") ? i->second["agent_id"].asUUID() : LLUUID(NULL); + bool second_life_buddy = agent_id.notNull() ? avatar_tracker.isBuddy(agent_id) : false; + + //add to avatar list + mFacebookFriends->addNewItem(agent_id, name, false); + + if(!second_life_buddy) + { + //FB+SL but not SL friend + if (agent_id.notNull()) + { + tab_type = LLPersonTabModel::FB_SL_NON_SL_FRIEND; + } + //FB only friend + else + { + tab_type = LLPersonTabModel::FB_ONLY_FRIEND; + } + + //Add to person tab model + LLPersonTabModel * person_tab_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderView->getPersonTabModelByIndex(tab_type)); + if (person_tab_model) + { + addParticipantToModel(person_tab_model, agent_id, name); + } + } + } + } +} + void LLPanelPeople::updateButtons() { std::string cur_tab = getActiveTabName(); @@ -870,6 +1060,13 @@ LLUUID LLPanelPeople::getCurrentItemID() const if (cur_tab == BLOCKED_TAB_NAME) return LLUUID::null; // FIXME? + + if (cur_tab == FBCTEST_TAB_NAME) + return LLUUID::null; + + if (cur_tab == FBCTESTTWO_TAB_NAME) + return LLUUID::null; + llassert(0 && "unknown tab selected"); return LLUUID::null; @@ -893,6 +1090,10 @@ void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const mGroupList->getSelectedUUIDs(selected_uuids); else if (cur_tab == BLOCKED_TAB_NAME) selected_uuids.clear(); // FIXME? + else if (cur_tab == FBCTEST_TAB_NAME) + return; + else if (cur_tab == FBCTESTTWO_TAB_NAME) + return; else llassert(0 && "unknown tab selected"); @@ -989,23 +1190,23 @@ 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); - setAccordionCollapsedByUser("tab_online", false); - setAccordionCollapsedByUser("tab_all", false); - showFriendsAccordionsIfNeeded(); + setAccordionCollapsedByUser("tab_online", false); + setAccordionCollapsedByUser("tab_all", 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); @@ -1014,6 +1215,11 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string) { mRecentList->setNameFilter(filter); } + else if (cur_tab == FBCTESTTWO_TAB_NAME) + { + mPersonFolderViewModel.getFilter().setFilterSubString(filter); + mPersonFolderView->requestArrange(); + } } void LLPanelPeople::onTabSelected(const LLSD& param) @@ -1225,7 +1431,7 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) mAllFriendList->showPermissions(show_permissions); mOnlineFriendList->showPermissions(show_permissions); } -} + } void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) { @@ -1446,4 +1652,107 @@ bool LLPanelPeople::isAccordionCollapsedByUser(const std::string& name) return isAccordionCollapsedByUser(getChild<LLUICtrl>(name)); } +void LLPanelPeople::addTestParticipant() +{ + std::string suffix("Aa"); + std::string prefix("FB Name"); + LLPersonTabModel * person_tab_model; + LLUUID agentID; + std::string name; + LLPersonTabModel::tab_type tab_type; + + for(int i = 0; i < 300; ++i) + { + //Adds FB+SL people that aren't yet SL friends + if(i < 10) + { + tab_type = LLPersonTabModel::FB_SL_NON_SL_FRIEND; + agentID = gAgent.getID(); + } + //Adds FB only friends + else + { + tab_type = LLPersonTabModel::FB_ONLY_FRIEND; + agentID = LLUUID(NULL); + } + + person_tab_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderView->getPersonTabModelByIndex(tab_type)); + name = prefix + " " + suffix; + addParticipantToModel(person_tab_model, agentID, name); + // Next suffix : Aa, Ab, Ac ... Az, Ba, Bb, Bc ... Bz, Ca, Cb ... + suffix[1]+=1; + if (suffix[1]=='{') + { + suffix[1]='a'; + suffix[0]+=1; + if (suffix[0]=='[') + suffix[0]='A'; + } + } +} + +void LLPanelPeople::addParticipantToModel(LLPersonTabModel * person_folder_model, const LLUUID& agent_id, const std::string& name) +{ + LLPersonModel* person_model = NULL; + + LLAvatarName avatar_name; + bool has_name = agent_id.notNull() ? LLAvatarNameCache::get(agent_id, &avatar_name) : false; + std::string avatar_name_string; + + if(has_name) + { + avatar_name_string = avatar_name.getDisplayName(); + } + + person_model = new LLPersonModel(agent_id, avatar_name_string, name, mPersonFolderViewModel); + person_folder_model->addParticipant(person_model); +} + +void LLPanelPeople::onLoginFbcButtonClicked() +{ + if (LLFacebookConnect::instance().getConnected()) + { + LLFacebookConnect::instance().disconnectFromFacebook(); + } + else + { + LLFacebookConnect::instance().getConnectionToFacebook(); + } +} + +void LLPanelPeople::onFacebookCheckinClicked() +{ + // Get the local SLURL + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + + LLFacebookConnect::instance().postCheckinMessage("Here I am in SL!", slurl.getSLURLString(), slurl.getRegion(), "", "", ""); +} + +void LLPanelPeople::onFacebookAppRequestClicked() +{ +} + +void LLPanelPeople::onFacebookAppSendClicked() +{ +} + +static LLFastTimer::DeclareTimer FTM_AVATAR_LIST_TEST("avatar list test"); + +void LLPanelPeople::onFacebookTestAddClicked() +{ + LLFastTimer _(FTM_AVATAR_LIST_TEST); + + mFacebookFriends->clear(); + + LL_INFOS("LLPanelPeople") << "start adding 300 users" << LL_ENDL; + + for(int i = 0; i < 300; ++i) + { + mFacebookFriends->addNewItem(LLUUID(), "Test", false); + } + + LL_INFOS("LLPanelPeople") << "finished adding 300 users" << LL_ENDL; +} + // EOF diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 4740964dee..f617517392 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,12 +30,17 @@ #include <llpanel.h> #include "llcallingcard.h" // for avatar tracker +#include "llpersonmodelcommon.h" +#include "llfloaterwebcontent.h" #include "llvoiceclient.h" class LLAvatarList; +class LLAvatarListSocial; class LLAvatarName; class LLFilterEditor; class LLGroupList; +class LLPersonFolderView; +class LLSocialList; class LLMenuButton; class LLTabContainer; @@ -55,6 +60,13 @@ public: // when voice is available /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + static void idle(void * user_data); + + void addTestParticipant(); + void addParticipantToModel(LLPersonTabModel * session_model, const LLUUID& agent_id, const std::string& name); + + bool mTryToConnectToFbc; + // internals class Updater; @@ -75,6 +87,7 @@ private: void updateFriendList(); void updateNearbyList(); void updateRecentList(); + void updateFacebookList(); bool isItemsFreeOfFriends(const uuid_vec_t& uuids); @@ -106,6 +119,12 @@ private: void onGroupsViewSortMenuItemClicked(const LLSD& userdata); void onRecentViewSortMenuItemClicked(const LLSD& userdata); + void onLoginFbcButtonClicked(); + void onFacebookAppRequestClicked(); + void onFacebookAppSendClicked(); + void onFacebookTestAddClicked(); + void onFacebookCheckinClicked(); + bool onFriendsViewSortMenuItemCheck(const LLSD& userdata); bool onRecentViewSortMenuItemCheck(const LLSD& userdata); bool onNearbyViewSortMenuItemCheck(const LLSD& userdata); @@ -132,16 +151,25 @@ private: LLAvatarList* mNearbyList; LLAvatarList* mRecentList; LLGroupList* mGroupList; + LLSocialList* mFacebookFriends; + S32 mFacebookListGeneration; LLNetMap* mMiniMap; std::vector<std::string> mSavedOriginalFilters; std::vector<std::string> mSavedFilters; + LLHandle<LLView> mFBCMenuHandle; + LLHandle<LLFloater> mFbcTestBrowserHandle; Updater* mFriendListUpdater; Updater* mNearbyListUpdater; Updater* mRecentListUpdater; + Updater* mFacebookListUpdater; Updater* mButtonsUpdater; + LLMenuButton* mFBCGearButton; LLHandle< LLFloater > mPicker; + + LLPersonFolderViewModel mPersonFolderViewModel; + LLPersonFolderView* mPersonFolderView; }; #endif //LL_LLPANELPEOPLE_H diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index c53760bca1..b5c9f4a310 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,23 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) adjustParticipant(avatar_id); } +static LLFastTimer::DeclareTimer FTM_FOLDERVIEW_TEST("add test avatar agents"); + + +void LLParticipantList::addTestAvatarAgents() +{ + LLFastTimer _(FTM_FOLDERVIEW_TEST); + + LL_INFOS("LLParticipantList") << "start adding 300 users" << LL_ENDL; + + for(int i = 0; i < 300; ++i) + { + addAvatarIDExceptAgent(LLUUID().generateNewID()); + } + + LL_INFOS("LLParticipantList") << "finished adding 300 users" << LL_ENDL; +} + void LLParticipantList::adjustParticipant(const LLUUID& speaker_id) { LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id); diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 3a3ae76604..936e289c08 100755 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -50,6 +50,7 @@ public: * @param[in] avatar_id - Avatar UUID to be added into the list */ void addAvatarIDExceptAgent(const LLUUID& avatar_id); + void addTestAvatarAgents(); /** * Refreshes the participant list. diff --git a/indra/newview/llpersonfolderview.cpp b/indra/newview/llpersonfolderview.cpp new file mode 100644 index 0000000000..7e969fc96c --- /dev/null +++ b/indra/newview/llpersonfolderview.cpp @@ -0,0 +1,149 @@ +/** +* @file llpersonfolderview.cpp +* @brief Implementation of llpersonfolderview +* @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 "llpersonfolderview.h" + +#include "llpersontabview.h" + + +LLPersonFolderView::LLPersonFolderView(const Params &p) : +LLFolderView(p), + mConversationsEventStream("ConversationsEventsTwo") +{ + rename("Persons"); // For tracking! + mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLPersonFolderView::onConversationModelEvent, this, _1)); + + createPersonTabs(); +} + +LLPersonFolderView::~LLPersonFolderView() +{ + mConversationsEventStream.stopListening("ConversationsRefresh"); +} + +BOOL LLPersonFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + LLFolderViewItem * item = getCurSelectedItem(); + + //Will disable highlight on tab + if(item) + { + LLPersonTabView * person_tab= dynamic_cast<LLPersonTabView *>(item); + if(person_tab) + { + person_tab->highlight = false; + } + else + { + person_tab = dynamic_cast<LLPersonTabView *>(item->getParent()); + person_tab->highlight = false; + } + } + + mKeyboardSelection = FALSE; + mSearchString.clear(); + + LLEditMenuHandler::gEditMenuHandler = this; + + return LLView::handleMouseDown( x, y, mask ); +} + +void LLPersonFolderView::createPersonTabs() +{ + createPersonTab(LLPersonTabModel::FB_SL_NON_SL_FRIEND, "SL residents you may want to friend"); + createPersonTab(LLPersonTabModel::FB_ONLY_FRIEND, "Invite people you know to SL"); +} + +void LLPersonFolderView::createPersonTab(LLPersonTabModel::tab_type tab_type, const std::string& tab_name) +{ + //Create a person tab + LLPersonTabModel* item = new LLPersonTabModel(tab_type, tab_name, *mViewModel); + LLPersonTabView::Params params; + params.name = item->getDisplayName(); + params.root = this; + params.listener = item; + params.tool_tip = params.name; + LLPersonTabView * widget = LLUICtrlFactory::create<LLPersonTabView>(params); + widget->addToFolder(this); + + mIndexToFolderMap[tab_type] = item->getID(); + mPersonFolderModelMap[item->getID()] = item; + mPersonFolderViewMap[item->getID()] = widget; +} + +bool LLPersonFolderView::onConversationModelEvent(const LLSD &event) +{ + std::string type = event.get("type").asString(); + LLUUID folder_id = event.get("folder_id").asUUID(); + LLUUID person_id = event.get("person_id").asUUID(); + + if(type == "add_participant") + { + LLPersonTabModel * person_tab_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderModelMap[folder_id]); + LLPersonTabView * person_tab_view = dynamic_cast<LLPersonTabView *>(mPersonFolderViewMap[folder_id]); + + if(person_tab_model) + { + LLPersonModel * person_model = person_tab_model->findParticipant(person_id); + + if(person_model) + { + LLPersonView * person_view = createConversationViewParticipant(person_model); + person_view->addToFolder(person_tab_view); + } + } + } + + return false; +} + +LLPersonView * LLPersonFolderView::createConversationViewParticipant(LLPersonModel * item) +{ + LLPersonView::Params params; + + params.name = item->getDisplayName(); + params.root = this; + params.listener = item; + + //24 should be loaded from .xml somehow + params.rect = LLRect (0, 24, getRect().getWidth(), 0); + params.tool_tip = params.name; + + return LLUICtrlFactory::create<LLPersonView>(params); +} + +LLPersonTabModel * LLPersonFolderView::getPersonTabModelByIndex(LLPersonTabModel::tab_type tab_type) +{ + return mPersonFolderModelMap[mIndexToFolderMap[tab_type]]; +} + +LLPersonTabView * LLPersonFolderView::getPersonTabViewByIndex(LLPersonTabModel::tab_type tab_type) +{ + return mPersonFolderViewMap[mIndexToFolderMap[tab_type]]; +} diff --git a/indra/newview/llpersonfolderview.h b/indra/newview/llpersonfolderview.h new file mode 100644 index 0000000000..85dec6515d --- /dev/null +++ b/indra/newview/llpersonfolderview.h @@ -0,0 +1,70 @@ +/** +* @file llpersonfolderview.h +* @brief Header file for llpersonfolderview +* @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_LLPERSONFOLDERVIEW_H +#define LL_LLPERSONFOLDERVIEW_H + +#include "llevents.h" +#include "llfolderview.h" +#include "llpersonmodelcommon.h" + +class LLPersonTabView; +class LLPersonView; +class LLPersonModel; + +typedef std::map<LLUUID, LLPersonTabModel *> person_folder_model_map; +typedef std::map<LLUUID, LLPersonTabView *> person_folder_view_map; + +class LLPersonFolderView : public LLFolderView +{ +public: + struct Params : public LLInitParam::Block<Params, LLFolderView::Params> + { + Params() + {} + }; + + LLPersonFolderView(const Params &p); + ~LLPersonFolderView(); + + BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + + void createPersonTabs(); + void createPersonTab(LLPersonTabModel::tab_type tab_type, const std::string& tab_name); + bool onConversationModelEvent(const LLSD &event); + LLPersonView * createConversationViewParticipant(LLPersonModel * item); + + LLPersonTabModel * getPersonTabModelByIndex(LLPersonTabModel::tab_type tab_type); + LLPersonTabView * getPersonTabViewByIndex(LLPersonTabModel::tab_type tab_type); + + person_folder_model_map mPersonFolderModelMap; + person_folder_view_map mPersonFolderViewMap; + std::map<LLPersonTabModel::tab_type, LLUUID> mIndexToFolderMap; + LLEventStream mConversationsEventStream; +}; + +#endif // LL_LLPERSONFOLDERVIEW_H + diff --git a/indra/newview/llpersonmodelcommon.cpp b/indra/newview/llpersonmodelcommon.cpp new file mode 100644 index 0000000000..73239dcb8d --- /dev/null +++ b/indra/newview/llpersonmodelcommon.cpp @@ -0,0 +1,313 @@ +/** +* @file llavatarfolder.cpp +* @brief Implementation of llavatarfolder +* @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 "llpersonmodelcommon.h" + +#include "llevents.h" +#include "llsdutil.h" +#include "llstring.h" + +// +// LLPersonModelCommon +// + +LLPersonModelCommon::LLPersonModelCommon(std::string display_name, LLFolderViewModelInterface& root_view_model) : + LLFolderViewModelItemCommon(root_view_model), + mLabelSuffix(""), + mID(LLUUID().generateNewID()) +{ + renameItem(display_name); +} + +LLPersonModelCommon::LLPersonModelCommon(std::string display_name, std::string suffix, LLFolderViewModelInterface& root_view_model) : +LLFolderViewModelItemCommon(root_view_model), + mID(LLUUID().generateNewID()) +{ + mLabelSuffix = suffix; + renameItem(display_name); +} + +LLPersonModelCommon::LLPersonModelCommon(LLFolderViewModelInterface& root_view_model) : + LLFolderViewModelItemCommon(root_view_model), + mName(""), + mLabelSuffix(""), + mSearchableName(""), + mPrevPassedAllFilters(false), + mID(LLUUID().generateNewID()) +{ +} + +LLPersonModelCommon::~LLPersonModelCommon() +{ + +} + +BOOL LLPersonModelCommon::renameItem(const std::string& new_name) +{ + mName = new_name; + mSearchableName = new_name + " " + mLabelSuffix; + LLStringUtil::toUpper(mSearchableName); + return TRUE; +} + +void LLPersonModelCommon::postEvent(const std::string& event_type, LLPersonTabModel* folder, LLPersonModel* person) +{ + LLUUID folder_id = folder->getID(); + LLUUID person_id = person->getID(); + LLSD event(LLSDMap("type", event_type)("folder_id", folder_id)("person_id", person_id)); + LLEventPumps::instance().obtain("ConversationsEventsTwo").post(event); +} + +// Virtual action callbacks +void LLPersonModelCommon::performAction(LLInventoryModel* model, std::string action) +{ +} + +void LLPersonModelCommon::openItem( void ) +{ +} + +void LLPersonModelCommon::closeItem( void ) +{ +} + +void LLPersonModelCommon::previewItem( void ) +{ +} + +void LLPersonModelCommon::showProperties(void) +{ +} + +bool LLPersonModelCommon::filter( LLFolderViewFilter& filter) +{ +/* + Hack: for the moment, we always apply the filter if we're called + if (!filter.isModified()) + { + llinfos << "Merov : LLPersonModelCommon::filter, exit, no modif" << llendl; + return true; + } + */ + if (!mChildren.empty()) + { + // If the current instance has children, it's a "person folder" and always passes filters (we do not filter out empty folders) + setPassedFilter(1, filter.getCurrentGeneration()); + // Call filter recursively on all children + for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); + iter != end_iter; + ++iter) + { + LLPersonModelCommon* item = dynamic_cast<LLPersonModelCommon*>(*iter); + item->filter(filter); + } + } + else + { + // If there's no children, the current instance is a person and we check and set the passed filter flag on it + const bool passed_filter = filter.check(this); + setPassedFilter(passed_filter, filter.getCurrentGeneration(), filter.getStringMatchOffset(this), filter.getFilterStringSize()); + } + + filter.clearModified(); + return true; +} + +void LLPersonModelCommon::setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset, std::string::size_type string_size) +{ + LLFolderViewModelItemCommon::setPassedFilter(passed, filter_generation, string_offset, string_size); + bool before = mPrevPassedAllFilters; + mPrevPassedAllFilters = passedFilter(filter_generation); + + if (before != mPrevPassedAllFilters) + { + // Need to rearrange the folder if the filtered state of the item changed + LLFolderViewFolder* parent_folder = mFolderViewItem->getParentFolder(); + if (parent_folder) + { + parent_folder->requestArrange(); + } + } +} + + +// +// LLPersonTabModel +// + +LLPersonTabModel::LLPersonTabModel(tab_type tab_type, std::string display_name, LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(display_name,root_view_model), +mTabType(tab_type) +{ + +} + +LLPersonTabModel::LLPersonTabModel(LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(root_view_model) +{ + +} + +void LLPersonTabModel::addParticipant(LLPersonModel* participant) +{ + addChild(participant); + postEvent("add_participant", this, participant); +} + +void LLPersonTabModel::removeParticipant(LLPersonModel* participant) +{ + removeChild(participant); + postEvent("remove_participant", this, participant); +} + +void LLPersonTabModel::removeParticipant(const LLUUID& participant_id) +{ + LLPersonModel* participant = findParticipant(participant_id); + if (participant) + { + removeParticipant(participant); + } +} + +void LLPersonTabModel::clearParticipants() +{ + clearChildren(); +} + +LLPersonModel* LLPersonTabModel::findParticipant(const LLUUID& person_id) +{ + LLPersonModel * person_model = NULL; + child_list_t::iterator iter; + + for(iter = mChildren.begin(); iter != mChildren.end(); ++iter) + { + person_model = static_cast<LLPersonModel *>(*iter); + + if(person_model->getID() == person_id) + { + break; + } + } + + return iter == mChildren.end() ? NULL : person_model; +} + +// +// LLPersonModel +// + +LLPersonModel::LLPersonModel(const LLUUID& agent_id, const std::string display_name, const std::string suffix, LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(display_name, suffix, root_view_model), +mAgentID(agent_id) +{ +} + +LLPersonModel::LLPersonModel(LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(root_view_model), +mAgentID(LLUUID(NULL)) +{ +} + +LLUUID LLPersonModel::getAgentID() +{ + return mAgentID; +} + +// +// LLPersonViewFilter +// + +LLPersonViewFilter::LLPersonViewFilter() : + mEmptyLookupMessage(""), + mFilterSubString(""), + mName(""), + mFilterModified(FILTER_NONE), + mCurrentGeneration(0) +{ +} + +void LLPersonViewFilter::setFilterSubString(const std::string& string) +{ + std::string filter_sub_string_new = string; + LLStringUtil::trimHead(filter_sub_string_new); + LLStringUtil::toUpper(filter_sub_string_new); + + if (mFilterSubString != filter_sub_string_new) + { + // *TODO : Add logic to support more and less restrictive filtering + setModified(FILTER_RESTART); + mFilterSubString = filter_sub_string_new; + } +} + +bool LLPersonViewFilter::showAllResults() const +{ + return mFilterSubString.size() > 0; +} + +bool LLPersonViewFilter::check(const LLFolderViewModelItem* item) +{ + return (mFilterSubString.size() ? (item->getSearchableName().find(mFilterSubString) != std::string::npos) : true); +} + +std::string::size_type LLPersonViewFilter::getStringMatchOffset(LLFolderViewModelItem* item) const +{ + return mFilterSubString.size() ? item->getSearchableName().find(mFilterSubString) : std::string::npos; +} + +std::string::size_type LLPersonViewFilter::getFilterStringSize() const +{ + return mFilterSubString.size(); +} + +bool LLPersonViewFilter::isActive() const +{ + return mFilterSubString.size(); +} + +bool LLPersonViewFilter::isModified() const +{ + return mFilterModified != FILTER_NONE; +} + +void LLPersonViewFilter::clearModified() +{ + mFilterModified = FILTER_NONE; +} + +void LLPersonViewFilter::setEmptyLookupMessage(const std::string& message) +{ + mEmptyLookupMessage = message; +} + +std::string LLPersonViewFilter::getEmptyLookupMessage() const +{ + return mEmptyLookupMessage; +} + diff --git a/indra/newview/llpersonmodelcommon.h b/indra/newview/llpersonmodelcommon.h new file mode 100644 index 0000000000..74598eaee0 --- /dev/null +++ b/indra/newview/llpersonmodelcommon.h @@ -0,0 +1,248 @@ +/** +* @file llavatarfolder.h +* @brief Header file for llavatarfolder +* @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_LLPERSONMODELCOMMON_H +#define LL_LLPERSONMODELCOMMON_H + +#include "../llui/llfolderviewitem.h" +#include "../llui/llfolderviewmodel.h" + +class LLPersonTabModel; +class LLPersonModel; + +// Conversation items: we hold a list of those and create an LLFolderViewItem widget for each +// that we tuck into the mConversationsListPanel. +class LLPersonModelCommon : public LLFolderViewModelItemCommon +{ +public: + + LLPersonModelCommon(std::string name, LLFolderViewModelInterface& root_view_model); + LLPersonModelCommon(std::string display_name, std::string suffix, LLFolderViewModelInterface& root_view_model); + LLPersonModelCommon(LLFolderViewModelInterface& root_view_model); + virtual ~LLPersonModelCommon(); + + // Stub those things we won't really be using in this conversation context + virtual const std::string& getName() const { return mName; } + virtual const std::string& getDisplayName() const { return mName; } + virtual const std::string& getSearchableName() const { return mSearchableName; } + + virtual LLPointer<LLUIImage> getIcon() const { return NULL; } + virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } + virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } + virtual std::string getLabelSuffix() const { return mLabelSuffix; } + virtual BOOL isItemRenameable() const { return TRUE; } + virtual BOOL renameItem(const std::string& new_name); + virtual BOOL isItemMovable( void ) const { return FALSE; } + virtual BOOL isItemRemovable( void ) const { return FALSE; } + virtual BOOL isItemInTrash( void) const { return FALSE; } + virtual BOOL removeItem() { return FALSE; } + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) { } + virtual void move( LLFolderViewModelItem* parent_listener ) { } + virtual BOOL isItemCopyable() const { return FALSE; } + virtual BOOL copyToClipboard() const { return FALSE; } + virtual BOOL cutToClipboard() const { return FALSE; } + virtual BOOL isClipboardPasteable() const { return FALSE; } + virtual void pasteFromClipboard() { } + virtual void pasteLinkFromClipboard() { } + virtual void buildContextMenu(LLMenuGL& menu, U32 flags) { } + virtual BOOL isUpToDate() const { return TRUE; } + virtual bool hasChildren() const { return FALSE; } + + virtual bool potentiallyVisible() { return true; } + + virtual bool filter( LLFolderViewFilter& filter); + + virtual bool descendantsPassedFilter(S32 filter_generation = -1) { return true; } + virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0); + virtual bool passedFilter(S32 filter_generation = -1) { return mPassedFilter; } + + // The action callbacks + virtual void performAction(LLInventoryModel* model, std::string action); + virtual void openItem( void ); + virtual void closeItem( void ); + virtual void previewItem( void ); + virtual void selectItem(void) { } + virtual void showProperties(void); + + // This method will be called to determine if a drop can be + // performed, and will set drop to TRUE if a drop is + // requested. + // Returns TRUE if a drop is possible/happened, FALSE otherwise. + virtual BOOL dragOrDrop(MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + std::string& tooltip_msg) { return FALSE; } + + const LLUUID& getID() {return mID;} + void postEvent(const std::string& event_type, LLPersonTabModel* session, LLPersonModel* participant); + +protected: + + std::string mName; // Name of the person + std::string mLabelSuffix; + std::string mSearchableName; // Name used in string matching for this person + bool mPrevPassedAllFilters; + LLUUID mID; +}; + +class LLPersonTabModel : public LLPersonModelCommon +{ +public: + enum tab_type + { + FB_SL_NON_SL_FRIEND, + FB_ONLY_FRIEND, + }; + + LLPersonTabModel(tab_type tab_type, std::string display_name, LLFolderViewModelInterface& root_view_model); + LLPersonTabModel(LLFolderViewModelInterface& root_view_model); + + LLPointer<LLUIImage> getIcon() const { return NULL; } + void addParticipant(LLPersonModel* participant); + void removeParticipant(LLPersonModel* participant); + void removeParticipant(const LLUUID& participant_id); + void clearParticipants(); + LLPersonModel* findParticipant(const LLUUID& person_id); + + tab_type mTabType; + +private: +}; + +class LLPersonModel : public LLPersonModelCommon +{ +public: + LLPersonModel(const LLUUID& agent_id, const std::string display_name, const std::string suffix, LLFolderViewModelInterface& root_view_model); + LLPersonModel(LLFolderViewModelInterface& root_view_model); + + LLUUID getAgentID(); + +private: + LLUUID mAgentID; +}; + +// Filtering functional object + +class LLPersonViewFilter : public LLFolderViewFilter +{ +public: + + enum ESortOrderType + { + SO_NAME = 0, // Sort by name + SO_ONLINE_STATUS = 0x1 // Sort by online status (i.e. online or not) + }; + // Default sort order is by name + static const U32 SO_DEFAULT = SO_NAME; + + LLPersonViewFilter(); + ~LLPersonViewFilter() {} + + // +-------------------------------------------------------------------+ + // + Execution And Results + // +-------------------------------------------------------------------+ + bool check(const LLFolderViewModelItem* item); + bool checkFolder(const LLFolderViewModelItem* folder) const { return true; } + + void setEmptyLookupMessage(const std::string& message); + std::string getEmptyLookupMessage() const; + + bool showAllResults() const; + + std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const; + std::string::size_type getFilterStringSize() const; + + // +-------------------------------------------------------------------+ + // + Status + // +-------------------------------------------------------------------+ + bool isActive() const; + bool isModified() const; + void clearModified(); + const std::string& getName() const { return mName; } + const std::string& getFilterText() { return mName; } + void setModified(EFilterModified behavior = FILTER_RESTART) { mFilterModified = behavior; mCurrentGeneration++; } + + // +-------------------------------------------------------------------+ + // + Time + // +-------------------------------------------------------------------+ + // Note : we currently filter the whole person list at once, no need to timeout then. + void resetTime(S32 timeout) { } + bool isTimedOut() { return false; } + + // +-------------------------------------------------------------------+ + // + Default + // +-------------------------------------------------------------------+ + // Note : we don't support runtime default setting for person filter + bool isDefault() const { return !isActive(); } + bool isNotDefault() const { return isActive(); } + void markDefault() { } + void resetDefault() { setModified(); } + + // +-------------------------------------------------------------------+ + // + Generation + // +-------------------------------------------------------------------+ + // Note : For the moment, we do not support restrictive filtering so all generation indexes are pointing to the current generation + S32 getCurrentGeneration() const { return mCurrentGeneration; } + S32 getFirstSuccessGeneration() const { return mCurrentGeneration; } + S32 getFirstRequiredGeneration() const { return mCurrentGeneration; } + + // Non Virtual Methods (i.e. specific to this class) + void setFilterSubString(const std::string& string); + +private: + std::string mName; + std::string mEmptyLookupMessage; + std::string mFilterSubString; + EFilterModified mFilterModified; + S32 mCurrentGeneration; +}; + +class LLPersonViewSort +{ +public: + LLPersonViewSort(U32 order = LLPersonViewFilter::SO_DEFAULT) : mSortOrder(order) { } + + bool operator()(const LLPersonModelCommon* const& a, const LLPersonModelCommon* const& b) const {return false;} + operator U32() const { return mSortOrder; } +private: + // Note: we're treating this value as a sort order bitmask as done in other places in the code (e.g. inventory) + U32 mSortOrder; +}; + + +class LLPersonFolderViewModel + : public LLFolderViewModel<LLPersonViewSort, LLPersonModelCommon, LLPersonModelCommon, LLPersonViewFilter> +{ +public: + typedef LLFolderViewModel<LLPersonViewSort, LLPersonModelCommon, LLPersonModelCommon, LLPersonViewFilter> base_t; + + void sort(LLFolderViewFolder* folder) { base_t::sort(folder);} + bool startDrag(std::vector<LLFolderViewModelItem*>& items) { return false; } // We do not allow drag of conversation items +}; + + +#endif // LL_LLPERSONMODELCOMMON_H + diff --git a/indra/newview/llpersontabview.cpp b/indra/newview/llpersontabview.cpp new file mode 100644 index 0000000000..34ffc6ffce --- /dev/null +++ b/indra/newview/llpersontabview.cpp @@ -0,0 +1,465 @@ +/** +* @file llpersontabview.cpp +* @brief Implementation of llpersontabview +* @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 "llpersontabview.h" + +#include "llavataractions.h" +#include "llfloaterreg.h" +#include "llpersonmodelcommon.h" + +static LLDefaultChildRegistry::Register<LLPersonTabView> r_person_tab_view("person_tab_view"); + +const LLColor4U DEFAULT_WHITE(255, 255, 255); + +LLPersonTabView::Params::Params() +{} + +LLPersonTabView::LLPersonTabView(const LLPersonTabView::Params& p) : +LLFolderViewFolder(p), +highlight(false), +mImageHeader(LLUI::getUIImage("Accordion_Off")), +mImageHeaderOver(LLUI::getUIImage("Accordion_Over")), +mImageHeaderFocused(LLUI::getUIImage("Accordion_Selected")) +{ +} + +S32 LLPersonTabView::getLabelXPos() +{ + return getIndentation() + mArrowSize + 15;//Should be a .xml variable but causes crash; +} + +LLPersonTabView::~LLPersonTabView() +{ + +} + +BOOL LLPersonTabView::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + bool selected_item = LLFolderViewFolder::handleMouseDown(x, y, mask); + + if(selected_item) + { + gFocusMgr.setKeyboardFocus( this ); + highlight = true; + } + + return selected_item; +} + +void LLPersonTabView::draw() +{ + static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + static const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLPersonTabView>(); + + const LLFontGL * font = LLFontGL::getFontSansSerif(); + F32 text_left = (F32)getLabelXPos(); + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; + LLColor4 color = sFgColor; + F32 right_x = 0; + + drawHighlight(); + updateLabelRotation(); + drawOpenFolderArrow(default_params, sFgColor); + + drawLabel(font, text_left, y, color, right_x); + + LLView::draw(); +} + +void LLPersonTabView::drawHighlight() +{ + S32 width = getRect().getWidth(); + S32 height = mItemHeight; + S32 x = 1; + S32 y = getRect().getHeight() - mItemHeight; + + if(highlight) + { + mImageHeaderFocused->draw(x,y,width,height); + } + else + { + mImageHeader->draw(x,y,width,height); + } + + if(mIsMouseOverTitle) + { + mImageHeaderOver->draw(x,y,width,height); + } + +} + +// +// LLPersonView +// + +static LLDefaultChildRegistry::Register<LLPersonView> r_person_view("person_view"); + +bool LLPersonView::sChildrenWidthsInitialized = false; +ChildWidthVec LLPersonView::mChildWidthVec; + +LLPersonView::Params::Params() : +facebook_icon("facebook_icon"), +avatar_icon("avatar_icon"), +last_interaction_time_textbox("last_interaction_time_textbox"), +permission_edit_theirs_icon("permission_edit_theirs_icon"), +permission_edit_mine_icon("permission_edit_mine_icon"), +permission_map_icon("permission_map_icon"), +permission_online_icon("permission_online_icon"), +info_btn("info_btn"), +profile_btn("profile_btn"), +output_monitor("output_monitor") +{} + +LLPersonView::LLPersonView(const LLPersonView::Params& p) : +LLFolderViewItem(p), +mImageOver(LLUI::getUIImage("ListItem_Over")), +mImageSelected(LLUI::getUIImage("ListItem_Select")), +mFacebookIcon(NULL), +mAvatarIcon(NULL), +mLastInteractionTimeTextbox(NULL), +mPermissionEditTheirsIcon(NULL), +mPermissionEditMineIcon(NULL), +mPermissionMapIcon(NULL), +mPermissionOnlineIcon(NULL), +mInfoBtn(NULL), +mProfileBtn(NULL), +mOutputMonitorCtrl(NULL) +{ +} + +S32 LLPersonView::getLabelXPos() +{ + S32 label_x_pos; + + if(mAvatarIcon->getVisible()) + { + label_x_pos = getIndentation() + mAvatarIcon->getRect().getWidth() + mIconPad; + } + else + { + label_x_pos = getIndentation() + mFacebookIcon->getRect().getWidth() + mIconPad; + } + + + return label_x_pos; +} + +void LLPersonView::addToFolder(LLFolderViewFolder * person_folder_view) +{ + const LLFontGL * font = LLFontGL::getFontSansSerifSmall(); + + LLFolderViewItem::addToFolder(person_folder_view); + //Added item to folder could change folder's mHasVisibleChildren flag so call arrange + person_folder_view->requestArrange(); + + mPersonTabModel = static_cast<LLPersonTabModel *>(getParentFolder()->getViewModelItem()); + + if(mPersonTabModel->mTabType == LLPersonTabModel::FB_SL_NON_SL_FRIEND) + { + mAvatarIcon->setVisible(TRUE); + mFacebookIcon->setVisible(TRUE); + + S32 label_width = font->getWidth(mLabel); + F32 text_left = (F32)getLabelXPos(); + + LLRect mFacebookIconRect = mFacebookIcon->getRect(); + S32 new_left = text_left + label_width + 7; + mFacebookIconRect.set(new_left, + mFacebookIconRect.mTop, + new_left + mFacebookIconRect.getWidth(), + mFacebookIconRect.mBottom); + mFacebookIcon->setRect(mFacebookIconRect); + } + else if(mPersonTabModel->mTabType == LLPersonTabModel::FB_ONLY_FRIEND) + { + mFacebookIcon->setVisible(TRUE); + } + +} + +LLPersonView::~LLPersonView() +{ + +} + +BOOL LLPersonView::postBuild() +{ + if(!sChildrenWidthsInitialized) + { + initChildrenWidthVec(this); + sChildrenWidthsInitialized = true; + } + + initChildVec(); + updateChildren(); + + LLPersonModel * person_model = static_cast<LLPersonModel *>(getViewModelItem()); + + mAvatarIcon->setValue(person_model->getAgentID()); + mInfoBtn->setClickedCallback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", person_model->getAgentID()), FALSE)); + mProfileBtn->setClickedCallback(boost::bind(&LLAvatarActions::showProfile, person_model->getAgentID())); + + return LLFolderViewItem::postBuild(); +} + +void LLPersonView::onMouseEnter(S32 x, S32 y, MASK mask) +{ + if(mPersonTabModel->mTabType == LLPersonTabModel::FB_SL_NON_SL_FRIEND) + { + mInfoBtn->setVisible(TRUE); + mProfileBtn->setVisible(TRUE); + } + + updateChildren(); + LLFolderViewItem::onMouseEnter(x, y, mask); +} + +void LLPersonView::onMouseLeave(S32 x, S32 y, MASK mask) +{ + if(mPersonTabModel->mTabType == LLPersonTabModel::FB_SL_NON_SL_FRIEND) + { + mInfoBtn->setVisible(FALSE); + mProfileBtn->setVisible(FALSE); + } + + updateChildren(); + LLFolderViewItem::onMouseLeave(x, y, mask); +} + +BOOL LLPersonView::handleMouseDown( S32 x, S32 y, MASK mask) +{ + if(!LLView::childrenHandleMouseDown(x, y, mask)) + { + gFocusMgr.setMouseCapture( this ); + } + + if (!mIsSelected) + { + if(mask & MASK_CONTROL) + { + getRoot()->changeSelection(this, !mIsSelected); + } + else if (mask & MASK_SHIFT) + { + getParentFolder()->extendSelectionTo(this); + } + else + { + getRoot()->setSelection(this, FALSE); + } + make_ui_sound("UISndClick"); + } + else + { + // If selected, we reserve the decision of deselecting/reselecting to the mouse up moment. + // This is necessary so we maintain selection consistent when starting a drag. + mSelectPending = TRUE; + } + + mDragStartX = x; + mDragStartY = y; + return TRUE; +} + +void LLPersonView::draw() +{ + static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); + + const LLFontGL * font = LLFontGL::getFontSansSerifSmall(); + F32 text_left = (F32)getLabelXPos(); + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; + LLColor4 color = mIsSelected ? sHighlightFgColor : sFgColor; + F32 right_x = 0; + + drawHighlight(); + if(mLabel.length()) + { + drawLabel(mLabel, font, text_left, y, color, right_x); + } + if(mLabelSuffix.length()) + { + drawLabel(mLabelSuffix, font, mFacebookIcon->getRect().mRight + 7, y, color, right_x); + } + + LLView::draw(); +} + +void LLPersonView::drawHighlight() +{ + static LLUIColor outline_color = LLUIColorTable::instance().getColor("EmphasisColor", DEFAULT_WHITE); + + S32 width = getRect().getWidth(); + S32 height = mItemHeight; + S32 x = 1; + S32 y = 0; + + if(mIsSelected) + { + mImageSelected->draw(x, y, width, height); + //Draw outline + gl_rect_2d(x, + height, + width, + y, + outline_color, FALSE); + } + + if(mIsMouseOverTitle) + { + mImageOver->draw(x, y, width, height); + } +} + +void LLPersonView::drawLabel(const std::string text, const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x) +{ + font->renderUTF8(text, 0, x, y, color, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, TRUE); +} + +void LLPersonView::initFromParams(const LLPersonView::Params & params) +{ + LLIconCtrl::Params facebook_icon_params(params.facebook_icon()); + applyXUILayout(facebook_icon_params, this); + mFacebookIcon = LLUICtrlFactory::create<LLIconCtrl>(facebook_icon_params); + addChild(mFacebookIcon); + + LLAvatarIconCtrl::Params avatar_icon_params(params.avatar_icon()); + applyXUILayout(avatar_icon_params, this); + mAvatarIcon = LLUICtrlFactory::create<LLAvatarIconCtrl>(avatar_icon_params); + addChild(mAvatarIcon); + + LLTextBox::Params last_interaction_time_textbox(params.last_interaction_time_textbox()); + applyXUILayout(last_interaction_time_textbox, this); + mLastInteractionTimeTextbox = LLUICtrlFactory::create<LLTextBox>(last_interaction_time_textbox); + addChild(mLastInteractionTimeTextbox); + + LLIconCtrl::Params permission_edit_theirs_icon(params.permission_edit_theirs_icon()); + applyXUILayout(permission_edit_theirs_icon, this); + mPermissionEditTheirsIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_edit_theirs_icon); + addChild(mPermissionEditTheirsIcon); + + LLIconCtrl::Params permission_edit_mine_icon(params.permission_edit_mine_icon()); + applyXUILayout(permission_edit_mine_icon, this); + mPermissionEditMineIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_edit_mine_icon); + addChild(mPermissionEditMineIcon); + + LLIconCtrl::Params permission_map_icon(params.permission_map_icon()); + applyXUILayout(permission_map_icon, this); + mPermissionMapIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_map_icon); + addChild(mPermissionMapIcon); + + LLIconCtrl::Params permission_online_icon(params.permission_online_icon()); + applyXUILayout(permission_online_icon, this); + mPermissionOnlineIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_online_icon); + addChild(mPermissionOnlineIcon); + + LLButton::Params info_btn(params.info_btn()); + applyXUILayout(info_btn, this); + mInfoBtn = LLUICtrlFactory::create<LLButton>(info_btn); + addChild(mInfoBtn); + + LLButton::Params profile_btn(params.profile_btn()); + applyXUILayout(profile_btn, this); + mProfileBtn = LLUICtrlFactory::create<LLButton>(profile_btn); + addChild(mProfileBtn); + + LLOutputMonitorCtrl::Params output_monitor(params.output_monitor()); + applyXUILayout(output_monitor, this); + mOutputMonitorCtrl = LLUICtrlFactory::create<LLOutputMonitorCtrl>(output_monitor); + addChild(mOutputMonitorCtrl); +} + +void LLPersonView::initChildrenWidthVec(LLPersonView* self) +{ + S32 output_monitor_width = self->getRect().getWidth() - self->mOutputMonitorCtrl->getRect().mLeft; + S32 profile_btn_width = self->mOutputMonitorCtrl->getRect().mLeft - self->mProfileBtn->getRect().mLeft; + S32 info_btn_width = self->mProfileBtn->getRect().mLeft - self->mInfoBtn->getRect().mLeft; + S32 permission_online_icon_width = self->mInfoBtn->getRect().mLeft - self->mPermissionOnlineIcon->getRect().mLeft; + S32 permissions_map_icon_width = self->mPermissionOnlineIcon->getRect().mLeft - self->mPermissionMapIcon->getRect().mLeft; + S32 permission_edit_mine_icon_width = self->mPermissionMapIcon->getRect().mLeft - self->mPermissionEditMineIcon->getRect().mLeft; + S32 permission_edit_theirs_icon_width = self->mPermissionEditMineIcon->getRect().mLeft - self->mPermissionEditTheirsIcon->getRect().mLeft; + S32 last_interaction_time_textbox_width = self->mPermissionEditTheirsIcon->getRect().mLeft - self->mLastInteractionTimeTextbox->getRect().mLeft; + + self->mChildWidthVec.push_back(output_monitor_width); + self->mChildWidthVec.push_back(profile_btn_width); + self->mChildWidthVec.push_back(info_btn_width); + self->mChildWidthVec.push_back(permission_online_icon_width); + self->mChildWidthVec.push_back(permissions_map_icon_width); + self->mChildWidthVec.push_back(permission_edit_mine_icon_width); + self->mChildWidthVec.push_back(permission_edit_theirs_icon_width); + self->mChildWidthVec.push_back(last_interaction_time_textbox_width); +} + +void LLPersonView::initChildVec() +{ + mChildVec.push_back(mOutputMonitorCtrl); + mChildVec.push_back(mProfileBtn); + mChildVec.push_back(mInfoBtn); + mChildVec.push_back(mPermissionOnlineIcon); + mChildVec.push_back(mPermissionMapIcon); + mChildVec.push_back(mPermissionEditMineIcon); + mChildVec.push_back(mPermissionEditTheirsIcon); + mChildVec.push_back(mLastInteractionTimeTextbox); +} + +void LLPersonView::updateChildren() +{ + mLabelPaddingRight = 0; + LLView * control; + S32 control_width; + LLRect control_rect; + + llassert(mChildWidthVec.size() == mChildVec.size()); + + for(S32 i = 0; i < mChildWidthVec.size(); ++i) + { + control = mChildVec[i]; + + if(!control->getVisible()) + { + continue; + } + + control_width = mChildWidthVec[i]; + mLabelPaddingRight += control_width; + + control_rect = control->getRect(); + control_rect.setLeftTopAndSize( + getLocalRect().getWidth() - mLabelPaddingRight, + control_rect.mTop, + control_rect.getWidth(), + control_rect.getHeight()); + + control->setShape(control_rect); + + } +} diff --git a/indra/newview/llpersontabview.h b/indra/newview/llpersontabview.h new file mode 100644 index 0000000000..6f244c2794 --- /dev/null +++ b/indra/newview/llpersontabview.h @@ -0,0 +1,151 @@ +/** +* @file llpersontabview.h +* @brief Header file for llpersontabview +* @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_LLPERSONTABVIEW_H +#define LL_LLPERSONTABVIEW_H + +#include "llavatariconctrl.h" +#include "llbutton.h" +#include "llfolderviewitem.h" +#include "lloutputmonitorctrl.h" +#include "lltextbox.h" + +class LLPersonTabModel; + +class LLPersonTabView : public LLFolderViewFolder +{ + +public: + + struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params> + { + Params(); + }; + + LLPersonTabView(const LLPersonTabView::Params& p); + virtual ~LLPersonTabView(); + + S32 getLabelXPos(); + bool highlight; + + BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + +protected: + void draw(); + void drawHighlight(); + +private: + + // Background images + LLPointer<LLUIImage> mImageHeader; + LLPointer<LLUIImage> mImageHeaderOver; + LLPointer<LLUIImage> mImageHeaderFocused; + +}; + +typedef std::vector<S32> ChildWidthVec; +typedef std::vector<LLView *> ChildVec; + +class LLPersonView : public LLFolderViewItem +{ + +public: + + struct Params : public LLInitParam::Block<Params, LLFolderViewItem::Params> + { + Params(); + Optional<LLIconCtrl::Params> facebook_icon; + Optional<LLAvatarIconCtrl::Params> avatar_icon; + Optional<LLTextBox::Params> last_interaction_time_textbox; + Optional<LLIconCtrl::Params> permission_edit_theirs_icon; + Optional<LLIconCtrl::Params> permission_edit_mine_icon; + Optional<LLIconCtrl::Params> permission_map_icon; + Optional<LLIconCtrl::Params> permission_online_icon; + Optional<LLButton::Params> info_btn; + Optional<LLButton::Params> profile_btn; + Optional<LLOutputMonitorCtrl::Params> output_monitor; + }; + + LLPersonView(const LLPersonView::Params& p); + virtual ~LLPersonView(); + + S32 getLabelXPos(); + void addToFolder(LLFolderViewFolder * person_folder_view); + void initFromParams(const LLPersonView::Params & params); + BOOL postBuild(); + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + BOOL handleMouseDown( S32 x, S32 y, MASK mask); + +protected: + + void draw(); + void drawHighlight(); + void drawLabel(const std::string text, const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); + +private: + + //Short-cut to tab model + LLPersonTabModel * mPersonTabModel; + + LLPointer<LLUIImage> mImageOver; + LLPointer<LLUIImage> mImageSelected; + LLIconCtrl * mFacebookIcon; + LLAvatarIconCtrl* mAvatarIcon; + LLTextBox * mLastInteractionTimeTextbox; + LLIconCtrl * mPermissionEditTheirsIcon; + LLIconCtrl * mPermissionEditMineIcon; + LLIconCtrl * mPermissionMapIcon; + LLIconCtrl * mPermissionOnlineIcon; + LLButton * mInfoBtn; + LLButton * mProfileBtn; + LLOutputMonitorCtrl * mOutputMonitorCtrl; + + typedef enum e_avatar_item_child { + ALIC_SPEAKER_INDICATOR, + ALIC_PROFILE_BUTTON, + ALIC_INFO_BUTTON, + ALIC_PERMISSION_ONLINE, + ALIC_PERMISSION_MAP, + ALIC_PERMISSION_EDIT_MINE, + ALIC_PERMISSION_EDIT_THEIRS, + ALIC_INTERACTION_TIME, + ALIC_COUNT, + } EAvatarListItemChildIndex; + + //Widths of controls are same for every instance so can be static + static ChildWidthVec mChildWidthVec; + //Control pointers are different for each instance so non-static + ChildVec mChildVec; + + static bool sChildrenWidthsInitialized; + static void initChildrenWidthVec(LLPersonView* self); + void initChildVec(); + void updateChildren(); +}; + +#endif // LL_LLPERSONTABVIEW_H + diff --git a/indra/newview/llsociallist.cpp b/indra/newview/llsociallist.cpp new file mode 100644 index 0000000000..9f827cf04f --- /dev/null +++ b/indra/newview/llsociallist.cpp @@ -0,0 +1,154 @@ +/** +* @file llsociallist.cpp +* @brief Implementation of llsociallist +* @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 "llsociallist.h" + +#include "llavataractions.h" +#include "llfloaterreg.h" +#include "llavatariconctrl.h" +#include "llavatarnamecache.h" +#include "lloutputmonitorctrl.h" +#include "lltextutil.h" + +static LLDefaultChildRegistry::Register<LLSocialList> r("social_list"); + +LLSocialList::LLSocialList(const Params&p) : LLFlatListViewEx(p) +{ + +} + +LLSocialList::~LLSocialList() +{ + +} + +void LLSocialList::draw() +{ + LLFlatListView::draw(); +} + +void LLSocialList::refresh() +{ + +} + +void LLSocialList::addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos) +{ + LLSocialListItem * item = new LLSocialListItem(); + LLAvatarName avatar_name; + bool has_avatar_name = id.notNull() && LLAvatarNameCache::get(id, &avatar_name); + + item->mAvatarId = id; + if(id.notNull()) + { + item->mIcon->setValue(id); + } + + item->setName(has_avatar_name ? name + " (" + avatar_name.getDisplayName() + ")" : name, mNameFilter); + addItem(item, id, pos); +} + +LLSocialListItem::LLSocialListItem() +{ + buildFromFile("panel_avatar_list_item.xml"); +} + +LLSocialListItem::~LLSocialListItem() +{ + +} + +BOOL LLSocialListItem::postBuild() +{ + mIcon = getChild<LLAvatarIconCtrl>("avatar_icon"); + mLabelTextBox = getChild<LLTextBox>("avatar_name"); + + mLastInteractionTime = getChild<LLTextBox>("last_interaction"); + mIconPermissionOnline = getChild<LLIconCtrl>("permission_online_icon"); + mIconPermissionMap = getChild<LLIconCtrl>("permission_map_icon"); + mIconPermissionEditMine = getChild<LLIconCtrl>("permission_edit_mine_icon"); + mIconPermissionEditTheirs = getChild<LLIconCtrl>("permission_edit_theirs_icon"); + mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator"); + mInfoBtn = getChild<LLButton>("info_btn"); + mProfileBtn = getChild<LLButton>("profile_btn"); + + mLastInteractionTime->setVisible(false); + mIconPermissionOnline->setVisible(false); + mIconPermissionMap->setVisible(false); + mIconPermissionEditMine->setVisible(false); + mIconPermissionEditTheirs->setVisible(false); + mSpeakingIndicator->setVisible(false); + mInfoBtn->setVisible(false); + mProfileBtn->setVisible(false); + + mInfoBtn->setClickedCallback(boost::bind(&LLSocialListItem::onInfoBtnClick, this)); + mProfileBtn->setClickedCallback(boost::bind(&LLSocialListItem::onProfileBtnClick, this)); + + return TRUE; +} + +void LLSocialListItem::setName(const std::string& name, const std::string& highlight) +{ + mLabel = name; + LLTextUtil::textboxSetHighlightedVal(mLabelTextBox, mLabelTextBoxStyle, name, highlight); +} + +void LLSocialListItem::setValue(const LLSD& value) +{ + getChildView("selected_icon")->setVisible( value["selected"]); +} + +void LLSocialListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible( true); + mInfoBtn->setVisible(true); + mProfileBtn->setVisible(true); + + LLPanel::onMouseEnter(x, y, mask); +} + +void LLSocialListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible( false); + mInfoBtn->setVisible(false); + mProfileBtn->setVisible(false); + + LLPanel::onMouseLeave(x, y, mask); +} + +void LLSocialListItem::onInfoBtnClick() +{ + LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarId)); +} + +void LLSocialListItem::onProfileBtnClick() +{ + LLAvatarActions::showProfile(mAvatarId); +} diff --git a/indra/newview/llsociallist.h b/indra/newview/llsociallist.h new file mode 100644 index 0000000000..bc667fc400 --- /dev/null +++ b/indra/newview/llsociallist.h @@ -0,0 +1,102 @@ +/** +* @file llsociallist.h +* @brief Header file for llsociallist +* @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_LLSOCIALLIST_H +#define LL_LLSOCIALLIST_H + +#include "llflatlistview.h" +#include "llstyle.h" + + +/** + * Generic list of avatars. + * + * Updates itself when it's dirty, using optional name filter. + * To initiate update, modify the UUID list and call setDirty(). + * + * @see getIDs() + * @see setDirty() + * @see setNameFilter() + */ + +class LLAvatarIconCtrl; +class LLIconCtrl; +class LLOutputMonitorCtrl; + +class LLSocialList : public LLFlatListViewEx +{ +public: + + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + }; + + LLSocialList(const Params&p); + virtual ~LLSocialList(); + + virtual void draw(); + void refresh(); + void addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos = ADD_BOTTOM); + + + + std::string mNameFilter; +}; + +class LLSocialListItem : public LLPanel +{ + public: + LLSocialListItem(); + ~LLSocialListItem(); + + BOOL postBuild(); + void setName(const std::string& name, const std::string& highlight = LLStringUtil::null); + void setValue(const LLSD& value); + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + void onInfoBtnClick(); + void onProfileBtnClick(); + + LLUUID mAvatarId; + + LLTextBox * mLabelTextBox; + std::string mLabel; + LLStyle::Params mLabelTextBoxStyle; + + + LLAvatarIconCtrl * mIcon; + LLTextBox * mLastInteractionTime; + LLIconCtrl * mIconPermissionOnline; + LLIconCtrl * mIconPermissionMap; + LLIconCtrl * mIconPermissionEditMine; + LLIconCtrl * mIconPermissionEditTheirs; + LLOutputMonitorCtrl * mSpeakingIndicator; + LLButton * mInfoBtn; + LLButton * mProfileBtn; +}; + + +#endif // LL_LLSOCIALLIST_H diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index 75e6e3d13a..025ef3945d 100755 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -28,6 +28,7 @@ #include "lltoastimpanel.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llfloaterreg.h" #include "llgroupactions.h" #include "llgroupiconctrl.h" @@ -61,6 +62,15 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif style_params.font.name(font_name); style_params.font.size(font_size); + LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(p.session_id); + mIsGroupMsg = (im_session->mSessionType == LLIMModel::LLIMSession::GROUP_SESSION); + if(mIsGroupMsg) + { + mAvatarName->setValue(im_session->mName); + LLAvatarName avatar_name; + LLAvatarNameCache::get(p.avatar_id, &avatar_name); + p.message = "[From " + avatar_name.getDisplayName() + "]\n" + p.message; + } //Handle IRC styled /me messages. std::string prefix = p.message.substr(0, 4); @@ -81,12 +91,17 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif mMessage->setText(p.message, style_params); } - mAvatarName->setValue(p.from); + if(!mIsGroupMsg) + { + mAvatarName->setValue(p.from); + } mTime->setValue(p.time); mSessionID = p.session_id; mAvatarID = p.avatar_id; mNotification = p.notification; + + initIcon(); S32 maxLinesCount; @@ -147,7 +162,14 @@ void LLToastIMPanel::spawnNameToolTip() LLToolTip::Params params; params.background_visible(false); - params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE)); + if(!mIsGroupMsg) + { + params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", mAvatarID), FALSE)); + } + else + { + params.click_callback(boost::bind(&LLFloaterReg::showInstance, "inspect_group", LLSD().with("group_id", mSessionID), FALSE)); + } params.delay_time(0.0f); // spawn instantly on hover params.image(LLUI::getUIImage("Info_Small")); params.message(""); diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h index 3eb11fb3bc..767617dabc 100755 --- a/indra/newview/lltoastimpanel.h +++ b/indra/newview/lltoastimpanel.h @@ -73,6 +73,8 @@ private: LLTextBox* mAvatarName; LLTextBox* mTime; LLTextBox* mMessage; + + bool mIsGroupMsg; }; #endif // LLTOASTIMPANEL_H_ diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index beca08203f..bb9ad8c606 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -52,6 +52,7 @@ #include "lldaycyclemanager.h" #include "lldebugview.h" #include "llenvmanager.h" +#include "llfacebookconnect.h" #include "llfilepicker.h" #include "llfirstuse.h" #include "llfloaterbuy.h" @@ -5970,6 +5971,21 @@ void handle_report_abuse() LLFloaterReporter::showFromMenu(COMPLAINT_REPORT); } +void handle_facebook_connect() +{ + if (LLFacebookConnect::instance().getConnected()) + { + LLFacebookConnect::instance().disconnectFromFacebook(); + } + else + { + LLFacebookConnect::instance().getConnectionToFacebook(); + } +} + +//bool is_facebook_connected(); + + void handle_buy_currency() { LLBuyCurrencyHTML::openCurrencyFloater(); @@ -8719,4 +8735,7 @@ void initialize_menus() view_listener_t::addMenu(new LLEditableSelected(), "EditableSelected"); view_listener_t::addMenu(new LLEditableSelectedMono(), "EditableSelectedMono"); view_listener_t::addMenu(new LLToggleUIHints(), "ToggleUIHints"); + + // Facebook Connect + commit.add("Facebook.Connect", boost::bind(&handle_facebook_connect)); } diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index 143420e227..b916d95b7a 100755 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -135,6 +135,10 @@ bool enable_pay_object(); bool enable_buy_object(); bool handle_go_to(); +// Facebook Connect +void handle_facebook_connect(); +//bool is_facebook_connected(); + // Export to XML or Collada void handle_export_selected( void * ); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index ace16396db..a8b5177c31 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2649,7 +2649,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) { send_do_not_disturb_message(msg, from_id); } - else + + if (!is_muted) { LL_INFOS("Messaging") << "Received IM_GROUP_INVITATION message." << LL_ENDL; // Read the binary bucket for more information. diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b8b53aa6e4..fba835f642 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1585,6 +1585,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("EnvironmentSettings"); capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); + capabilityNames.append("FacebookConnect"); + //capabilityNames.append("FacebookRedirect"); if (gSavedSettings.getBOOL("UseHTTPInventory")) { diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index fe4d5b3e4d..04e5447abf 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1811,7 +1811,6 @@ void LLViewerWindow::initBase() gFloaterView = main_view->getChild<LLFloaterView>("Floater View"); gFloaterView->setFloaterSnapView(main_view->getChild<LLView>("floater_snap_region")->getHandle()); gSnapshotFloaterView = main_view->getChild<LLSnapshotFloaterView>("Snapshot Floater View"); - // Console llassert( !gConsole ); diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 42568775d9..fc6458c67e 100755 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -875,4 +875,19 @@ <color name="blue" value="0 0 1 1"/> + + <!--Resize bar colors --> + + <color + name="ResizebarBorderLight" + value="0.231 0.231 0.231 1"/> + + <color + name="ResizebarBorderDark" + value="0.133 0.133 0.133 1"/> + + <color + name="ResizebarBody" + value="0.208 0.208 0.208 1"/> + </colors> 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/textures.xml b/indra/newview/skins/default/textures/textures.xml index fcab966dee..18146943a5 100755 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -199,6 +199,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" /> @@ -773,4 +775,7 @@ with the same filename but different name <texture name="Popup_Caution" file_name="icons/pop_up_caution.png"/> <texture name="Camera_Drag_Dot" file_name="world/CameraDragDot.png"/> <texture name="NavBar Separator" file_name="navbar/separator.png"/> + + <texture name="Horizontal Drag Handle" file_name="widgets/horizontal_drag_handle.png"/> + <texture name="Vertical Drag Handle" file_name="widgets/vertical_drag_handle.png"/> </textures> diff --git a/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png b/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png Binary files differnew file mode 100644 index 0000000000..642eac4065 --- /dev/null +++ b/indra/newview/skins/default/textures/widgets/horizontal_drag_handle.png diff --git a/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png b/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png Binary files differnew file mode 100644 index 0000000000..b06b70cf36 --- /dev/null +++ b/indra/newview/skins/default/textures/widgets/vertical_drag_handle.png diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index 65f623a47e..da016462db 100755 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -24,24 +24,28 @@ value="Conv_toolbar_expand"/> <layout_stack animate="true" - bottom="-1" + bottom="-5" + drag_handle_gap="6" + drag_handle_first_indent="27" + drag_handle_second_indent="10" follows="all" layout="topleft" left="0" name="conversations_stack" orientation="horizontal" right="-1" + show_drag_handle="true" top="0"> <layout_panel auto_resize="false" user_resize="true" name="conversations_layout_panel" min_dim="38" - expanded_min_dim="156"> + expanded_min_dim="136"> <layout_stack animate="false" follows="left|top|right" - height="35" + height="27" layout="topleft" left="0" name="conversations_pane_buttons_stack" @@ -50,7 +54,6 @@ top="0"> <layout_panel auto_resize="true" - height="35" name="conversations_pane_buttons_expanded"> <menu_button follows="top|left" @@ -64,7 +67,7 @@ left="5" name="sort_btn" tool_tip="View/sort options" - top="5" + top="1" width="31" /> <button follows="top|left" @@ -74,7 +77,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="add_btn" tool_tip="Start a new conversation" @@ -87,7 +90,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="speak_btn" tool_tip="Speak with people using your microphone" @@ -95,9 +98,8 @@ </layout_panel> <layout_panel auto_resize="false" - height="35" name="conversations_pane_buttons_collapsed" - width="41"> + width="31"> <button follows="right|top" height="25" @@ -106,8 +108,8 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" - left="1" + top="1" + left="0" name="expand_collapse_btn" tool_tip="Collapse/Expand this list" width="31" /> @@ -119,7 +121,7 @@ layout="topleft" name="conversations_list_panel" opaque="true" - top="35" + top_pad="0" left="5" right="-1"/> </layout_panel> @@ -127,7 +129,7 @@ auto_resize="true" user_resize="true" name="messages_layout_panel" - expanded_min_dim="222"> + expanded_min_dim="212"> <panel_container bottom="-1" follows="all" @@ -136,44 +138,44 @@ name="im_box_tab_container" right="-1" top="0"> - <panel - bottom="-1" - follows="all" - layout="topleft" - name="stub_panel" - opaque="true" - top_pad="0" - left="0" - right="-1"> - <button - follows="right|top" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="Conv_toolbar_collapse" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" + <panel + bottom="-1" + follows="all" layout="topleft" - top="5" - right="-10" - name="stub_collapse_btn" - tool_tip="Collapse this pane" - width="31" /> - <text - type="string" - clip_partial="false" - follows="left|top|right" - layout="topleft" - left="15" - right="-15" - name="stub_textbox" - top="25" - height="40" - valign="center" - parse_urls="true" - wrap="true"> - This conversation is in a separate window. [secondlife:/// Bring it back.] - </text> - </panel> + name="stub_panel" + opaque="true" + top_pad="0" + left="0" + right="-1"> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_collapse" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="1" + right="-10" + name="stub_collapse_btn" + tool_tip="Collapse this pane" + width="31" /> + <text + type="string" + clip_partial="false" + follows="left|top|right" + layout="topleft" + left="15" + right="-15" + name="stub_textbox" + top="25" + height="40" + valign="center" + parse_urls="true" + wrap="true"> + This conversation is in a separate window. [secondlife:/// Bring it back.] + </text> + </panel> </panel_container> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index 2152a9f6e9..8da4213c65 100755 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -70,26 +70,23 @@ top="0" left="0" right="-1" - bottom="-3"> + bottom="-1"> <layout_stack animate="false" + bottom="-1" default_tab_group="2" follows="all" - right="-5" - bottom="-1" - top="0" - left="5" - border_size="0" + left="3" layout="topleft" - orientation="vertical" name="main_stack" - tab_group="1"> + right="-3" + orientation="vertical" + tab_group="1" + top="0"> <layout_panel auto_resize="false" name="toolbar_panel" - height="35" - right="-1" - left="1"> + height="25"> <menu_button menu_filename="menu_im_session_showmodes.xml" follows="top|left" @@ -102,7 +99,7 @@ left="5" name="view_options_btn" tool_tip="View/sort options" - top="5" + top="1" width="31" /> <menu_button menu_filename="menu_im_conversation.xml" @@ -113,7 +110,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="gear_btn" visible="false" @@ -128,7 +125,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="add_btn" tool_tip="Add someone to this conversation" @@ -141,7 +138,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="voice_call_btn" tool_tip="Open voice connection" @@ -166,8 +163,8 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" - right="-67" + top="1" + right="-70" name="close_btn" tool_tip="End this conversation" width="31" /> @@ -179,7 +176,7 @@ image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - top="5" + top="1" left_pad="2" name="expand_collapse_btn" tool_tip="Collapse/Expand this pane" @@ -194,18 +191,21 @@ layout="topleft" left_pad="2" name="tear_off_btn" - top="5" + top="1" width="31" /> </layout_panel> <layout_panel name="body_panel" - top="1" - bottom="-1"> + height="235"> <layout_stack default_tab_group="2" + drag_handle_gap="6" + drag_handle_first_indent="0" + drag_handle_second_indent="1" follows="all" orientation="horizontal" name="im_panels" + show_drag_handle="true" tab_group="1" top="0" right="-1" @@ -217,14 +217,12 @@ min_dim="0" width="150" user_resize="true" - auto_resize="false" - bottom="-1" /> + auto_resize="false" /> <layout_panel default_tab_group="3" tab_group="2" name="right_part_holder" - min_width="221" - bottom="-1"> + min_width="172"> <layout_stack animate="true" default_tab_group="2" @@ -233,7 +231,7 @@ name="translate_and_chat_stack" tab_group="1" top="0" - left="0" + left="1" right="-1" bottom="-1"> <layout_panel @@ -259,7 +257,7 @@ parse_highlights="true" parse_urls="true" right="-1" - left="5" + left="0" top="0" bottom="-1" /> </layout_panel> @@ -268,10 +266,7 @@ </layout_stack> </layout_panel> <layout_panel - top_delta="0" - top="0" - height="26" - bottom="-1" + height="35" auto_resize="false" name="chat_layout_panel"> <layout_stack @@ -281,15 +276,11 @@ orientation="horizontal" name="input_panels" top="0" - bottom="-2" + bottom="-1" left="0" right="-1"> <layout_panel - name="input_editor_layout_panel" - auto_resize="true" - user_resize="false" - top="0" - bottom="-1"> + name="input_editor_layout_panel"> <chat_editor layout="topleft" expand_lines_count="5" @@ -302,32 +293,27 @@ max_length="1023" spellcheck="true" tab_group="3" - top="1" - bottom="-2" - left="4" - right="-4" + bottom="-8" + left="5" + right="-5" wrap="true" /> </layout_panel> <layout_panel auto_resize="false" - user_resize="false" name="input_button_layout_panel" - width="30" - top="0" - bottom="-1"> + width="32"> <button - layout="topleft" left="1" - right="-1" - top="1" - height="22" + top="4" + height="25" follows="left|right|top" image_hover_unselected="Toolbar_Middle_Over" image_overlay="Conv_expand_one_line" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" name="minz_btn" - tool_tip="Shows/hides message panel" /> + tool_tip="Shows/hides message panel" + width="28" /> </layout_panel> </layout_stack> </layout_panel> diff --git a/indra/newview/skins/default/xui/en/menu_gear_fbc.xml b/indra/newview/skins/default/xui/en/menu_gear_fbc.xml new file mode 100644 index 0000000000..2c341b6ecc --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_gear_fbc.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Facebook App Settings" + layout="topleft" + name="Facebook App Settings"> + <menu_item_check.on_click + function="Advanced.WebContentTest" + parameter="http://www.facebook.com/settings?tab=applications" /> + </menu_item_check> + <menu_item_check + label="Facebook App Request" + layout="topleft" + name="Facebook App Request"> + <menu_item_check.on_click + function="People.requestFBC" + parameter="http://www.facebook.com/settings?tab=applications" /> + </menu_item_check> + <menu_item_check + label="Facebook App Send" + layout="topleft" + name="Facebook App Send"> + <menu_item_check.on_click + function="People.sendFBC" + parameter="http://www.facebook.com/settings?tab=applications" /> + </menu_item_check> + <menu_item_check + label="Facebook Add 300 test users to AvatarList" + layout="topleft" + name="Facebook App Add"> + <menu_item_check.on_click + function="People.testaddFBC"/> + </menu_item_check> + <menu_item_check + label="Facebook Add 300 test users to FolderView" + layout="topleft" + name="Facebook App Add"> + <menu_item_check.on_click + function="People.testaddFBCFolderView"/> + </menu_item_check> + <menu_item_check + label="Facebook post checkin message" + layout="topleft" + name="Facebook Checkin"> + <menu_item_check.on_click + function="People.testFBCCheckin"/> + </menu_item_check> +</toggleable_menu>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index a11cd13fdb..b34816fb14 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -16,6 +16,13 @@ parameter="agent" /> </menu_item_call> <menu_item_call + label="Connect to Facebook..." + name="ConnectToFacebook"> + <menu_item_call.on_click + function="Facebook.Connect" /> + </menu_item_call> + <menu_item_separator/> + <menu_item_call label="Appearance..." name="ChangeOutfit"> <menu_item_call.on_click @@ -3018,6 +3025,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 970a11c6c4..9d1973f267 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -3709,6 +3709,17 @@ Leave Group? </notification> <notification + icon="alertmodal.tga" + name="OwnerCannotLeaveGroup" + type="alertmodal"> + Unable to leave group. You cannot leave the group because you are the last owner of the group. Please assign another member to the owner role first. + <tag>group</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification icon="alert.tga" name="ConfirmKick" type="alert"> @@ -6573,7 +6584,7 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th icon="notify.tga" name="JoinGroup" persist="true" - type="notify"> + type="offer"> <tag>group</tag> [MESSAGE] <form name="form"> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 7ce2627be9..bc6b82e328 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 + allow_select="true" + follows="all" + height="173" + layout="topleft" + left="0" + multi_select="true" + name="suggested_friends" + show_permissions_granted="true" + top="0" + width="307" /> + </accordion_tab> </accordion> <text follows="all" @@ -633,5 +650,200 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M top="0" right="-1" /> </panel> + +<!-- ================================= FBC TEST tab (Temporary) ========================== --> + + <panel + background_opaque="true" + background_visible="true" + bg_alpha_color="DkGray" + bg_opaque_color="DkGray" + follows="all" + height="383" + label="FBC TEST" + layout="topleft" + left="0" + help_topic="people_fbctest_tab" + name="fbctest_panel" + top="0" + width="313"> + <accordion + background_visible="true" + bg_alpha_color="DkGray2" + bg_opaque_color="DkGray2" + follows="all" + height="356" + layout="topleft" + left="3" + name="friends_accordion" + top="0" + width="307"> + <accordion_tab + layout="topleft" + height="172" + min_height="150" + name="tab_facebook" + title="Facebook Friends"> + <social_list + allow_select="true" + follows="all" + height="172" + layout="topleft" + left="0" + multi_select="true" + name="facebook_friends" + show_permissions_granted="true" + top="0" + width="307" /> + </accordion_tab> + </accordion> + <panel + background_visible="true" + follows="left|right|bottom" + height="27" + label="bottom_panel" + layout="topleft" + left="3" + name="bottom_panel" + top_pad="0" + width="313"> + <menu_button + follows="bottom|left" + tool_tip="Options" + height="25" + image_hover_unselected="Toolbar_Left_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Left_Selected" + image_unselected="Toolbar_Left_Off" + layout="topleft" + name="fbc_options_btn" + top="1" + width="31" /> + <button + follows="bottom|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="1" + name="fbc_login_btn" + tool_tip="Log in to FBC" + width="31"> + <commit_callback + function="People.loginFBC" /> + </button> + <icon + follows="bottom|left|right" + height="25" + image_name="Toolbar_Right_Off" + layout="topleft" + left_pad="1" + name="dummy_icon" + width="244" + /> + </panel> + </panel> + +<!-- ================================= FBC TEST TWO tab (Final, to be renamed) ========================== --> + + <panel + background_opaque="true" + background_visible="true" + bg_alpha_color="DkGray" + bg_opaque_color="DkGray" + follows="all" + height="383" + label="FBC TEST TWO" + layout="topleft" + left="0" + help_topic="people_fbctest_tab" + name="fbctesttwo_panel" + top="0"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="fbc_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="fbc_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + name="gear_btn" + tool_tip="Actions on selected person" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_friends_view.xml" + menu_position="bottomleft" + name="fbc_view_btn" + tool_tip="View/sort options" + top_delta="0" + width="31" /> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="fbc_add_btn" + tool_tip="Offer friendship to a resident" + top_delta="0" + width="31"> + <commit_callback + function="People.AddFriendWizard" /> + </button> + <dnd_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="fbc_del_btn" + tool_tip="Remove selected person as a friend" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> + </panel> + </panel> </tab_container> </panel> 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) {} |