diff options
Diffstat (limited to 'indra')
77 files changed, 1379 insertions, 250 deletions
diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h index 46491d8a29..2c339f4a3f 100644 --- a/indra/llui/lldockablefloater.h +++ b/indra/llui/lldockablefloater.h @@ -83,6 +83,8 @@ public: virtual void onDockHidden(); virtual void onDockShown(); + LLDockControl* getDockControl(); + private: /** * Provides unique of dockable floater. @@ -92,7 +94,6 @@ private: protected: void setDockControl(LLDockControl* dockControl); - LLDockControl* getDockControl(); const LLUIImagePtr& getDockTongue(); private: diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h index 30a45bedc7..550955c4c5 100644 --- a/indra/llui/lldockcontrol.h +++ b/indra/llui/lldockcontrol.h @@ -76,6 +76,9 @@ public: // gets a rect that bounds possible positions for a dockable control (EXT-1111) void getAllowedRect(LLRect& rect); + S32 getTongueWidth() { return mDockTongue->getWidth(); } + S32 getTongueHeight() { return mDockTongue->getHeight(); } + private: virtual void moveDockable(); private: diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 831ac66d06..64a4824a17 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -891,7 +891,13 @@ void LLFlatListView::setNoItemsCommentVisible(bool visible) const // We have to update child rect here because of issues with rect after reshaping while creating LLTextbox // It is possible to have invalid LLRect if Flat List is in LLAccordionTab LLRect comment_rect = getLocalRect(); - comment_rect.stretch(-getBorderWidth()); + + // To see comment correctly (EXT - 3244) in mNoItemsCommentTextbox we must get border width + // of LLFlatListView (@see getBorderWidth()) and stretch mNoItemsCommentTextbox to this width + // But getBorderWidth() returns 0 if LLFlatListView not visible. So we have to get border width + // from 'scroll_border' + LLViewBorder* scroll_border = getChild<LLViewBorder>("scroll border"); + comment_rect.stretch(-scroll_border->getBorderWidth()); mNoItemsCommentTextbox->setRect(comment_rect); } mNoItemsCommentTextbox->setVisible(visible); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index e68affc36c..faf9ccbeb8 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1887,9 +1887,10 @@ void LLTextEditor::doDelete() removeChar(); } - onKeyStroke(); } + onKeyStroke(); + needsReflow(); } diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 7694d02837..7350457274 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -197,6 +197,31 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) } // +// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com +// +LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() +{ + mPattern = boost::regex("(\\bwww\\.\\S+\\.\\S+|\\S+.com\\S*|\\S+.net\\S*|\\S+.edu\\S*|\\S+.org\\S*)", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) +{ + if (string.find("://") == std::string::npos) + { + return "http://" + escapeUrl(string); + } + return escapeUrl(string); +} + +// // LLUrlEntrySLURL Describes generic http: and https: Urls // LLUrlEntrySLURL::LLUrlEntrySLURL() diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index b3fb333fdd..4adffde99c 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -135,6 +135,17 @@ public: }; /// +/// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com +/// +class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTPNoProtocol(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string); +}; + +/// /// LLUrlEntrySLURL Describes http://slurl.com/... Urls /// class LLUrlEntrySLURL : public LLUrlEntryBase diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index f47db2db1a..afcff0d409 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -58,6 +58,9 @@ LLUrlRegistry::LLUrlRegistry() //so it should be registered in the end of list registerUrl(new LLUrlEntrySL()); registerUrl(new LLUrlEntrySLLabel()); + // most common pattern is a URL without any protocol, + // e.g., "secondlife.com" + registerUrl(new LLUrlEntryHTTPNoProtocol()); } LLUrlRegistry::~LLUrlRegistry() @@ -118,10 +121,23 @@ static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &en return true; } +static bool stringHasUrl(const std::string &text) +{ + // fast heuristic test for a URL in a string. This is used + // to avoid lots of costly regex calls, BUT it needs to be + // kept in sync with the LLUrlEntry regexes we support. + return (text.find("://") != std::string::npos || + text.find("www.") != std::string::npos || + text.find(".com") != std::string::npos || + text.find(".net") != std::string::npos || + text.find(".edu") != std::string::npos || + text.find(".org") != std::string::npos); +} + bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb) { // avoid costly regexes if there is clearly no URL in the text - if (text.find("://") == std::string::npos) + if (! stringHasUrl(text)) { return false; } diff --git a/indra/lscript/lscript_export.h b/indra/lscript/lscript_export.h index d4626a8cd2..4c883582e2 100644 --- a/indra/lscript/lscript_export.h +++ b/indra/lscript/lscript_export.h @@ -35,6 +35,6 @@ #include "lscript_library.h" -extern LLScriptLibrary gScriptLibrary; + #endif diff --git a/indra/lscript/lscript_library.h b/indra/lscript/lscript_library.h index 6728d70d0a..363d11f3aa 100644 --- a/indra/lscript/lscript_library.h +++ b/indra/lscript/lscript_library.h @@ -70,7 +70,7 @@ public: std::vector<LLScriptLibraryFunction> mFunctions; }; -extern LLScriptLibrary gScriptLibrary; + class LLScriptLibData { @@ -428,4 +428,6 @@ public: }; +extern LLScriptLibrary gScriptLibrary; + #endif diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 80a3977d02..289e900eaf 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5038,7 +5038,18 @@ <key>Type</key> <string>S32</string> <key>Value</key> - <integer>35</integer> + <integer>5</integer> + </map> + <key>NotificationChannelHeightRatio</key> + <map> + <key>Comment</key> + <string>Notification channel and World View ratio(0.0 - always show 1 notification, 1.0 - max ratio).</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.5</real> </map> <key>OverflowToastHeight</key> <map> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 508badcc6f..9a1b749ba7 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -78,6 +78,8 @@ #include "lllocationhistory.h" #include "llfasttimerview.h" #include "llvoicechannel.h" +#include "llsidetray.h" + #include "llweb.h" #include "llsecondlifeurls.h" @@ -2854,6 +2856,8 @@ void LLAppViewer::requestQuit() gFloaterView->closeAllChildren(true); } + LLSideTray::getInstance()->notifyChildren(LLSD().with("request","quit")); + send_stats(); gLogoutTimer.reset(); @@ -3758,6 +3762,13 @@ void LLAppViewer::idleShutdown() { return; } + + if (LLSideTray::getInstance()->notifyChildren(LLSD().with("request","wait_quit"))) + { + return; + } + + // ProductEngine: Try moving this code to where we shut down sTextureCache in cleanup() // *TODO: ugly diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 5af023f565..5f90a7627f 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -62,9 +62,6 @@ #include "llimfloater.h" #include "lltrans.h" -// callback connection to auto-call when the IM floater initializes -boost::signals2::connection gAdhocAutoCall; - // static void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::string& name) { @@ -250,8 +247,8 @@ void LLAvatarActions::startAdhocCall(const std::vector<LLUUID>& ids) // always open IM window when connecting to voice LLIMFloater::show(session_id); - // start the call once the floater has fully initialized - gAdhocAutoCall = LLIMModel::getInstance()->addSessionInitializedCallback(callbackAutoStartCall); + // start the call once the session has fully initialized + gIMMgr->autoStartCallOnStartup(session_id); make_ui_sound("UISndStartIM"); } @@ -467,17 +464,6 @@ bool LLAvatarActions::callbackAddFriend(const LLSD& notification, const LLSD& re } // static -void LLAvatarActions::callbackAutoStartCall(const LLSD& data) -{ - // start the adhoc voice call now the IM panel has initialized - LLUUID session_id = data["session_id"].asUUID(); - gIMMgr->startCall(session_id); - - // and deschedule this callback as its work is done now - gAdhocAutoCall.disconnect(); -} - -// static void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message) { const LLUUID calling_card_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 4c9851a48d..2dd2a4c4b1 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -139,7 +139,6 @@ private: static bool handleRemove(const LLSD& notification, const LLSD& response); static bool handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id); static void callback_invite_to_group(LLUUID group_id, LLUUID id); - static void callbackAutoStartCall(const LLSD& data); // Just request friendship, no dialog. static void requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message); diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 4b3b7a99d8..30967677e8 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -61,6 +61,7 @@ static LLDefaultChildRegistry::Register<LLIMP2PChiclet> t3("chiclet_im_p2p"); static LLDefaultChildRegistry::Register<LLIMGroupChiclet> t4("chiclet_im_group"); static LLDefaultChildRegistry::Register<LLAdHocChiclet> t5("chiclet_im_adhoc"); static LLDefaultChildRegistry::Register<LLScriptChiclet> t6("chiclet_script"); +static LLDefaultChildRegistry::Register<LLInvOfferChiclet> t7("chiclet_offer"); static const LLRect CHICLET_RECT(0, 25, 25, 0); static const LLRect CHICLET_ICON_RECT(0, 22, 22, 0); @@ -78,29 +79,73 @@ boost::signals2::signal<LLChiclet* (const LLUUID&), ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// +/** + * Updates the Well's 'Lit' state to flash it when "new messages" are come. + * + * It gets callback which will be called 2*N times with passed period. See EXT-3147 + */ +class LLSysWellChiclet::FlashToLitTimer : public LLEventTimer +{ +public: + typedef boost::function<void()> callback_t; + FlashToLitTimer(S32 count, F32 period, callback_t cb) + : LLEventTimer(period) + , mCallback(cb) + , mFlashCount(2 * count) + , mCurrentFlashCount(0) + { + mEventTimer.stop(); + } + + BOOL tick() + { + mCallback(); + + if (++mCurrentFlashCount == mFlashCount) mEventTimer.stop(); + return FALSE; + } + + void flash() + { + mCurrentFlashCount = 0; + mEventTimer.start(); + } + +private: + callback_t mCallback; + S32 mFlashCount; + S32 mCurrentFlashCount; +}; + LLSysWellChiclet::Params::Params() : button("button") , unread_notifications("unread_notifications") +, max_displayed_count("max_displayed_count", 9) +, flash_to_lit_count("flash_to_lit_count", 3) +, flash_period("flash_period", 0.5F) { button.name("button"); button.tab_stop(FALSE); button.label(LLStringUtil::null); - } LLSysWellChiclet::LLSysWellChiclet(const Params& p) : LLChiclet(p) , mButton(NULL) , mCounter(0) +, mMaxDisplayedCount(p.max_displayed_count) +, mFlashToLitTimer(NULL) { LLButton::Params button_params = p.button; mButton = LLUICtrlFactory::create<LLButton>(button_params); addChild(mButton); + + mFlashToLitTimer = new FlashToLitTimer(p.flash_to_lit_count, p.flash_period, boost::bind(&LLSysWellChiclet::changeLitState, this)); } LLSysWellChiclet::~LLSysWellChiclet() { - + delete mFlashToLitTimer; } void LLSysWellChiclet::setCounter(S32 counter) @@ -108,11 +153,30 @@ void LLSysWellChiclet::setCounter(S32 counter) std::string s_count; if(counter != 0) { - s_count = llformat("%d", counter); + static std::string more_messages_exist("+"); + std::string more_messages(counter > mMaxDisplayedCount ? more_messages_exist : ""); + s_count = llformat("%d%s" + , llmin(counter, mMaxDisplayedCount) + , more_messages.c_str() + ); } mButton->setLabel(s_count); + /* + Emulate 4 states of button by background images, see detains in EXT-3147 + xml attribute Description + image_unselected "Unlit" - there are no new messages + image_selected "Unlit" + "Selected" - there are no new messages and the Well is open + image_pressed "Lit" - there are new messages + image_pressed_selected "Lit" + "Selected" - there are new messages and the Well is open + */ + mButton->setForcePressedState(counter > 0); + + if (mCounter == 0 && counter > 0) + { + mFlashToLitTimer->flash(); + } mCounter = counter; } @@ -126,6 +190,14 @@ void LLSysWellChiclet::setToggleState(BOOL toggled) { mButton->setToggleState(toggled); } +void LLSysWellChiclet::changeLitState() +{ + static bool set_lit = false; + + mButton->setForcePressedState(set_lit); + + set_lit ^= true; +} /************************************************************************/ /* LLIMWellChiclet implementation */ @@ -939,12 +1011,34 @@ void im_chiclet_callback(LLChicletPanel* panel, const LLSD& data){ } } +void object_chiclet_callback(const LLSD& data) +{ + LLUUID object_id = data["object_id"]; + bool new_message = data["new_message"]; + + std::list<LLChiclet*> chiclets = LLIMChiclet::sFindChicletsSignal(object_id); + std::list<LLChiclet *>::iterator iter; + for (iter = chiclets.begin(); iter != chiclets.end(); iter++) + { + LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*iter); + if (chiclet != NULL) + { + if(data.has("unread")) + { + chiclet->setCounter(data["unread"]); + } + chiclet->setShowNewMessagesIcon(new_message); + } + } +} BOOL LLChicletPanel::postBuild() { LLPanel::postBuild(); LLIMModel::instance().addNewMsgCallback(boost::bind(im_chiclet_callback, this, _1)); LLIMModel::instance().addNoUnreadMsgsCallback(boost::bind(im_chiclet_callback, this, _1)); + LLScriptFloaterManager::getInstance()->addNewObjectCallback(boost::bind(object_chiclet_callback, _1)); + LLScriptFloaterManager::getInstance()->addToggleObjectFloaterCallback(boost::bind(object_chiclet_callback, _1)); LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLChicletPanel::findChiclet<LLChiclet>, this, _1)); LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLChicletPanel::onCurrentVoiceChannelChanged, this, _1)); @@ -1545,6 +1639,11 @@ void LLScriptChiclet::setSessionId(const LLUUID& session_id) } } +void LLScriptChiclet::setCounter(S32 counter) +{ + setShowNewMessagesIcon( counter > 0 ); +} + void LLScriptChiclet::onMouseDown() { LLScriptFloaterManager::getInstance()->toggleScriptFloater(getSessionId()); @@ -1601,6 +1700,11 @@ void LLInvOfferChiclet::setSessionId(const LLUUID& session_id) } } +void LLInvOfferChiclet::setCounter(S32 counter) +{ + setShowNewMessagesIcon( counter > 0 ); +} + void LLInvOfferChiclet::onMouseDown() { LLScriptFloaterManager::instance().toggleScriptFloater(getSessionId()); diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 609ce16713..65abcd1f5f 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -315,7 +315,7 @@ public: { Optional<std::string> new_messages_icon_name; - Params() : new_messages_icon_name("new_messages_icon_name", "icn_voice-localchat.tga") + Params() : new_messages_icon_name("new_messages_icon_name", "Unread_IM") {} }; @@ -389,7 +389,7 @@ public: * Made public so that it can be triggered from outside * (more specifically, from the Active IM window). */ - void onMouseDown(); + virtual void onMouseDown(); protected: @@ -594,7 +594,7 @@ public: /*virtual*/ void setSessionId(const LLUUID& session_id); - /*virtual*/ void setCounter(S32 counter){} + /*virtual*/ void setCounter(S32 counter); /*virtual*/ S32 getCounter() { return 0; } @@ -634,7 +634,7 @@ public: /*virtual*/ void setSessionId(const LLUUID& session_id); - /*virtual*/ void setCounter(S32 counter){} + /*virtual*/ void setCounter(S32 counter); /*virtual*/ S32 getCounter() { return 0; } @@ -745,7 +745,7 @@ private: /** * Implements notification chiclet. Used to display total amount of unread messages - * across all IM sessions, total amount of system notifications. + * across all IM sessions, total amount of system notifications. See EXT-3147 for details */ class LLSysWellChiclet : public LLChiclet { @@ -757,6 +757,24 @@ public: Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications; + /** + * Contains maximum displayed count of unread messages. Default value is 9. + * + * If count is less than "max_unread_count" will be displayed as is. + * Otherwise 9+ will be shown (for default value). + */ + Optional<S32> max_displayed_count; + + /** + * How many time chiclet should flash before set "Lit" state. Default value is 3. + */ + Optional<S32> flash_to_lit_count; + + /** + * Period of flashing while setting "Lit" state, in seconds. Default value is 0.5. + */ + Optional<F32> flash_period; + Params(); }; @@ -778,9 +796,26 @@ protected: LLSysWellChiclet(const Params& p); friend class LLUICtrlFactory; + /** + * Change Well 'Lit' state from 'Lit' to 'Unlit' and vice-versa. + * + * There is an assumption that it will be called 2*N times to do not change its start state. + * @see FlashToLitTimer + */ + void changeLitState(); + protected: + class FlashToLitTimer; LLButton* mButton; S32 mCounter; + S32 mMaxDisplayedCount; + + /** + * How many times Well will blink. + */ + S32 mFlashToLitCount; + FlashToLitTimer* mFlashToLitTimer; + }; /** diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index b2399d238b..dc6847f236 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -306,13 +306,16 @@ void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op ) } } -void LLColorSwatchCtrl::onFloaterClose() +// This is called when the main floatercustomize panel is closed. +// Since this class has pointers up to its parents, we need to cleanup +// this class first in order to avoid a crash. +void LLColorSwatchCtrl::onParentFloaterClosed() { LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)mPickerHandle.get(); - if (pickerp) { pickerp->setSwatch(NULL); + pickerp->closeFloater(); } mPickerHandle.markDead(); diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h index 2f6aec85e8..4bb7d837cb 100644 --- a/indra/newview/llcolorswatch.h +++ b/indra/newview/llcolorswatch.h @@ -105,7 +105,7 @@ public: /*virtual*/ void setEnabled( BOOL enabled ); static void onColorChanged ( void* data, EColorPickOp pick_op = COLOR_CHANGE ); - void onFloaterClose(); + void onParentFloaterClosed(); protected: BOOL mValid; diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index af6488388a..1d92661ea2 100644 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp @@ -34,6 +34,7 @@ #include "llviewerprecompiledheaders.h" #include "llcommandhandler.h" +#include "llnotificationsutil.h" // system includes #include <boost/tokenizer.hpp> @@ -93,6 +94,8 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, LLMediaCtrl* web, bool trusted_browser) { + static bool slurl_blocked = false; + static bool slurl_throttled = false; static F64 last_throttle_time = 0.0; F64 cur_time = 0.0; std::map<std::string, LLCommandHandlerInfo>::iterator it = mMap.find(cmd); @@ -110,6 +113,11 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, // block request from external browser, but report as // "handled" because it was well formatted. LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL; + if (! slurl_blocked) + { + LLNotificationsUtil::add("BlockedSLURL"); + slurl_blocked = true; + } return true; case LLCommandHandler::UNTRUSTED_THROTTLE: @@ -119,6 +127,11 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, // block request from external browser if it happened // within THROTTLE_PERIOD secs of the last command LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL; + if (! slurl_throttled) + { + LLNotificationsUtil::add("ThrottledSLURL"); + slurl_throttled = true; + } return true; } last_throttle_time = cur_time; diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index 57bb93d81a..b9e0f928f1 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -41,6 +41,7 @@ // project include #include "llagent.h" +#include "llappviewer.h" #include "llbutton.h" #include "llcheckboxctrl.h" #include "llcombobox.h" @@ -142,6 +143,10 @@ BOOL LLFloaterChat::postBuild() void LLFloaterChat::updateConsoleVisibility() { + if(gDisconnected) + { + return; + } // determine whether we should show console due to not being visible gConsole->setVisible( !isInVisibleChain() // are we not in part of UI being drawn? || isMinimized() // are we minimized? diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp index 56b56dc3d2..73b79d8e13 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -241,16 +241,6 @@ BOOL LLFloaterColorPicker::postBuild() return TRUE; } -/*virtual*/ -void LLFloaterColorPicker::onClose(bool app_settings) -{ - if (mSwatch) - { - mSwatch->onFloaterClose(); - } - stopUsingPipette(); -} - ////////////////////////////////////////////////////////////////////////////// // void LLFloaterColorPicker::initUI ( F32 rValIn, F32 gValIn, F32 bValIn ) diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h index b381740acd..0bbbe2051c 100644 --- a/indra/newview/llfloatercolorpicker.h +++ b/indra/newview/llfloatercolorpicker.h @@ -56,7 +56,6 @@ class LLFloaterColorPicker // overrides virtual BOOL postBuild (); - virtual void onClose(bool app_settings); virtual void draw (); virtual BOOL handleMouseDown ( S32 x, S32 y, MASK mask ); virtual BOOL handleMouseUp ( S32 x, S32 y, MASK mask ); diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index 2c618263ec..c658963708 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -42,7 +42,8 @@ LLFloaterSearch::LLFloaterSearch(const LLSD& key) : LLFloater(key), LLViewerMediaObserver(), - mBrowser(NULL) + mBrowser(NULL), + mSearchGodLevel(0) { // declare a map that transforms a category name into // the URL suffix that is used to search that category @@ -86,12 +87,21 @@ void LLFloaterSearch::handleMediaEvent(LLPluginClassMedia *self, EMediaEvent eve case MEDIA_EVENT_NAVIGATE_COMPLETE: childSetText("status_text", getString("done_text")); break; - + default: break; } } +void LLFloaterSearch::godLevelChanged(U8 godlevel) +{ + // search results can change based upon god level - if the user + // changes god level, then give them a warning (we don't refresh + // the search as this might undo any page navigation or + // AJAX-driven changes since the last search). + childSetVisible("refresh_search", (godlevel != mSearchGodLevel)); +} + void LLFloaterSearch::search(const LLSD &key) { if (! mBrowser) @@ -99,6 +109,10 @@ void LLFloaterSearch::search(const LLSD &key) return; } + // reset the god level warning as we're sending the latest state + childHide("refresh_search"); + mSearchGodLevel = gAgent.getGodLevel(); + // get the URL for the search page std::string url = getString("search_url"); if (! LLStringUtil::endsWith(url, "/")) diff --git a/indra/newview/llfloatersearch.h b/indra/newview/llfloatersearch.h index 743107484f..ba817adf7f 100644 --- a/indra/newview/llfloatersearch.h +++ b/indra/newview/llfloatersearch.h @@ -66,14 +66,20 @@ public: /// "events", "groups", "wiki", "destinations", "classifieds" void search(const LLSD &key); + /// changing godmode can affect the search results that are + /// returned by the search website - use this method to tell the + /// search floater that the user has changed god level. + void godLevelChanged(U8 godlevel); + private: /*virtual*/ BOOL postBuild(); // inherited from LLViewerMediaObserver /*virtual*/ void handleMediaEvent(LLPluginClassMedia *self, EMediaEvent event); - + LLMediaCtrl *mBrowser; LLSD mCategoryPaths; + U8 mSearchGodLevel; }; #endif // LL_LLFLOATERSEARCH_H diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 2bc07d0c27..47a168e354 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -90,8 +90,20 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) case IM_SESSION_CONFERENCE_START: mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); break; - default: + case IM_SESSION_GROUP_START: mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); + break; + case IM_SESSION_INVITE: + if (gAgent.isInGroup(mSessionID)) + { + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); + } + else + { + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); + } + break; + default: break; } } } @@ -415,6 +427,7 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock) if(channel) { channel->updateShowToastsState(); + channel->redrawToasts(); } } @@ -439,6 +452,7 @@ void LLIMFloater::setVisible(BOOL visible) if(channel) { channel->updateShowToastsState(); + channel->redrawToasts(); } if (visible && mChatHistory && mInputEditor) diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 6c4af0522f..4d2ba16a4c 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -88,6 +88,9 @@ const static std::string IM_TEXT("message"); const static std::string IM_FROM("from"); const static std::string IM_FROM_ID("from_id"); +const static std::string NO_SESSION("(IM Session Doesn't Exist)"); +const static std::string ADHOC_NAME_SUFFIX(" Conference"); + std::string LLCallDialogManager::sPreviousSessionlName = ""; std::string LLCallDialogManager::sCurrentSessionlName = ""; LLIMModel::LLIMSession* LLCallDialogManager::sSession = NULL; @@ -116,6 +119,13 @@ void toast_callback(const LLSD& msg){ return; } + // Skip toasting if we have open window of IM with this session id + LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]); + if (open_im_floater && open_im_floater->getVisible()) + { + return; + } + LLSD args; args["MESSAGE"] = msg["message"]; args["TIME"] = msg["time"]; @@ -154,11 +164,11 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& mInitialTargetIDs(ids), mVoiceChannel(NULL), mSpeakers(NULL), - mCallDialogManager(NULL), mSessionInitialized(false), mCallBackEnabled(true), mTextIMPossible(true), - mOtherParticipantIsAvatar(true) + mOtherParticipantIsAvatar(true), + mStartCallOnInitialize(false) { if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type) { @@ -287,9 +297,6 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES LLIMModel::LLIMSession::~LLIMSession() { - delete mCallDialogManager; - mCallDialogManager = NULL; - delete mSpeakers; mSpeakers = NULL; @@ -413,6 +420,12 @@ void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, con { im_floater->sessionInitReplyReceived(new_session_id); } + + // auto-start the call on session initialization? + if (session->mStartCallOnInitialize) + { + gIMMgr->startCall(new_session_id); + } } //*TODO remove this "floater" stuff when Communicate Floater is gone @@ -448,10 +461,16 @@ void LLIMModel::testMessages() addMessage(bot2_session_id, from, bot2_id, "Test Message: OMGWTFBBQ."); } - +//session name should not be empty bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids) { + if (name.empty()) + { + llwarns << "Attempt to create a new session with empty name; id = " << session_id << llendl; + return false; + } + if (findIMSession(session_id)) { llwarns << "IM Session " << session_id << " already exists" << llendl; @@ -608,7 +627,7 @@ const std::string& LLIMModel::getName(const LLUUID& session_id) const if (!session) { llwarns << "session " << session_id << "does not exist " << llendl; - return LLStringUtil::null; + return NO_SESSION; } return session->mName; @@ -995,18 +1014,6 @@ bool LLIMModel::sendStartSession( return false; } -// static -void LLIMModel::sendSessionInitialized(const LLUUID &session_id) -{ - LLIMSession* session = getInstance()->findIMSession(session_id); - if (session) - { - LLSD arg; - arg["session_id"] = session_id; - getInstance()->mSessionInitializedSignal(arg); - } -} - // // Helper Functions // @@ -1079,7 +1086,7 @@ public: if ( 404 == statusNum ) { std::string error_string; - error_string = "does not exist"; + error_string = "session_does_not_exist_error"; gIMMgr->showSessionStartError(error_string, mSessionID); } } @@ -1268,6 +1275,7 @@ void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EStat { LLSD mCallDialogPayload; LLOutgoingCallDialog* ocd; + bool is_incoming; mCallDialogPayload["session_id"] = sSession->mSessionID; mCallDialogPayload["session_name"] = sSession->mName; @@ -1277,8 +1285,10 @@ void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EStat switch(new_state) { case LLVoiceChannel::STATE_CALL_STARTED : - // do not show "Calling to..." if it is incoming P2P call - if(sSession->mSessionType == LLIMModel::LLIMSession::P2P_SESSION && static_cast<LLVoiceChannelP2P*>(sSession->mVoiceChannel)->isIncomingCall()) + // do not show "Calling to..." if it is incoming call + is_incoming = LLVoiceClient::getInstance()->isSessionIncoming(sSession->mSessionID); + // *TODO: implement for AdHoc and Group voice chats + if(is_incoming) { return; } @@ -1290,6 +1300,7 @@ void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EStat ocd->getChild<LLTextBox>("leaving")->setVisible(true); ocd->getChild<LLTextBox>("connecting")->setVisible(false); ocd->getChild<LLTextBox>("noanswer")->setVisible(false); + ocd->getChild<LLButton>("Cancel")->setVisible(true); } return; @@ -1301,10 +1312,12 @@ void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EStat ocd->getChild<LLTextBox>("leaving")->setVisible(true); ocd->getChild<LLTextBox>("connecting")->setVisible(true); ocd->getChild<LLTextBox>("noanswer")->setVisible(false); + ocd->getChild<LLButton>("Cancel")->setVisible(true); } return; case LLVoiceChannel::STATE_ERROR : + mCallDialogPayload["start_timer"] = true; ocd = dynamic_cast<LLOutgoingCallDialog*>(LLFloaterReg::showInstance("outgoing_call", mCallDialogPayload, TRUE)); if (ocd) { @@ -1312,6 +1325,7 @@ void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EStat ocd->getChild<LLTextBox>("leaving")->setVisible(false); ocd->getChild<LLTextBox>("connecting")->setVisible(false); ocd->getChild<LLTextBox>("noanswer")->setVisible(true); + ocd->getChild<LLButton>("Cancel")->setVisible(false); } return; @@ -1363,6 +1377,33 @@ LLCallDialog(payload) instance->onCancel(instance); } } +void LLOutgoingCallDialog::draw() +{ + if (lifetimeHasExpired()) + { + onLifetimeExpired(); + } + LLDockableFloater::draw(); +} + +bool LLOutgoingCallDialog::lifetimeHasExpired() +{ + if (mLifetimeTimer.getStarted()) + { + F32 elapsed_time = mLifetimeTimer.getElapsedTimeF32(); + if (elapsed_time > LIFETIME) + { + return true; + } + } + return false; +} + +void LLOutgoingCallDialog::onLifetimeExpired() +{ + mLifetimeTimer.stop(); + closeFloater(); +} void LLOutgoingCallDialog::onOpen(const LLSD& key) { @@ -1391,6 +1432,13 @@ void LLOutgoingCallDialog::onOpen(const LLSD& key) childSetTextArg("connecting", "[CALLEE_NAME]", callee_name); LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); icon->setValue(callee_id); + + // stop timer by default + mLifetimeTimer.stop(); + if(mPayload.has("start_timer")) + { + mLifetimeTimer.reset(); + } } @@ -1514,6 +1562,8 @@ void LLIncomingCallDialog::processCallResponse(S32 response) return; LLUUID session_id = mPayload["session_id"].asUUID(); + LLUUID caller_id = mPayload["caller_id"].asUUID(); + std::string session_name = mPayload["session_name"].asString(); EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger(); LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)mPayload["inv_type"].asInteger(); bool voice = true; @@ -1530,8 +1580,8 @@ void LLIncomingCallDialog::processCallResponse(S32 response) { // create a normal IM session session_id = gIMMgr->addP2PSession( - mPayload["session_name"].asString(), - mPayload["caller_id"].asUUID(), + session_name, + caller_id, mPayload["session_handle"].asString(), mPayload["session_uri"].asString()); @@ -1549,10 +1599,38 @@ void LLIncomingCallDialog::processCallResponse(S32 response) } else { - LLUUID new_session_id = gIMMgr->addSession( - mPayload["session_name"].asString(), - type, - session_id); + //session name should not be empty, but it can contain spaces so we don't trim + std::string correct_session_name = session_name; + if (session_name.empty()) + { + llwarns << "Received an empty session name from a server" << llendl; + + switch(type){ + case IM_SESSION_CONFERENCE_START: + case IM_SESSION_GROUP_START: + case IM_SESSION_INVITE: + if (gAgent.isInGroup(session_id)) + { + LLGroupData data; + if (!gAgent.getGroupData(session_id, data)) break; + correct_session_name = data.mName; + } + else + { + if (gCacheName->getFullName(caller_id, correct_session_name)) + { + correct_session_name.append(ADHOC_NAME_SUFFIX); + } + } + llinfos << "Corrected session name is " << correct_session_name << llendl; + break; + default: + llwarning("Received an empty session name from a server and failed to generate a new proper session name", 0); + break; + } + } + + LLUUID new_session_id = gIMMgr->addSession(correct_session_name, type, session_id); if (new_session_id != LLUUID::null) { LLIMFloater::show(new_session_id); @@ -1929,6 +2007,15 @@ BOOL LLIMMgr::getIMReceived() const return mIMReceived; } +void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id) +{ + LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(session_id); + if (session) + { + session->mStartCallOnInitialize = true; + } +} + LLUUID LLIMMgr::addP2PSession(const std::string& name, const LLUUID& other_participant_id, const std::string& voice_session_handle, @@ -1979,6 +2066,12 @@ LLUUID LLIMMgr::addSession( return LLUUID::null; } + if (name.empty()) + { + llwarning("Session name cannot be null!", 0); + return LLUUID::null; + } + LLUUID session_id = computeSessionID(dialog,other_participant_id); bool new_session = !LLIMModel::getInstance()->findIMSession(session_id); diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index c002434a18..f26889ac91 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -81,7 +81,6 @@ public: SType mSessionType; LLUUID mOtherParticipantID; std::vector<LLUUID> mInitialTargetIDs; - LLCallDialogManager* mCallDialogManager; // connection to voice channel state change signal boost::signals2::connection mVoiceChannelStateChangeConnection; @@ -105,6 +104,7 @@ public: bool mTextIMPossible; bool mOtherParticipantIsAvatar; + bool mStartCallOnInitialize; }; @@ -124,7 +124,6 @@ public: typedef boost::function<void(const LLSD&)> session_callback_t; session_signal_t mNewMsgSignal; session_signal_t mNoUnreadMsgsSignal; - session_signal_t mSessionInitializedSignal; /** * Find an IM Session corresponding to session_id @@ -139,10 +138,10 @@ public: boost::signals2::connection addNewMsgCallback( session_callback_t cb ) { return mNewMsgSignal.connect(cb); } boost::signals2::connection addNoUnreadMsgsCallback( session_callback_t cb ) { return mNoUnreadMsgsSignal.connect(cb); } - boost::signals2::connection addSessionInitializedCallback(session_callback_t cb ) { return mSessionInitializedSignal.connect(cb); } /** * Create new session object in a model + * @param name session name should not be empty, will return false if empty */ bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids = std::vector<LLUUID>()); @@ -213,7 +212,6 @@ public: static bool sendStartSession(const LLUUID& temp_session_id, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids, EInstantMessage dialog); static void sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing); - static void sendSessionInitialized(const LLUUID &session_id); static void sendMessage(const std::string& utf8_text, const LLUUID& im_session_id, const LLUUID& other_participant_id, EInstantMessage dialog); @@ -299,6 +297,7 @@ public: /** * Creates a P2P session with the requisite handle for responding to voice calls. * + * @param name session name, cannot be null * @param caller_uri - sip URI of caller. It should be always be passed into the method to avoid * incorrect working of LLVoiceChannel instances. See EXT-2985. */ @@ -330,6 +329,9 @@ public: void notifyNewIM(); void clearNewIMNotification(); + // automatically start a call once the session has initialized + void autoStartCallOnStartup(const LLUUID& session_id); + // IM received that you haven't seen yet BOOL getIMReceived() const; @@ -493,7 +495,16 @@ public: static void onCancel(void* user_data); + // check timer state + /*virtual*/ void draw(); + private: + // lifetime timer for NO_ANSWER notification + LLTimer mLifetimeTimer; + // lifetime duration for NO_ANSWER notification + static const S32 LIFETIME = 5; + bool lifetimeHasExpired(); + void onLifetimeExpired(); }; // Globals diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 4cbf27b725..92b9dc427f 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -903,13 +903,10 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open) if (!auto_open) return NULL; // D. Open the inventory side panel and use that. - LLSideTray *side_tray = LLSideTray::getInstance(); + LLSD key; LLSidepanelInventory *sidepanel_inventory = - dynamic_cast<LLSidepanelInventory *>(side_tray->getPanel("sidepanel_inventory")); - - // Use the inventory side panel only if it is already active. - // Activating it may unexpectedly switch off the currently active tab in some cases. - if (sidepanel_inventory && (LLPanel*)side_tray->getActiveTab() == (LLPanel*)sidepanel_inventory) + dynamic_cast<LLSidepanelInventory *>(LLSideTray::getInstance()->showPanel("sidepanel_inventory", key)); + if (sidepanel_inventory) { return sidepanel_inventory->getActivePanel(); } diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 5c240aa54a..83a8dcd9f0 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -266,6 +266,11 @@ public: static bool canLogToIM(const LLNotificationPtr& notification); /** + * Checks sufficient conditions to log notification message to nearby chat session. + */ + static bool canLogToNearbyChat(const LLNotificationPtr& notification); + + /** * Checks sufficient conditions to spawn IM session. */ static bool canSpawnIMSession(const LLNotificationPtr& notification); @@ -287,6 +292,11 @@ public: * Writes group notice notification message to IM group session. */ static void logGroupNoticeToIMGroup(const LLNotificationPtr& notification); + + /** + * Writes notification message to nearby chat. + */ + static void logToNearbyChat(const LLNotificationPtr& notification, EChatSourceType type); }; } diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 6748bd7982..5b54092c5c 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -37,6 +37,8 @@ #include "llnotifications.h" #include "llimview.h" #include "llagent.h" +#include "llfloaterreg.h" +#include "llnearbychat.h" using namespace LLNotificationsUI; @@ -47,7 +49,9 @@ const static std::string GRANTED_MODIFY_RIGHTS("GrantedModifyRights"), ADD_FRIEND_WITH_MESSAGE("AddFriendWithMessage"), USER_GIVE_ITEM("UserGiveItem"), OFFER_FRIENDSHIP("OfferFriendship"), FRIENDSHIP_ACCEPTED("FriendshipAccepted"), - FRIENDSHIP_OFFERED("FriendshipOffered"); + FRIENDSHIP_OFFERED("FriendshipOffered"), + FRIEND_ONLINE("FriendOnline"), FRIEND_OFFLINE("FriendOffline"), + SERVER_OBJECT_MESSAGE("ServerObjectMessage"); // static bool LLHandlerUtil::canLogToIM(const LLNotificationPtr& notification) @@ -55,7 +59,16 @@ bool LLHandlerUtil::canLogToIM(const LLNotificationPtr& notification) return GRANTED_MODIFY_RIGHTS == notification->getName() || REVOKED_MODIFY_RIGHTS == notification->getName() || PAYMENT_RECIVED == notification->getName() - || FRIENDSHIP_OFFERED == notification->getName(); + || FRIENDSHIP_OFFERED == notification->getName() + || SERVER_OBJECT_MESSAGE == notification->getName(); +} + +// static +bool LLHandlerUtil::canLogToNearbyChat(const LLNotificationPtr& notification) +{ + return notification->getType() == "notifytip" + && FRIEND_ONLINE != notification->getName() + && FRIEND_OFFLINE != notification->getName(); } // static @@ -144,3 +157,15 @@ void LLHandlerUtil::logGroupNoticeToIMGroup( payload["group_id"], sender_id); } +// static +void LLHandlerUtil::logToNearbyChat(const LLNotificationPtr& notification, EChatSourceType type) +{ + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + if(nearby_chat) + { + LLChat chat_msg(notification->getMessage()); + chat_msg.mSourceType = type; + nearby_chat->addMessage(chat_msg); + } +} + diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index 95f5ec801c..9afaddae82 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -88,18 +88,17 @@ bool LLTipHandler::processNotification(const LLSD& notify) if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") { // archive message in nearby chat - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); - if(nearby_chat) + if (LLHandlerUtil::canLogToNearbyChat(notification)) { - LLChat chat_msg(notification->getMessage()); - chat_msg.mSourceType = CHAT_SOURCE_SYSTEM; - nearby_chat->addMessage(chat_msg); + LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); // don't show toast if Nearby Chat is opened + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance< + LLNearbyChat>("nearby_chat", LLSD()); if (nearby_chat->getVisible()) { return true; - } + } } LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index eb9cb10d56..b9f422ca6f 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -51,6 +51,7 @@ #include "llfloaterworldmap.h" #include "llfloaterreg.h" #include "llnotificationsutil.h" +#include "llvoiceclient.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLDropTarget @@ -395,6 +396,7 @@ void LLPanelProfileTab::updateButtons() && gAgent.isGodlike() || is_agent_mappable(getAvatarId()); childSetEnabled("show_on_map_btn", enable_map_btn); + childSetEnabled("call", LLVoiceClient::voiceEnabled()); } ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index 2cb3967685..fff2575893 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -90,6 +90,7 @@ LLPanelGroup::LLPanelGroup() : LLPanel(), LLGroupMgrObserver( LLUUID() ), mAllowEdit( TRUE ) + ,mShowingNotifyDialog(false) { // Set up the factory callbacks. // Roles sub tabs @@ -538,3 +539,69 @@ void LLPanelGroup::showNotice(const std::string& subject, } +bool LLPanelGroup::canClose() +{ + if(getVisible() == false) + return true; + + bool need_save = false; + std::string mesg; + for(std::vector<LLPanelGroupTab* >::iterator it = mTabs.begin();it!=mTabs.end();++it) + if(need_save|=(*it)->needsApply(mesg)) + break; + if(!need_save) + return false; + // If no message was provided, give a generic one. + if (mesg.empty()) + { + mesg = mDefaultNeedsApplyMesg; + } + // Create a notify box, telling the user about the unapplied tab. + LLSD args; + args["NEEDS_APPLY_MESSAGE"] = mesg; + args["WANT_APPLY_MESSAGE"] = mWantApplyMesg; + + LLNotificationsUtil::add("SaveChanges", args, LLSD(), boost::bind(&LLPanelGroup::handleNotifyCallback,this, _1, _2)); + + mShowingNotifyDialog = true; + + return false; +} + +bool LLPanelGroup::notifyChildren(const LLSD& info) +{ + if(info.has("request") && mID.isNull() ) + { + std::string str_action = info["request"]; + + if (str_action == "quit" ) + { + canClose(); + return true; + } + if(str_action == "wait_quit") + return mShowingNotifyDialog; + } + return false; +} +bool LLPanelGroup::handleNotifyCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + mShowingNotifyDialog = false; + switch (option) + { + case 0: // "Apply Changes" + apply(); + break; + case 1: // "Ignore Changes" + break; + case 2: // "Cancel" + default: + // Do nothing. The user is canceling the action. + // If we were quitting, we didn't really mean it. + LLAppViewer::instance()->abortQuit(); + break; + } + return false; +} + diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index 306e6575fc..f6aefdb676 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -89,7 +89,10 @@ public: const std::string& inventory_name, LLOfferInfo* inventory_offer); - + + bool notifyChildren (const LLSD& info); + bool handleNotifyCallback(const LLSD&, const LLSD&); + protected: virtual void update(LLGroupChange gc); @@ -107,6 +110,9 @@ protected: protected: bool apply(LLPanelGroupTab* tab); + bool canClose(); + + bool mShowingNotifyDialog; LLTimer mRefreshTimer; diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 8c19865550..4f76d32ad5 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -101,14 +101,6 @@ void LLPanelChatControlPanel::draw() && callback_enabled; childSetEnabled("call_btn", enable_connect); - // send a signal when the floater is fully initialized - // this lets LLAvatarActions::startAdhocCall() start the call - if (enable_connect && !mInitialized) - { - LLIMModel::sendSessionInitialized(mSessionId); - mInitialized = true; - } - LLPanel::draw(); } diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index 871779b273..711340efc7 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -45,8 +45,7 @@ class LLPanelChatControlPanel : public LLPanel { public: LLPanelChatControlPanel() : - mSessionId(LLUUID()), - mInitialized(false) {}; + mSessionId(LLUUID()) {}; ~LLPanelChatControlPanel(); virtual BOOL postBuild(); @@ -63,7 +62,6 @@ public: private: LLUUID mSessionId; - bool mInitialized; // connection to voice channel state change signal boost::signals2::connection mVoiceChannelStateChangeConnection; diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index c85fab2092..32c9faa688 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -66,6 +66,30 @@ static const std::string TRASH_BUTTON_NAME = "trash_btn"; // helper functions static void filter_list(LLInventorySubTreePanel* inventory_list, const std::string& string); +static void save_folder_state_if_no_filter(LLInventorySubTreePanel* inventory_list); + +/** + * Bridge to support knowing when the inventory has changed to update folder (open/close) state + * for landmarks panels. + * + * Due to Inventory data are loaded in background we need to save folder state each time + * next level is loaded. See EXT-3094. + */ +class LLLandmarksPanelObserver : public LLInventoryObserver +{ +public: + LLLandmarksPanelObserver(LLLandmarksPanel* lp) : mLP(lp) {} + virtual ~LLLandmarksPanelObserver() {} + /*virtual*/ void changed(U32 mask); + +private: + LLLandmarksPanel* mLP; +}; + +void LLLandmarksPanelObserver::changed(U32 mask) +{ + mLP->saveFolderStateIfNoFilter(); +} LLLandmarksPanel::LLLandmarksPanel() : LLPanelPlacesTab() @@ -78,11 +102,18 @@ LLLandmarksPanel::LLLandmarksPanel() , mGearFolderMenu(NULL) , mGearLandmarkMenu(NULL) { + mInventoryObserver = new LLLandmarksPanelObserver(this); + gInventory.addObserver(mInventoryObserver); + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_landmarks.xml"); } LLLandmarksPanel::~LLLandmarksPanel() { + if (gInventory.containsObserver(mInventoryObserver)) + { + gInventory.removeObserver(mInventoryObserver); + } } BOOL LLLandmarksPanel::postBuild() @@ -226,6 +257,14 @@ void LLLandmarksPanel::onSelectorButtonClicked() } } +void LLLandmarksPanel::saveFolderStateIfNoFilter() +{ + save_folder_state_if_no_filter(mFavoritesInventoryPanel); + save_folder_state_if_no_filter(mLandmarksInventoryPanel); + save_folder_state_if_no_filter(mMyInventoryPanel); + save_folder_state_if_no_filter(mLibraryInventoryPanel); +} + ////////////////////////////////////////////////////////////////////////// // PROTECTED METHODS ////////////////////////////////////////////////////////////////////////// @@ -743,7 +782,7 @@ void LLLandmarksPanel::updateFilteredAccordions() { LLInventoryPanel* inventory_list = NULL; LLAccordionCtrlTab* accordion_tab = NULL; -// bool needs_arrange = false; + bool needs_arrange = false; for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter) { @@ -755,7 +794,7 @@ void LLLandmarksPanel::updateFilteredAccordions() if (NULL == inventory_list) continue; // This doesn't seem to work correctly. Disabling for now. -Seraph - /* + // Enabled to show/hide accordions with/without landmarks. See EXT-2346. (Seth PE) LLFolderView* fv = inventory_list->getRootFolder(); // arrange folder view contents to draw its descendants if it has any @@ -766,17 +805,17 @@ void LLLandmarksPanel::updateFilteredAccordions() needs_arrange = true; accordion_tab->setVisible(has_descendants); - */ - accordion_tab->setVisible(TRUE); + + //accordion_tab->setVisible(TRUE); } // we have to arrange accordion tabs for cases when filter string is less restrictive but // all items are still filtered. -// if (needs_arrange) -// { + if (needs_arrange) + { static LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("landmarks_accordion"); accordion->arrange(); -// } + } } /* @@ -1010,4 +1049,13 @@ static void filter_list(LLInventorySubTreePanel* inventory_list, const std::stri inventory_list->setFilterSubString(string); } + +static void save_folder_state_if_no_filter(LLInventorySubTreePanel* inventory_list) +{ + // save current folder open state if no filter currently applied + if (inventory_list->getRootFolder() && inventory_list->getRootFolder()->getFilterSubString().empty()) + { + // inventory_list->saveFolderState(); // *TODO: commented out to fix build + } +} // EOF diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index bee141d051..b0e537f647 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -67,6 +67,11 @@ public: mCurrentSelectedList = inventory_list; } + /** + * Saves folder state for all Inventory Panels if there are no applied filter. + */ + void saveFolderStateIfNoFilter(); + protected: /** * @return true - if current selected panel is not null and selected item is a landmark @@ -151,6 +156,7 @@ private: LLMenuGL* mGearFolderMenu; LLMenuGL* mMenuAdd; LLInventorySubTreePanel* mCurrentSelectedList; + LLInventoryObserver* mInventoryObserver; LLPanel* mListCommands; bool mSortByDate; diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 9d38dab26e..43366ef814 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -157,7 +157,7 @@ LLInventoryItem* LLTaskInvFVBridge::findItem() const LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(object) { - return (LLInventoryItem*)(object->getInventoryObject(mUUID)); + return dynamic_cast<LLInventoryItem*>(object->getInventoryObject(mUUID)); } return NULL; } @@ -712,6 +712,7 @@ public: virtual LLUIImagePtr getIcon() const; virtual const std::string& getDisplayName() const { return getName(); } virtual BOOL isItemRenameable() const; + // virtual BOOL isItemCopyable() const { return FALSE; } virtual BOOL renameItem(const std::string& new_name); virtual BOOL isItemRemovable(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 1e4682701e..5fb7dab7be 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -775,7 +775,7 @@ void LLPanelPeople::updateButtons() buttonSetEnabled("teleport_btn", friends_tab_active && item_selected && isFriendOnline(selected_uuids.front())); buttonSetEnabled("view_profile_btn", item_selected); buttonSetEnabled("im_btn", multiple_selected); // allow starting the friends conference for multiple selection - buttonSetEnabled("call_btn", multiple_selected); + buttonSetEnabled("call_btn", multiple_selected && LLVoiceClient::voiceEnabled()); buttonSetEnabled("share_btn", item_selected); // not implemented yet bool none_group_selected = item_selected && selected_id.isNull(); diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index 57f3d86d53..04fe42de9f 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -100,7 +100,7 @@ LLContextMenu* NearbyMenu::createMenu() registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startCall, id)); registrar.add("Avatar.OfferTeleport", boost::bind(&NearbyMenu::offerTeleport, this)); registrar.add("Avatar.ShowOnMap", boost::bind(&LLAvatarActions::startIM, id)); // *TODO: unimplemented - registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::startIM, id)); // *TODO: unimplemented + registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::share, id)); registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, id)); registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, id)); diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index f7f3c5830d..4a554c1b2c 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -854,6 +854,7 @@ void LLPanelPlaces::updateVerbs() mCancelBtn->setVisible(isLandmarkEditModeOn); mCloseBtn->setVisible(is_create_landmark_visible && !isLandmarkEditModeOn); + mShowOnMapBtn->setEnabled(!is_create_landmark_visible && !isLandmarkEditModeOn); mOverflowBtn->setEnabled(is_place_info_visible && !is_create_landmark_visible); if (is_place_info_visible) diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 13f195a1be..48a7a32a3b 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -278,6 +278,16 @@ void LLParticipantList::addAvatarIDExceptAgent(std::vector<LLUUID>& existing_lis // bool LLParticipantList::SpeakerAddListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) { + /** + * We need to filter speaking objects. These objects shouldn't appear in the list + * @c LLFloaterChat::addChat() in llviewermessage.cpp to get detailed call hierarchy + */ + const LLUUID& speaker_id = event->getValue().asUUID(); + LLPointer<LLSpeaker> speaker = mParent.mSpeakerMgr->findSpeaker(speaker_id); + if(speaker.isNull() || speaker->mType == LLSpeaker::SPEAKER_OBJECT) + { + return false; + } return mParent.onAddItemEvent(event, userdata); } @@ -314,6 +324,8 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() registrar.add("ParticipantList.ToggleAllowTextChat", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleAllowTextChat, this, _2)); registrar.add("ParticipantList.ToggleMuteText", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteText, this, _2)); + registrar.add("ParticipantList.ModerateVoice", boost::bind(&LLParticipantList::LLParticipantListMenu::moderateVoice, this, _2)); + enable_registrar.add("ParticipantList.EnableItem", boost::bind(&LLParticipantList::LLParticipantListMenu::enableContextMenuItem, this, _2)); enable_registrar.add("ParticipantList.CheckItem", boost::bind(&LLParticipantList::LLParticipantListMenu::checkContextMenuItem, this, _2)); @@ -322,6 +334,28 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() "menu_participant_list.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); } +void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const std::vector<LLUUID>& uuids, S32 x, S32 y) +{ + LLPanelPeopleMenus::ContextMenu::show(spawning_view, uuids, x, y); + + if (uuids.size() == 0) return; + + const LLUUID speaker_id = mUUIDs.front(); + BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, LLMute::flagVoiceChat); + + if (is_muted) + { + LLMenuGL::sMenuContainer->childSetVisible("ModerateVoiceMuteSelected", false); + LLMenuGL::sMenuContainer->childSetVisible("ModerateVoiceMuteOthers", false); + } + else + { + LLMenuGL::sMenuContainer->childSetVisible("ModerateVoiceUnMuteSelected", false); + LLMenuGL::sMenuContainer->childSetVisible("ModerateVoiceUnMuteOthers", false); + } + +} + void LLParticipantList::LLParticipantListMenu::toggleAllowTextChat(const LLSD& userdata) { const LLUUID speaker_id = mUUIDs.front(); @@ -379,10 +413,10 @@ void LLParticipantList::LLParticipantListMenu::toggleAllowTextChat(const LLSD& u new MuteTextResponder(mParent.mSpeakerMgr->getSessionID())); } -void LLParticipantList::LLParticipantListMenu::toggleMuteText(const LLSD& userdata) +void LLParticipantList::LLParticipantListMenu::toggleMute(const LLSD& userdata, U32 flags) { const LLUUID speaker_id = mUUIDs.front(); - BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, LLMute::flagTextChat); + BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, flags); std::string name; //fill in name using voice client's copy of name cache @@ -398,12 +432,54 @@ void LLParticipantList::LLParticipantListMenu::toggleMuteText(const LLSD& userda if (!is_muted) { - LLMuteList::getInstance()->add(mute, LLMute::flagTextChat); + LLMuteList::getInstance()->add(mute, flags); } else { - LLMuteList::getInstance()->remove(mute, LLMute::flagTextChat); + LLMuteList::getInstance()->remove(mute, flags); + } +} + +void LLParticipantList::LLParticipantListMenu::toggleMuteText(const LLSD& userdata) +{ + toggleMute(userdata, LLMute::flagTextChat); +} + +void LLParticipantList::LLParticipantListMenu::toggleMuteVoice(const LLSD& userdata) +{ + toggleMute(userdata, LLMute::flagVoiceChat); +} + +void LLParticipantList::LLParticipantListMenu::moderateVoice(const LLSD& userdata) +{ + +} +void LLParticipantList::LLParticipantListMenu::moderateVoiceOtherParticipants(const LLSD& userdata) +{ + LLSpeakerMgr::speaker_list_t speakers; + mParent.mSpeakerMgr->getSpeakerList(&speakers, true); + + const LLUUID& excluded_avatar_id = mUUIDs.front(); + bool should_mute = userdata.asString() == "mute"; + for (LLSpeakerMgr::speaker_list_t::iterator iter = speakers.begin(); + iter != speakers.end(); ++iter) + { + LLSpeaker* speakerp = (*iter).get(); + LLUUID speaker_id = speakerp->mID; + if (excluded_avatar_id == speaker_id) continue; + + LLMute mute(speaker_id, speakerp->mDisplayName, speakerp->mType == LLSpeaker::SPEAKER_AGENT ? LLMute::AGENT : LLMute::OBJECT); + + if (should_mute) + { + LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat); + } + else + { + LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat); + } } + } bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& userdata) @@ -414,7 +490,7 @@ bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& return mUUIDs.front() != gAgentID; } else - if (item == "can_allow_text_chat") + if (item == "can_allow_text_chat" || "can_moderate_voice" == item) { LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mParent.mSpeakerMgr->getSessionID()); return im_session->mType == IM_SESSION_GROUP_START && mParent.mSpeakerMgr->findSpeaker(gAgentID)->mIsModerator; diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 86b38f5f1e..83191a5b8d 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -117,6 +117,7 @@ class LLParticipantList public: LLParticipantListMenu(LLParticipantList& parent):mParent(parent){}; /*virtual*/ LLContextMenu* createMenu(); + /*virtual*/ void show(LLView* spawning_view, const std::vector<LLUUID>& uuids, S32 x, S32 y); protected: LLParticipantList& mParent; private: @@ -124,8 +125,13 @@ class LLParticipantList bool checkContextMenuItem(const LLSD& userdata); void toggleAllowTextChat(const LLSD& userdata); + void toggleMute(const LLSD& userdata, U32 flags); void toggleMuteText(const LLSD& userdata); + void toggleMuteVoice(const LLSD& userdata); + // Voice moderation support + void moderateVoice(const LLSD& userdata); + void moderateVoiceOtherParticipants(const LLSD& userdata); }; private: diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 0362fdbf56..8d80310769 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -54,6 +54,7 @@ #include "llscrolllistcell.h" #include "llslider.h" #include "lscript_rt_interface.h" +#include "lscript_library.h" #include "lscript_export.h" #include "lltextbox.h" #include "lltooldraganddrop.h" diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index 4f0c873c61..f66f725070 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -78,6 +78,22 @@ LLScreenChannelBase::~LLScreenChannelBase() { mWorldViewRectConnection.disconnect(); } + +bool LLScreenChannelBase::isHovering() +{ + bool res = mHoveredToast != NULL; + if (!res) + { + return res; + } + + S32 x, y; + mHoveredToast->screenPointToLocal(gViewerWindow->getCurrentMouseX(), + gViewerWindow->getCurrentMouseY(), &x, &y); + res = mHoveredToast->pointInView(x, y) == TRUE; + return res; +} + void LLScreenChannelBase::updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect) { S32 top_delta = old_world_rect.mTop - new_world_rect.mTop; @@ -125,6 +141,8 @@ LLScreenChannelBase(id) void LLScreenChannel::init(S32 channel_left, S32 channel_right) { LLScreenChannelBase::init(channel_left, channel_right); + LLRect world_rect = gViewerWindow->getWorldViewRectScaled(); + updatePositionAndSize(world_rect, world_rect); } //-------------------------------------------------------------------------- @@ -136,7 +154,23 @@ LLScreenChannel::~LLScreenChannel() //-------------------------------------------------------------------------- void LLScreenChannel::updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect) { - LLScreenChannelBase::updatePositionAndSize(old_world_rect, new_world_rect); + S32 right_delta = old_world_rect.mRight - new_world_rect.mRight; + LLRect this_rect = getRect(); + + this_rect.mTop = (S32) (new_world_rect.getHeight() * getHeightRatio()); + switch(mChannelAlignment) + { + case CA_LEFT : + break; + case CA_CENTRE : + this_rect.setCenterAndSize(new_world_rect.getWidth() / 2, new_world_rect.getHeight() / 2, this_rect.getWidth(), this_rect.getHeight()); + break; + case CA_RIGHT : + this_rect.mLeft -= right_delta; + this_rect.mRight -= right_delta; + } + setRect(this_rect); + redrawToasts(); } //-------------------------------------------------------------------------- @@ -169,6 +203,7 @@ void LLScreenChannel::addToast(const LLToast::Params& p) if(show_toast) { mToastList.push_back(new_toast_elem); + updateShowToastsState(); redrawToasts(); } else // store_toast @@ -224,6 +259,7 @@ void LLScreenChannel::deleteToast(LLToast* toast) if(mHoveredToast == toast) { mHoveredToast = NULL; + startFadingToasts(); } // close the toast @@ -405,26 +441,27 @@ void LLScreenChannel::showToastsBottom() { if( it != mToastList.rend()-1) { - stop_showing_toasts = ((*it).toast->getRect().mTop + gSavedSettings.getS32("OverflowToastHeight") + gSavedSettings.getS32("ToastGap")) > getRect().mTop; + S32 toast_top = (*it).toast->getRect().mTop + gSavedSettings.getS32("ToastGap"); + stop_showing_toasts = toast_top > getRect().mTop; } } + // at least one toast should be visible + if(it == mToastList.rbegin()) + { + stop_showing_toasts = false; + } + if(stop_showing_toasts) break; if( !(*it).toast->getVisible() ) { - if((*it).toast->isFirstLook()) - { - (*it).toast->setVisible(TRUE); - } - else - { - // HACK - // EXT-2653: it is necessary to prevent overlapping for secondary showed toasts - (*it).toast->setVisible(TRUE); - gFloaterView->sendChildToBack((*it).toast); - } + // HACK + // EXT-2653: it is necessary to prevent overlapping for secondary showed toasts + (*it).toast->setVisible(TRUE); + // Show toast behind floaters. (EXT-3089) + gFloaterView->sendChildToBack((*it).toast); } } @@ -566,6 +603,21 @@ void LLScreenChannel::createStartUpToast(S32 notif_num, F32 timer) mStartUpToastPanel->setVisible(TRUE); } +// static -------------------------------------------------------------------------- +F32 LLScreenChannel::getHeightRatio() +{ + F32 ratio = gSavedSettings.getF32("NotificationChannelHeightRatio"); + if(0.0f > ratio) + { + ratio = 0.0f; + } + else if(1.0f < ratio) + { + ratio = 1.0f; + } + return ratio; +} + //-------------------------------------------------------------------------- void LLScreenChannel::updateStartUpString(S32 num) { @@ -608,7 +660,7 @@ void LLNotificationsUI::LLScreenChannel::startFadingToasts() if (!mToastList.size()) return; //because onMouseLeave is processed after onMouseEnter - if (mHoveredToast) return; + if (isHovering()) return; std::vector<ToastElem>::iterator it = mToastList.begin(); while (it != mToastList.end()) @@ -685,39 +737,28 @@ void LLScreenChannel::removeToastsBySessionID(LLUUID id) //-------------------------------------------------------------------------- void LLScreenChannel::onToastHover(LLToast* toast, bool mouse_enter) { - // because of LLViewerWindow::updateUI() that ALWAYS calls onMouseEnter BEFORE onMouseLeave - // we must check this to prevent incorrect setting for hovering in a channel - std::map<LLToast*, bool>::iterator it_first, it_second; - S32 stack_size = mToastEventStack.size(); - if(mouse_enter) - { - mHoveredToast = toast; - } - else + // because of LLViewerWindow::updateUI() that NOT ALWAYS calls onMouseEnter BEFORE onMouseLeave + // we must check hovering directly to prevent incorrect setting for hovering in a channel + S32 x,y; + if (mouse_enter) { - mHoveredToast = NULL; - } - - switch(stack_size) - { - case 0: - mToastEventStack.insert(std::pair<LLToast*, bool>(toast, mouse_enter)); - break; - case 1: - it_first = mToastEventStack.begin(); - if((*it_first).second && !mouse_enter && ((*it_first).first != toast) ) + toast->screenPointToLocal(gViewerWindow->getCurrentMouseX(), + gViewerWindow->getCurrentMouseY(), &x, &y); + bool hover = toast->pointInView(x, y) == TRUE; + if (hover) { - mToastEventStack.clear(); mHoveredToast = toast; } - else + } + else if (mHoveredToast != NULL) + { + mHoveredToast->screenPointToLocal(gViewerWindow->getCurrentMouseX(), + gViewerWindow->getCurrentMouseY(), &x, &y); + bool hover = mHoveredToast->pointInView(x, y) == TRUE; + if (!hover) { - mToastEventStack.clear(); - mToastEventStack.insert(std::pair<LLToast*, bool>(toast, mouse_enter)); + mHoveredToast = NULL; } - break; - default: - LL_ERRS ("LLScreenChannel::onToastHover: stack size error " ) << stack_size << llendl; } if(!isHovering()) @@ -727,7 +768,7 @@ void LLScreenChannel::onToastHover(LLToast* toast, bool mouse_enter) //-------------------------------------------------------------------------- void LLScreenChannel::updateShowToastsState() { - LLFloater* floater = LLDockableFloater::getInstanceHandle().get(); + LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get()); if(!floater) { @@ -735,27 +776,17 @@ void LLScreenChannel::updateShowToastsState() return; } - // for IM floaters showed in a docked state - prohibit showing of ani toast - if(dynamic_cast<LLIMFloater*>(floater) - || dynamic_cast<LLScriptFloater*>(floater) ) - { - setShowToasts(!(floater->getVisible() && floater->isDocked())); - if (!getShowToasts()) - { - removeAndStoreAllStorableToasts(); - } - } - // *TODO: mantipov: what we have to do with derived classes: LLNotificationWellWindow & LLIMWelWindow? // See EXT-3081 for details // for Message Well floater showed in a docked state - adjust channel's height - if(dynamic_cast<LLSysWellWindow*>(floater)) + if(dynamic_cast<LLSysWellWindow*>(floater) || dynamic_cast<LLIMFloater*>(floater)) { S32 channel_bottom = gViewerWindow->getWorldViewRectScaled().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin");; LLRect this_rect = getRect(); if(floater->getVisible() && floater->isDocked()) { - channel_bottom += (floater->getRect().getHeight() + gSavedSettings.getS32("ToastGap")); + channel_bottom += floater->getRect().getHeight(); + channel_bottom += floater->getDockControl()->getTongueHeight(); } if(channel_bottom != this_rect.mBottom) diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index 67f1c9bdc6..b8efbb148f 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -95,7 +95,7 @@ public: virtual void setControlHovering(bool control) { mControlHovering = control; } - bool isHovering() { return mHoveredToast != NULL; } + bool isHovering(); void setCanStoreToasts(bool store) { mCanStoreToasts = store; } @@ -265,6 +265,11 @@ private: // create the StartUp Toast void createStartUpToast(S32 notif_num, F32 timer); + /** + * Notification channel and World View ratio(0.0 - always show 1 notification, 1.0 - max ratio). + */ + static F32 getHeightRatio(); + // Channel's flags static bool mWasStartUpToastShown; @@ -274,7 +279,6 @@ private: std::vector<ToastElem> mToastList; std::vector<ToastElem> mStoredToastList; - std::map<LLToast*, bool> mToastEventStack; }; } diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp index 8de99a48aa..5c4f6e8860 100644 --- a/indra/newview/llscriptfloater.cpp +++ b/indra/newview/llscriptfloater.cpp @@ -39,6 +39,7 @@ #include "llfloaterreg.h" #include "llnotifications.h" #include "llscreenchannel.h" +#include "llsyswellwindow.h" #include "lltoastnotifypanel.h" #include "llviewerwindow.h" #include "llimfloater.h" @@ -240,6 +241,14 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id) LLBottomTray::getInstance()->getChicletPanel()->createChiclet<LLScriptChiclet>(object_id); } + LLIMWellWindow::getInstance()->addObjectRow(object_id, set_new_message); + + LLSD data; + data["object_id"] = object_id; + data["new_message"] = set_new_message; + data["unread"] = 1; // each object has got only one floater + mNewObjectSignal(data); + toggleScriptFloater(object_id, set_new_message); } @@ -267,6 +276,8 @@ void LLScriptFloaterManager::onRemoveNotification(const LLUUID& notification_id) // remove related chiclet LLBottomTray::getInstance()->getChicletPanel()->removeChiclet(object_id); + LLIMWellWindow::getInstance()->removeObjectRow(object_id); + // close floater LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>("script_floater", notification_id); if(floater) @@ -291,13 +302,6 @@ void LLScriptFloaterManager::removeNotificationByObjectId(const LLUUID& object_i void LLScriptFloaterManager::toggleScriptFloater(const LLUUID& object_id, bool set_new_message) { - // hide "new message" icon from chiclet - LLIMChiclet* chiclet = LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(object_id); - if(chiclet) - { - chiclet->setShowNewMessagesIcon(set_new_message); - } - // kill toast using namespace LLNotificationsUI; LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()->findChannelByID( @@ -307,6 +311,11 @@ void LLScriptFloaterManager::toggleScriptFloater(const LLUUID& object_id, bool s channel->killToastByNotificationID(findNotificationToastId(object_id)); } + LLSD data; + data["object_id"] = object_id; + data["new_message"] = set_new_message; + mToggleFloaterSignal(data); + // toggle floater LLScriptFloater::toggle(object_id); } diff --git a/indra/newview/llscriptfloater.h b/indra/newview/llscriptfloater.h index 8de7a28d0f..95ec5a4d9c 100644 --- a/indra/newview/llscriptfloater.h +++ b/indra/newview/llscriptfloater.h @@ -88,6 +88,11 @@ public: */ static void onToastButtonClick(const LLSD¬ification, const LLSD&response); + typedef boost::signals2::signal<void(const LLSD&)> object_signal_t; + + boost::signals2::connection addNewObjectCallback(const object_signal_t::slot_type& cb) { return mNewObjectSignal.connect(cb); } + boost::signals2::connection addToggleObjectFloaterCallback(const object_signal_t::slot_type& cb) { return mToggleFloaterSignal.connect(cb); } + private: struct LLNotificationData @@ -100,6 +105,9 @@ private: typedef std::map<LLUUID, LLNotificationData> script_notification_map_t; script_notification_map_t mNotifications; + + object_signal_t mNewObjectSignal; + object_signal_t mToggleFloaterSignal; }; /** diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 4d6c19157a..44930f03c5 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -5754,21 +5754,21 @@ void LLSelectMgr::redo() //----------------------------------------------------------------------------- BOOL LLSelectMgr::canDoDelete() const { - bool can_delete = false;
- // This function is "logically const" - it does not change state in
- // a way visible outside the selection manager.
- LLSelectMgr* self = const_cast<LLSelectMgr*>(this);
- LLViewerObject* obj = self->mSelectedObjects->getFirstDeleteableObject();
- // Note: Can only delete root objects (see getFirstDeleteableObject() for more info)
- if (obj!= NULL)
- {
- // all the faces needs to be selected
- if(self->mSelectedObjects->contains(obj,SELECT_ALL_TES ))
- {
- can_delete = true;
- }
- }
-
+ bool can_delete = false; + // This function is "logically const" - it does not change state in + // a way visible outside the selection manager. + LLSelectMgr* self = const_cast<LLSelectMgr*>(this); + LLViewerObject* obj = self->mSelectedObjects->getFirstDeleteableObject(); + // Note: Can only delete root objects (see getFirstDeleteableObject() for more info) + if (obj!= NULL) + { + // all the faces needs to be selected + if(self->mSelectedObjects->contains(obj,SELECT_ALL_TES )) + { + can_delete = true; + } + } + return can_delete; } diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index bef7f5d6aa..1ebf624eeb 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -34,10 +34,12 @@ #include "llflatlistview.h" #include "llfloaterreg.h" +#include "llnotifications.h" #include "llsyswellwindow.h" #include "llbottomtray.h" +#include "llscriptfloater.h" #include "llviewercontrol.h" #include "llviewerwindow.h" @@ -170,6 +172,7 @@ void LLSysWellWindow::setVisible(BOOL visible) if(mChannel) { mChannel->updateShowToastsState(); + mChannel->redrawToasts(); } } @@ -188,6 +191,7 @@ void LLSysWellWindow::setDocked(bool docked, bool pop_on_undock) if(mChannel) { mChannel->updateShowToastsState(); + mChannel->redrawToasts(); } } @@ -356,7 +360,148 @@ BOOL LLIMWellWindow::RowPanel::handleMouseDown(S32 x, S32 y, MASK mask) return LLPanel::handleMouseDown(x, y, mask); } +/************************************************************************/ +/* ObjectRowPanel implementation */ +/************************************************************************/ + +LLIMWellWindow::ObjectRowPanel::ObjectRowPanel(const LLUUID& object_id, bool new_message/* = false*/) + : LLPanel() + , mChiclet(NULL) +{ + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_active_object_row.xml", NULL); + + initChiclet(object_id); + + LLTextBox* obj_name = getChild<LLTextBox>("object_name"); + obj_name->setValue(getObjectName(object_id)); + + mCloseBtn = getChild<LLButton>("hide_btn"); + mCloseBtn->setCommitCallback(boost::bind(&LLIMWellWindow::ObjectRowPanel::onClosePanel, this)); +} + +//--------------------------------------------------------------------------------- +LLIMWellWindow::ObjectRowPanel::~ObjectRowPanel() +{ +} + +std::string LLIMWellWindow::ObjectRowPanel::getObjectName(const LLUUID& object_id) +{ + using namespace LLNotificationsUI; + LLUUID notification_id = LLScriptFloaterManager::getInstance()->findNotificationId(object_id); + LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id); + if(!notification) + { + llwarns << "Invalid notification" << llendl; + return LLStringUtil::null; + } + + std::string text; + + switch(getObjectType(notification)) + { + case OBJ_SCRIPT: + text = notification->getSubstitutions()["TITLE"].asString(); + break; + case OBJ_LOAD_URL: + text = notification->getSubstitutions()["OBJECTNAME"].asString(); + break; + case OBJ_GIVE_INVENTORY: + text = notification->getSubstitutions()["NAME"].asString(); + break; + default: + text = getString("unknown_obj"); + break; + } + + return text; +} + +//--------------------------------------------------------------------------------- +void LLIMWellWindow::ObjectRowPanel::onClosePanel() +{ + LLScriptFloaterManager::getInstance()->removeNotificationByObjectId(mChiclet->getSessionId()); +} + +//static +LLIMWellWindow::ObjectRowPanel::object_type_map LLIMWellWindow::ObjectRowPanel::initObjectTypeMap() +{ + object_type_map type_map; + type_map["ScriptDialog"] = OBJ_SCRIPT; + type_map["LoadWebPage"] = OBJ_LOAD_URL; + type_map["ObjectGiveItem"] = OBJ_GIVE_INVENTORY; + return type_map; +} + +// static +LLIMWellWindow::ObjectRowPanel::EObjectType LLIMWellWindow::ObjectRowPanel::getObjectType(const LLNotificationPtr& notification) +{ + if(!notification) + { + llwarns << "Invalid notification" << llendl; + return OBJ_UNKNOWN; + } + + static object_type_map type_map = initObjectTypeMap(); + std::string name = notification->getName(); + object_type_map::const_iterator it = type_map.find(name); + if(it != type_map.end()) + { + return it->second; + } + + llwarns << "Unknown object type" << llendl; + return OBJ_UNKNOWN; +} + +void LLIMWellWindow::ObjectRowPanel::initChiclet(const LLUUID& object_id, bool new_message/* = false*/) +{ + using namespace LLNotificationsUI; + LLUUID notification_id = LLScriptFloaterManager::getInstance()->findNotificationId(object_id); + LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id); + if(!notification) + { + llwarns << "Invalid notification" << llendl; + return; + } + + // Choose which of the pre-created chiclets to use. + switch(getObjectType(notification)) + { + case OBJ_GIVE_INVENTORY: + mChiclet = getChild<LLInvOfferChiclet>("inv_offer_chiclet"); + break; + default: + mChiclet = getChild<LLScriptChiclet>("object_chiclet"); + break; + } + + mChiclet->setVisible(true); + mChiclet->setSessionId(object_id); +// mChiclet->setShowNewMessagesIcon(new_message); +} + +//--------------------------------------------------------------------------------- +void LLIMWellWindow::ObjectRowPanel::onMouseEnter(S32 x, S32 y, MASK mask) +{ + setTransparentColor(LLUIColorTable::instance().getColor("SysWellItemSelected")); +} + +//--------------------------------------------------------------------------------- +void LLIMWellWindow::ObjectRowPanel::onMouseLeave(S32 x, S32 y, MASK mask) +{ + setTransparentColor(LLUIColorTable::instance().getColor("SysWellItemUnselected")); +} + +//--------------------------------------------------------------------------------- +// virtual +BOOL LLIMWellWindow::ObjectRowPanel::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // Pass the mouse down event to the chiclet (EXT-596). + if (!mChiclet->pointInView(x, y) && !mCloseBtn->getRect().pointInRect(x, y)) // prevent double call of LLIMChiclet::onMouseDown() + mChiclet->onMouseDown(); + return LLPanel::handleMouseDown(x, y, mask); +} /************************************************************************/ /* LLNotificationWellWindow implementation */ @@ -503,6 +648,7 @@ LLIMWellWindow::LLIMWellWindow(const LLSD& key) { LLIMMgr::getInstance()->addSessionObserver(this); LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLIMWellWindow::findIMChiclet, this, _1)); + LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLIMWellWindow::findObjectChiclet, this, _1)); } LLIMWellWindow::~LLIMWellWindow() @@ -557,6 +703,18 @@ void LLIMWellWindow::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID } } +LLChiclet* LLIMWellWindow::findObjectChiclet(const LLUUID& object_id) +{ + LLChiclet* res = NULL; + ObjectRowPanel* panel = mMessageList->getTypedItemByValue<ObjectRowPanel>(object_id); + if (panel != NULL) + { + res = panel->mChiclet; + } + + return res; +} + ////////////////////////////////////////////////////////////////////////// // PRIVATE METHODS LLChiclet* LLIMWellWindow::findIMChiclet(const LLUUID& sessionId) @@ -615,4 +773,41 @@ void LLIMWellWindow::delIMRow(const LLUUID& sessionId) } } +void LLIMWellWindow::addObjectRow(const LLUUID& object_id, bool new_message/* = false*/) +{ + if (mMessageList->getItemByValue(object_id) == NULL) + { + ObjectRowPanel* item = new ObjectRowPanel(object_id, new_message); + if (mMessageList->insertItemAfter(mSeparator, item, object_id)) + { + handleItemAdded(IT_INSTANT_MESSAGE); + } + else + { + llwarns << "Unable to add Object Row into the list, objectID: " << object_id << llendl; + item->die(); + } + reshapeWindow(); + } +} + +void LLIMWellWindow::removeObjectRow(const LLUUID& object_id) +{ + if (mMessageList->removeItemByValue(object_id)) + { + handleItemRemoved(IT_INSTANT_MESSAGE); + } + else + { + llwarns << "Unable to remove Object Row from the list, objectID: " << object_id << llendl; + } + + reshapeWindow(); + // hide chiclet window if there are no items left + if(isWindowEmpty()) + { + setVisible(FALSE); + } +} + // EOF diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index adbc83677d..43b2723df0 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -46,7 +46,7 @@ class LLFlatListView; class LLChiclet; class LLIMChiclet; - +class LLScriptChiclet; class LLSysWellWindow : public LLDockableFloater @@ -126,14 +126,12 @@ protected: * * It contains a list of notifications that have not been responded to. */ -class LLNotificationWellWindow : public LLSysWellWindow, public LLInitClass<LLNotificationWellWindow> +class LLNotificationWellWindow : public LLSysWellWindow { public: LLNotificationWellWindow(const LLSD& key); static LLNotificationWellWindow* getInstance(const LLSD& key = LLSD()); - static void initClass() { getInstance(); } - /*virtual*/ BOOL postBuild(); /*virtual*/ void setVisible(BOOL visible); @@ -181,11 +179,16 @@ public: /*virtual*/ void sessionRemoved(const LLUUID& session_id); /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); + void addObjectRow(const LLUUID& object_id, bool new_message = false); + void removeObjectRow(const LLUUID& object_id); + protected: /*virtual*/ const std::string& getAnchorViewName() { return IM_WELL_ANCHOR_NAME; } private: LLChiclet * findIMChiclet(const LLUUID& sessionId); + LLChiclet* findObjectChiclet(const LLUUID& object_id); + void addIMRow(const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId); void delIMRow(const LLUUID& sessionId); @@ -210,6 +213,37 @@ private: LLButton* mCloseBtn; const LLSysWellWindow* mParent; }; + + class ObjectRowPanel: public LLPanel + { + typedef enum e_object_type + { + OBJ_UNKNOWN, + + OBJ_SCRIPT, + OBJ_GIVE_INVENTORY, + OBJ_LOAD_URL + }EObjectType; + + public: + ObjectRowPanel(const LLUUID& object_id, bool new_message = false); + virtual ~ObjectRowPanel(); + /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + private: + void onClosePanel(); + static EObjectType getObjectType(const LLNotificationPtr& notification); + void initChiclet(const LLUUID& object_id, bool new_message = false); + std::string getObjectName(const LLUUID& object_id); + + typedef std::map<std::string, EObjectType> object_type_map; + static object_type_map initObjectTypeMap(); + public: + LLIMChiclet* mChiclet; + private: + LLButton* mCloseBtn; + }; }; #endif // LL_LLSYSWELLWINDOW_H diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index ef49d7f057..67007629c0 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1630,6 +1630,16 @@ S32 LLTextureFetch::update(U32 max_time_ms) { sendRequestListToSimulators(); } + + if (!mThreaded) + { + // Update Curl on same thread as mCurlGetRequest was constructed + S32 processed = mCurlGetRequest->process(); + if (processed > 0) + { + lldebugs << "processed: " << processed << " messages." << llendl; + } + } return res; } diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index fc7c029a17..4131e2755a 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -67,7 +67,8 @@ LLToast::LLToast(const LLToast::Params& p) mHideBtn(NULL), mNotification(p.notification), mIsHidden(false), - mHideBtnPressed(false) + mHideBtnPressed(false), + mIsTip(p.is_tip) { LLUICtrlFactory::getInstance()->buildFloater(this, "panel_toast.xml", NULL); @@ -98,10 +99,30 @@ BOOL LLToast::postBuild() mTimer.stop(); } + if (mIsTip) + { + mTextEditor = mPanel->getChild<LLTextEditor>("text_editor_box"); + + if (mTextEditor) + { + mTextEditor->setMouseUpCallback(boost::bind(&LLToast::hide,this)); + mPanel->setMouseUpCallback(boost::bind(&LLToast::handleTipToastClick, this, _2, _3, _4)); + } + } + return TRUE; } //-------------------------------------------------------------------------- +void LLToast::handleTipToastClick(S32 x, S32 y, MASK mask) +{ + if (!mTextEditor->getRect().pointInRect(x, y)) + { + hide(); + } +} + +//-------------------------------------------------------------------------- void LLToast::setHideButtonEnabled(bool enabled) { if(mHideBtn) diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index d08e46e160..0c3c598704 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -40,6 +40,7 @@ #include "llnotificationptr.h" #include "llviewercontrol.h" +#include "lltexteditor.h" #define MOUSE_LEAVE false #define MOUSE_ENTER true @@ -155,6 +156,8 @@ public: private: + void handleTipToastClick(S32 x, S32 y, MASK mask); + // check timer bool lifetimeHasExpired(); // on timer finished function @@ -169,8 +172,9 @@ private: F32 mToastLifetime; // in seconds F32 mToastFadingTime; // in seconds - LLPanel* mPanel; - LLButton* mHideBtn; + LLPanel* mPanel; + LLButton* mHideBtn; + LLTextEditor* mTextEditor; LLColor4 mBgColor; bool mCanFade; @@ -178,6 +182,7 @@ private: bool mHideBtnEnabled; bool mHideBtnPressed; bool mIsHidden; // this flag is TRUE when a toast has faded or was hidden with (x) button (EXT-1849) + bool mIsTip; }; } diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index 7beba59c83..ed9e2e8818 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -85,7 +85,6 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif sys_msg_icon->setVisible(FALSE); mAvatar->setValue(p.avatar_id); - setMouseDownCallback(boost::bind(&LLToastIMPanel::onClickToastIM, this)); } S32 maxLinesCount; @@ -102,11 +101,13 @@ LLToastIMPanel::~LLToastIMPanel() { } -//-------------------------------------------------------------------------- -void LLToastIMPanel::onClickToastIM() +//virtual +BOOL LLToastIMPanel::handleMouseDown(S32 x, S32 y, MASK mask) { - mNotification->respond(mNotification->getResponseTemplate()); -} - -//-------------------------------------------------------------------------- + if (LLPanel::handleMouseDown(x,y,mask) == FALSE) + { + mNotification->respond(mNotification->getResponseTemplate()); + } + return TRUE; +} diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h index 23f08ef610..53661f2cf6 100644 --- a/indra/newview/lltoastimpanel.h +++ b/indra/newview/lltoastimpanel.h @@ -57,12 +57,10 @@ public: LLToastIMPanel(LLToastIMPanel::Params &p); virtual ~LLToastIMPanel(); - + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); private: static const S32 DEFAULT_MESSAGE_MAX_LINE_COUNT; - void onClickToastIM(); - LLNotificationPtr mNotification; LLUUID mSessionID; LLAvatarIconCtrl* mAvatar; diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 2d0a14dc70..aa35f22930 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -772,6 +772,9 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, { LLInventoryObject* cargo = locateInventory(item, cat); + // fix for EXT-3191 + if (NULL == cargo) return; + EAcceptance item_acceptance = ACCEPT_NO; handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, mCargoTypes[mCurItemIndex], diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 78322dda75..9def699708 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -59,6 +59,7 @@ #include "llfloaterland.h" #include "llfloaterpay.h" #include "llfloaterreporter.h" +#include "llfloatersearch.h" #include "llfloaterscriptdebug.h" #include "llfloatertools.h" #include "llfloaterworldmap.h" @@ -3408,6 +3409,13 @@ void set_god_level(U8 god_level) // changing god-level can affect which menus we see show_debug_menus(); + + // changing god-level can invalidate search results + LLFloaterSearch *search = dynamic_cast<LLFloaterSearch*>(LLFloaterReg::getInstance("search")); + if (search) + { + search->godLevelChanged(god_level); + } } #ifdef TOGGLE_HACKED_GODLIKE_VIEWER diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 8c0529e344..31bd264e3a 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2096,8 +2096,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) LLSD payload; payload["object_id"] = session_id; payload["owner_id"] = from_id; + payload["from_id"] = from_id; payload["slurl"] = location; payload["name"] = name; + std::string session_name; + gCacheName->getFullName(from_id, session_name); + payload["SESSION_NAME"] = session_name; if (from_group) { payload["groupowned"] = "true"; diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 2fae78cdfb..7a1abfd4e8 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -2525,7 +2525,8 @@ boost::signals2::connection LLViewerParcelMgr::setTeleportFailedCallback(parcel_ */ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos) { - if (local) + // Treat only teleports within the same parcel as local (EXT-3139). + if (local && LLViewerParcelMgr::getInstance()->inAgentParcel(new_pos)) { // Local teleport. We already have the agent parcel data. // Emit the signal immediately. @@ -2533,7 +2534,7 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos } else { - // Non-local teleport. + // Non-local teleport (inter-region or between different parcels of the same region). // The agent parcel data has not been updated yet. // Let's wait for the update and then emit the signal. mTeleportInProgress = TRUE; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 7e1e7c940f..63acba50e7 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -4279,6 +4279,7 @@ void LLVoiceClient::mediaStreamUpdatedEvent( { // Send the voice chat invite to the GUI layer // *TODO: Question: Should we correlate with the mute list here? + session->mIncoming = true; session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); session->mVoiceInvitePending = true; if(session->mName.empty()) @@ -6353,6 +6354,20 @@ LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participan return result; } +bool LLVoiceClient::isSessionIncoming(const LLUUID &session_id) +{ + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + if(session->mIMSessionID == session_id) + { + return session->mIncoming; + break; + } + } + return false; +} + LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle) { sessionState *result = NULL; diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 347fae6156..edfe0173f8 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -527,6 +527,8 @@ static void updatePosition(void); // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. bool isSessionTextIMPossible(const LLUUID &session_id); + + bool isSessionIncoming(const LLUUID &session_id); private: diff --git a/indra/newview/skins/default/textures/bottomtray/WellButton_Lit.png b/indra/newview/skins/default/textures/bottomtray/WellButton_Lit.png Binary files differnew file mode 100644 index 0000000000..60676b43fd --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/WellButton_Lit.png diff --git a/indra/newview/skins/default/textures/bottomtray/WellButton_Lit_Selected.png b/indra/newview/skins/default/textures/bottomtray/WellButton_Lit_Selected.png Binary files differnew file mode 100644 index 0000000000..98cde96aff --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/WellButton_Lit_Selected.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 607df10048..24d3512bcb 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -375,7 +375,7 @@ with the same filename but different name <texture name="Parcel_PushNo_Light" file_name="icons/Parcel_PushNo_Light.png" preload="false" /> <texture name="Parcel_R_Light" file_name="icons/Parcel_R_Light.png" preload="false" /> <texture name="Parcel_Scripts_Light" file_name="icons/Parcel_Scripts_Light.png" preload="false" /> - <texture name="Parcel_ScriptsNo_Light" file_name="icons/Parcel_ScriptsNo_Light.png" preload="false" /> + <texture name="Parcel_ScriptsNo_Light" file_name="icons/Parcel_ScriptsNo_Dark.png" preload="false" /> <texture name="Parcel_Voice_Light" file_name="icons/Parcel_Voice_Light.png" preload="false" /> <texture name="Parcel_VoiceNo_Light" file_name="icons/Parcel_VoiceNo_Light.png" preload="false" /> @@ -592,6 +592,9 @@ with the same filename but different name <texture name="Unread_IM" file_name="bottomtray/Unread_IM.png" preload="false" /> <texture name="Unread_Msg" file_name="bottomtray/Unread_Msg.png" preload="false" /> + + <texture name="WellButton_Lit" file_name="bottomtray/WellButton_Lit.png" /> + <texture name="WellButton_Lit_Selected" file_name="bottomtray/WellButton_Lit_Selected.png" /> <texture name="VoicePTT_Lvl1" file_name="bottomtray/VoicePTT_Lvl1.png" preload="false" /> <texture name="VoicePTT_Lvl2" file_name="bottomtray/VoicePTT_Lvl2.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/en/floater_incoming_call.xml b/indra/newview/skins/default/xui/en/floater_incoming_call.xml index 526fda90d1..acd59b6f09 100644 --- a/indra/newview/skins/default/xui/en/floater_incoming_call.xml +++ b/indra/newview/skins/default/xui/en/floater_incoming_call.xml @@ -36,21 +36,25 @@ top="35" width="36" /> <text + clip_partial="true" font="SansSerifLarge" - height="20" + height="37" layout="topleft" left="77" name="caller name" - top="27" + top="20" + use_ellipses="true" width="315" word_wrap="true" /> <text + clip_partial="true" font="SansSerif" - height="50" + height="30" layout="topleft" left="77" name="question" - top="52" + top_pad="5" + use_ellipses="true" width="315" word_wrap="true"> Do you want to leave [CURRENT_CHAT] and join this voice chat? diff --git a/indra/newview/skins/default/xui/en/floater_search.xml b/indra/newview/skins/default/xui/en/floater_search.xml index 82e4d87b28..e6bdcdf78e 100644 --- a/indra/newview/skins/default/xui/en/floater_search.xml +++ b/indra/newview/skins/default/xui/en/floater_search.xml @@ -55,8 +55,20 @@ layout="topleft" left_delta="0" name="status_text" - top_pad="5" + top_pad="7" width="150" /> + <text + visible="false" + follows="bottom|right" + height="16" + left_delta="0" + name="refresh_search" + left_pad="0" + right="-10" + halign="right" + width="450"> + Redo search to reflect current God level + </text> </layout_panel> </layout_stack> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_participant_list.xml b/indra/newview/skins/default/xui/en/menu_participant_list.xml index 5ab327a182..0422972cd4 100644 --- a/indra/newview/skins/default/xui/en/menu_participant_list.xml +++ b/indra/newview/skins/default/xui/en/menu_participant_list.xml @@ -89,4 +89,51 @@ function="ParticipantList.EnableItem" parameter="can_allow_text_chat" /> </menu_item_check> + <menu_item_separator + layout="topleft" + name="moderate_voice_separator" /> + <menu_item_call + label="Mute this participant" + layout="topleft" + name="ModerateVoiceMuteSelected"> + <on_click + function="ParticipantList.ModerateVoice" + parameter="selected" /> + <on_enable + function="ParticipantList.EnableItem" + parameter="can_moderate_voice" /> + </menu_item_call> + <menu_item_call + label="Mute everyone else" + layout="topleft" + name="ModerateVoiceMuteOthers"> + <on_click + function="ParticipantList.ModerateVoice" + parameter="others" /> + <on_enable + function="ParticipantList.EnableItem" + parameter="can_moderate_voice" /> + </menu_item_call> + <menu_item_call + label="Unmute this participant" + layout="topleft" + name="ModerateVoiceUnMuteSelected"> + <on_click + function="ParticipantList.ModerateVoice" + parameter="selected" /> + <on_enable + function="ParticipantList.EnableItem" + parameter="can_moderate_voice" /> + </menu_item_call> + <menu_item_call + label="Unmute everyone else" + layout="topleft" + name="ModerateVoiceUnMuteOthers"> + <on_click + function="ParticipantList.ModerateVoice" + parameter="others" /> + <on_enable + function="ParticipantList.EnableItem" + parameter="can_moderate_voice" /> + </menu_item_call> </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml index c3a2540b2e..39469f7101 100644 --- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml +++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml @@ -34,7 +34,6 @@ function="Avatar.Call" /> </menu_item_call> <menu_item_call - enabled="false" label="Share" layout="topleft" name="Share"> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 3659db230c..22896d59c3 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4719,10 +4719,6 @@ The objects on the selected parcel that are NOT owned by you have been returned type="notify"> Message from [NAME]: [MSG] - <usetemplate - name="okcancelbuttons" - notext="OK" - yestext="Inspect"/> </notification> <notification @@ -5754,6 +5750,23 @@ You just entered a region using a different server version, which may affect per The SLurl you clicked on is not supported. </notification> + <notification + icon="notifytip.tga" + name="BlockedSLURL" + priority="high" + type="notifytip"> +A SLurl was received from an untrusted browser and has been blocked for your security. + </notification> + + <notification + icon="notifytip.tga" + name="ThrottledSLURL" + priority="high" + type="notifytip"> +Multiple SLurls were received from an untrusted browser within a short period. +They will be blocked for a few seconds for your security. + </notification> + <notification name="IMToast" type="notifytoast"> [MESSAGE] <form name="form"> diff --git a/indra/newview/skins/default/xui/en/panel_active_object_row.xml b/indra/newview/skins/default/xui/en/panel_active_object_row.xml new file mode 100644 index 0000000000..7657fb8055 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_active_object_row.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + name="panel_activeim_row" + layout="topleft" + follows="left|right" + top="0" + left="0" + height="35" + width="318" + background_opaque="false" + background_visible="true" + bg_alpha_color="0.0 0.0 0.0 0.0" > + <string + name="unknown_obj"> + Unknown Object + </string> + <chiclet_script + name="object_chiclet" + layout="topleft" + follows="left" + top="3" + left="5" + height="25" + width="25" + visible="false" + speaker.name="speaker_p2p" + speaker.width="20" + speaker.height="25" + speaker.left="25" + speaker.top="25" + speaker.auto_update="true" + speaker.draw_border="false" + speaker.visible="false"> + </chiclet_script> + <chiclet_offer + name="inv_offer_chiclet" + layout="topleft" + follows="left" + top="3" + left="5" + height="25" + width="25" + visible="false" + speaker.name="speaker_p2p" + speaker.width="20" + speaker.height="25" + speaker.left="25" + speaker.top="25" + speaker.auto_update="true" + speaker.draw_border="false" + speaker.visible="false"> + </chiclet_offer> + <text + type="string" + name="object_name" + layout="topleft" + top="10" + left_pad="20" + height="14" + width="245" + length="1" + follows="right|left" + use_ellipses="true" + font="SansSerifBold"> + Unnamed Object + </text> + <button + top="10" + right="-5" + width="17" + height="17" + layout="topleft" + follows="right" + name="hide_btn" + mouse_opaque="true" + label="" + tab_stop="false" + image_unselected="Toast_CloseBtn" + image_selected="Toast_CloseBtn" + /> +</panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 7f847237ce..a41d492624 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -324,6 +324,7 @@ as for parent layout_panel (chiclet_list_panel) to resize bottom tray properly. min_width="34" user_resize="false"> <chiclet_im_well + flash_period="0.3" follows="right" height="23" layout="topleft" @@ -331,6 +332,14 @@ as for parent layout_panel (chiclet_list_panel) to resize bottom tray properly. name="im_well" top="4" width="34"> + <!-- +Emulate 4 states of button by background images, see detains in EXT-3147. The same should be for notification_well button +xml attribute Description +image_unselected "Unlit" - there are no new messages +image_selected "Unlit" + "Selected" - there are no new messages and the Well is open +image_pressed "Lit" - there are new messages +image_pressed_selected "Lit" + "Selected" - there are new messages and the Well is open + --> <button auto_resize="true" flash_color="EmphasisColor" @@ -339,10 +348,11 @@ as for parent layout_panel (chiclet_list_panel) to resize bottom tray properly. height="23" image_overlay="Notices_Unread" image_overlay_alignment="center" - image_pressed="PushButton_Press" - image_pressed_selected="PushButton_Selected_Press" + image_pressed="WellButton_Lit" + image_pressed_selected="WellButton_Lit_Selected" image_selected="PushButton_Selected_Press" left="0" + max_displayed_count="99" name="Unread IM messages" pad_left="0" pad_right="0" @@ -365,17 +375,19 @@ as for parent layout_panel (chiclet_list_panel) to resize bottom tray properly. min_width="34" user_resize="false"> <chiclet_notification + flash_period="0.25" follows="right" height="23" layout="topleft" left="0" + max_displayed_count="99" name="notification_well" top="4" width="34"> <button image_selected="PushButton_Selected_Press" - image_pressed="PushButton_Press" - image_pressed_selected="PushButton_Selected_Press" + image_pressed="WellButton_Lit" + image_pressed_selected="WellButton_Lit_Selected" auto_resize="true" halign="center" height="23" diff --git a/indra/newview/skins/default/xui/en/panel_group_general.xml b/indra/newview/skins/default/xui/en/panel_group_general.xml index e5dc4df0f8..af73faf9a1 100644 --- a/indra/newview/skins/default/xui/en/panel_group_general.xml +++ b/indra/newview/skins/default/xui/en/panel_group_general.xml @@ -23,7 +23,7 @@ Hover your mouse over the options for more help. </panel.string> <text_editor type="string" - follows="left|top" + follows="left|top|right" left="5" height="60" layout="topleft" @@ -37,7 +37,7 @@ Hover your mouse over the options for more help. <name_list column_padding="0" draw_heading="true" - follows="left|top" + follows="left|top|right" heading_height="20" height="156" layout="topleft" @@ -55,10 +55,10 @@ Hover your mouse over the options for more help. relative_width="0.4" /> </name_list> <text - follows="left|top" + follows="left|top|right" type="string" height="12" - layout="topleft" + layout="left|top|right" left="5" name="active_title_label" top_pad="5" @@ -66,7 +66,7 @@ Hover your mouse over the options for more help. My Title </text> <combo_box - follows="left|top" + follows="left|top|right" height="20" layout="topleft" left="5" @@ -98,7 +98,7 @@ Hover your mouse over the options for more help. bevel_style="in" border="true" bg_alpha_color="FloaterUnfocusBorderColor" - follows="left|top" + follows="left|top|right" height="88" layout="topleft" left="2" @@ -106,7 +106,7 @@ Hover your mouse over the options for more help. name="preferences_container" top_pad="2"> <check_box - follows="right|top" + follows="right|top|left" height="16" label="Open enrollment" layout="topleft" @@ -126,21 +126,21 @@ Hover your mouse over the options for more help. width="300" /> <spinner decimal_digits="0" - follows="left|top" + follows="left|top|right" halign="left" height="16" increment="1" label_width="15" label="L$" layout="topleft" - right="-10" + right="-30" max_val="99999" left_pad="0" name="spin_enrollment_fee" tool_tip="New members must pay this fee to join the group when Enrollment Fee is checked." - width="100" /> + width="80" /> <combo_box - follows="left|top" + follows="left|top|right" height="20" layout="topleft" left="10" @@ -158,7 +158,7 @@ Hover your mouse over the options for more help. value="Mature" /> </combo_box> <check_box - follows="left|top" + follows="left|top|right" height="16" initial_value="true" label="Show in search" diff --git a/indra/newview/skins/default/xui/en/panel_notes.xml b/indra/newview/skins/default/xui/en/panel_notes.xml index c02dabed2c..9e7c9477d4 100644 --- a/indra/newview/skins/default/xui/en/panel_notes.xml +++ b/indra/newview/skins/default/xui/en/panel_notes.xml @@ -120,6 +120,7 @@ left="0" mouse_opaque="false" name="add_friend" + tool_tip="Offer friendship to the resident" top="5" width="55" /> <button @@ -128,6 +129,7 @@ label="IM" layout="topleft" name="im" + tool_tip="Open instant message session" top="5" left_pad="5" width="40" /> @@ -137,6 +139,7 @@ label="Call" layout="topleft" name="call" + tool_tip="Call this resident" left_pad="5" top="5" width="55" /> @@ -147,6 +150,7 @@ label="Map" layout="topleft" name="show_on_map_btn" + tool_tip="Show the resident on the map" top="5" left_pad="5" width="50" /> @@ -156,6 +160,7 @@ label="Teleport" layout="topleft" name="teleport" + tool_tip="Offer teleport" left_pad="5" top="5" width="90" /> diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml index 6be203ef9c..638bc3cabd 100644 --- a/indra/newview/skins/default/xui/en/panel_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_profile.xml @@ -286,6 +286,7 @@ left="0" mouse_opaque="false" name="add_friend" + tool_tip="Offer friendship to the resident" top="5" width="77" /> <button @@ -294,6 +295,7 @@ label="IM" layout="topleft" name="im" + tool_tip="Open instant message session" top="5" left_pad="5" width="33" /> @@ -303,6 +305,7 @@ label="Call" layout="topleft" name="call" + tool_tip="Call this resident" left_pad="5" top="5" width="40" /> @@ -313,6 +316,7 @@ label="Map" layout="topleft" name="show_on_map_btn" + tool_tip="Show the resident on the map" top="5" left_pad="5" width="44" /> @@ -322,6 +326,7 @@ label="Teleport" layout="topleft" name="teleport" + tool_tip="Offer teleport" left_pad="5" top="5" width="67" /> @@ -331,6 +336,7 @@ label="▼" layout="topleft" name="overflow_btn" + tool_tip="Pay money to or share inventory with the resident" right="-1" top="5" width="21" /> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index d5022de089..c3650c71c3 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -2856,6 +2856,10 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. <string name="inventory_item_offered-im"> Inventory item offered </string> + <string name="share_alert"> + Drag items from inventory here + </string> + <string name="only_user_message"> You are the only user in this session. diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp index 30d338292e..d183aac208 100644 --- a/indra/test_apps/llplugintest/llmediaplugintest.cpp +++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp @@ -1537,7 +1537,7 @@ void LLMediaPluginTest::addMediaPanel( std::string url ) char cwd[ FILENAME_MAX ]; if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) { - std::cerr << "Couldn't get cwd - probably too long - failing to init." << llendl; + std::cerr << "Couldn't get cwd - probably too long - failing to init." << std::endl; return; } std::string user_data_path = std::string( cwd ) + "/"; @@ -1774,7 +1774,7 @@ void LLMediaPluginTest::replaceMediaPanel( mediaPanel* panel, std::string url ) char cwd[ FILENAME_MAX ]; if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) { - std::cerr << "Couldn't get cwd - probably too long - failing to init." << llendl; + std::cerr << "Couldn't get cwd - probably too long - failing to init." << std::endl; return; } std::string user_data_path = std::string( cwd ) + "/"; |