diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llscreenchannel.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/newview/llscreenchannel.cpp')
-rw-r--r-- | indra/newview/llscreenchannel.cpp | 2271 |
1 files changed, 1136 insertions, 1135 deletions
diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index bac79594a2..c766462227 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -1,1135 +1,1136 @@ -/** - * @file llscreenchannel.cpp - * @brief Class implements a channel on a screen in which appropriate toasts may appear. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - - -#include "llviewerprecompiledheaders.h" // must be first include - -#include "lliconctrl.h" -#include "lltextbox.h" -#include "llscreenchannel.h" - -#include "lltoastpanel.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llfloaterreg.h" -#include "lltrans.h" -#include "llagent.h" -#include "lldockablefloater.h" -#include "llsyswellwindow.h" -#include "llfloaterimsession.h" -#include "llscriptfloater.h" -#include "llrootview.h" - -#include <algorithm> - -using namespace LLNotificationsUI; - -bool LLScreenChannel::mWasStartUpToastShown = false; - -LLRect LLScreenChannelBase::getChannelRect() -{ - LL_PROFILE_ZONE_SCOPED; - - if (mFloaterSnapRegion == NULL) - { - mFloaterSnapRegion = gViewerWindow->getRootView()->getChildView("floater_snap_region"); - } - - if (mChicletRegion == NULL) - { - mChicletRegion = gViewerWindow->getRootView()->getChildView("chiclet_container"); - } - - LLRect channel_rect; - LLRect chiclet_rect; - - mFloaterSnapRegion->localRectToScreen(mFloaterSnapRegion->getLocalRect(), &channel_rect); - mChicletRegion->localRectToScreen(mChicletRegion->getLocalRect(), &chiclet_rect); - - channel_rect.mTop = chiclet_rect.mBottom; - return channel_rect; -} - - -//-------------------------------------------------------------------------- -////////////////////// -// LLScreenChannelBase -////////////////////// - -LLScreenChannelBase::LLScreenChannelBase(const Params& p) -: LLUICtrl(p), - mToastAlignment(p.toast_align), - mCanStoreToasts(true), - mHiddenToastsNum(0), - mHoveredToast(NULL), - mControlHovering(false), - mShowToasts(true), - mID(p.id), - mDisplayToastsAlways(p.display_toasts_always), - mChannelAlignment(p.channel_align), - mFloaterSnapRegion(NULL), - mChicletRegion(NULL) -{ - mID = p.id; - - setMouseOpaque( false ); - setVisible(false); -} - -bool LLScreenChannelBase::postBuild() -{ - if (mFloaterSnapRegion == NULL) - { - mFloaterSnapRegion = gViewerWindow->getRootView()->getChildView("floater_snap_region"); - } - - if (mChicletRegion == NULL) - { - mChicletRegion = gViewerWindow->getRootView()->getChildView("chiclet_container"); - } - - return true; -} - -void LLScreenChannelBase::reshape(S32 width, S32 height, bool called_from_parent) -{ - if (mChannelAlignment == CA_CENTRE) - { - // Keep notifications and alerts centered - // WorldViewRectScaled is out of date at reshape but Window has same width - S32 channel_bound = gViewerWindow->getWindowRectScaled().getWidth() / 2; - setRect(LLRect(channel_bound, 0, channel_bound, 0)); - updateRect(); //sets top and bottom only - } - redrawToasts(); -} - -bool LLScreenChannelBase::isHovering() -{ - if (!mHoveredToast) - { - return false; - } - - return mHoveredToast->isHovered(); -} - -void LLScreenChannelBase::updatePositionAndSize(LLRect rect) -{ - LLRect this_rect = getRect(); - - this_rect.mTop = rect.mTop; - switch(mChannelAlignment) - { - case CA_LEFT : - break; - case CA_CENTRE : - this_rect.setCenterAndSize( (rect.getWidth()) / 2, rect.getHeight() / 2, this_rect.getWidth(), this_rect.getHeight()); - break; - case CA_RIGHT : - this_rect.setLeftTopAndSize(rect.mRight - this_rect.getWidth(), - this_rect.mTop, - this_rect.getWidth(), - this_rect.getHeight()); - } - setRect(this_rect); - redrawToasts(); - -} - -void LLScreenChannelBase::init(S32 channel_left, S32 channel_right) -{ - // top and bottom set by updateRect() - setRect(LLRect(channel_left, 0, channel_right, 0)); - updateRect(); - setVisible(true); -} - -void LLScreenChannelBase::updateRect() -{ - S32 channel_top = getChannelRect().mTop; - S32 channel_bottom = getChannelRect().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin"); - S32 channel_left = getRect().mLeft; - S32 channel_right = getRect().mRight; - setRect(LLRect(channel_left, channel_top, channel_right, channel_bottom)); -} - -//-------------------------------------------------------------------------- -////////////////////// -// LLScreenChannel -////////////////////// -//-------------------------------------------------------------------------- -LLScreenChannel::LLScreenChannel(const Params& p) -: LLScreenChannelBase(p), - mStartUpToastPanel(NULL) -{ -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::init(S32 channel_left, S32 channel_right) -{ - LLScreenChannelBase::init(channel_left, channel_right); - LLRect channel_rect = getChannelRect(); - updatePositionAndSize(channel_rect); -} - -//-------------------------------------------------------------------------- -LLScreenChannel::~LLScreenChannel() -{ - -} - -std::list<const LLToast*> LLScreenChannel::findToasts(const Matcher& matcher) -{ - std::list<const LLToast*> res; - - // collect stored toasts - for (std::vector<ToastElem>::iterator it = mStoredToastList.begin(); it - != mStoredToastList.end(); it++) - { - const LLToast* toast = it->getToast(); - if (toast && matcher.matches(toast->getNotification())) - { - res.push_back(toast); - } - } - - // collect displayed toasts - for (std::vector<ToastElem>::iterator it = mToastList.begin(); it - != mToastList.end(); it++) - { - const LLToast* toast = it->getToast(); - if (toast && matcher.matches(toast->getNotification())) - { - res.push_back(toast); - } - } - - return res; -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::updatePositionAndSize(LLRect new_world_rect) -{ - LLRect this_rect = getRect(); - - switch(mChannelAlignment) - { - case CA_LEFT : - this_rect.mTop = (S32) (new_world_rect.getHeight() * getHeightRatio()); - break; - case CA_CENTRE : - LLScreenChannelBase::updatePositionAndSize(new_world_rect); - return; - case CA_RIGHT : - this_rect.mTop = (S32) (new_world_rect.getHeight() * getHeightRatio()); - this_rect.setLeftTopAndSize(new_world_rect.mRight - this_rect.getWidth(), - this_rect.mTop, - this_rect.getWidth(), - this_rect.getHeight()); - } - setRect(this_rect); - redrawToasts(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::addToast(const LLToast::Params& p) -{ - LL_PROFILE_ZONE_SCOPED - bool store_toast = false, show_toast = false; - - if (mDisplayToastsAlways) - { - show_toast = true; - } - else - { - show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); - } - store_toast = !show_toast && p.can_be_stored && mCanStoreToasts; - - if(!show_toast && !store_toast) - { - if(gAgent.isDoNotDisturb()) - { - return; - } - LLNotificationPtr notification = LLNotifications::instance().find(p.notif_id); - - if (notification && - (!notification->canLogToIM() || !notification->hasFormElements())) - { - // only cancel notification if it isn't being used in IM session - LLNotifications::instance().cancel(notification); - } - - // It was assumed that the toast would take ownership of the panel pointer. - // But since we have decided not to display the toast, kill the panel to - // prevent the memory leak. - if (p.panel != NULL) - { - p.panel()->die(); - } - return; - } - - LLToast* toast = new LLToast(p); - ToastElem new_toast_elem(toast->getHandle()); - - toast->setOnFadeCallback(boost::bind(&LLScreenChannel::onToastFade, this, _1)); - toast->setOnToastDestroyedCallback(boost::bind(&LLScreenChannel::onToastDestroyed, this, _1)); - if(mControlHovering) - { - toast->setOnToastHoverCallback(boost::bind(&LLScreenChannel::onToastHover, this, _1, _2)); - toast->setMouseEnterCallback(boost::bind(&LLScreenChannel::stopToastTimer, this, toast)); - toast->setMouseLeaveCallback(boost::bind(&LLScreenChannel::startToastTimer, this, toast)); - } - - if(show_toast) - { - mToastList.push_back(new_toast_elem); - if(p.can_be_stored) - { - // store toasts immediately - EXT-3762 - storeToast(new_toast_elem); - } - updateShowToastsState(); - redrawToasts(); - } - else // store_toast - { - mHiddenToastsNum++; - storeToast(new_toast_elem); - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::onToastDestroyed(LLToast* toast) -{ - std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), static_cast<LLPanel*>(toast)); - - if(it != mToastList.end()) - { - mToastList.erase(it); - } - - it = find(mStoredToastList.begin(), mStoredToastList.end(), static_cast<LLPanel*>(toast)); - - if(it != mStoredToastList.end()) - { - mStoredToastList.erase(it); - } - - // if destroyed toast is hovered - reset hovered - if (mHoveredToast == toast) - { - mHoveredToast = NULL; - } -} - - -//-------------------------------------------------------------------------- -void LLScreenChannel::onToastFade(LLToast* toast) -{ - std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), static_cast<LLPanel*>(toast)); - - if(it != mToastList.end()) - { - bool delete_toast = !mCanStoreToasts || !toast->getCanBeStored(); - if(delete_toast) - { - mToastList.erase(it); - deleteToast(toast); - } - else - { - storeToast((*it)); - mToastList.erase(it); - } - - redrawToasts(); - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::deleteToast(LLToast* toast) -{ - if (!toast || toast->isDead()) - { - return; - } - - // send signal to observers about destroying of a toast - toast->closeToast(); - - // update channel's Hovering state - // turning hovering off manually because onMouseLeave won't happen if a toast was closed using a keyboard - if(mHoveredToast == toast) - { - mHoveredToast = NULL; - } -} - -//-------------------------------------------------------------------------- - -void LLScreenChannel::storeToast(ToastElem& toast_elem) -{ - // do not store clones - std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), toast_elem.getID()); - if( it != mStoredToastList.end() ) - return; - - const LLToast* toast = toast_elem.getToast(); - if (toast) - { - mStoredToastList.push_back(toast_elem); - mOnStoreToast(toast->getPanel(), toast->getNotificationID()); - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::loadStoredToastsToChannel() -{ - std::vector<ToastElem>::iterator it; - - if(mStoredToastList.size() == 0) - return; - - for(it = mStoredToastList.begin(); it != mStoredToastList.end(); ++it) - { - LLToast* toast = it->getToast(); - if (toast) - { - toast->setIsHidden(false); - toast->startTimer(); - mToastList.push_back(*it); - } - } - - mStoredToastList.clear(); - redrawToasts(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) -{ - std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - - if( it == mStoredToastList.end() ) - return; - - LLToast* toast = it->getToast(); - if (toast) - { - if(toast->getVisible()) - { - // toast is already in channel - return; - } - - toast->setIsHidden(false); - toast->startTimer(); - mToastList.push_back(*it); - } - - redrawToasts(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::killToastByNotificationID(LLUUID id) -{ - // searching among toasts on a screen - std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); - LLNotificationPtr notification = LLNotifications::instance().find(id); - if (!notification) return; - - if( it != mToastList.end()) - { - LLToast* toast = it->getToast(); - // if it is a notification toast and notification is UnResponded - then respond on it - // else - simply destroy a toast - // - // NOTE: if a notification is unresponded this function will be called twice for the same toast. - // At first, the notification will be discarded, at second (it will be caused by discarding), - // the toast will be destroyed. - if(toast && toast->isNotificationValid()) - { - if (!notification->canLogToIM() || !notification->hasFormElements()) - { - // only cancel notification if it isn't being used in IM session - LLNotifications::instance().cancel(notification); - } - } - else - { - removeToastByNotificationID(id); - } - } - else - { - // searching among stored toasts - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - - if( it != mStoredToastList.end() ) - { - LLToast* toast = it->getToast(); - if (toast) - { - if (!notification->canLogToIM() || !notification->hasFormElements()) - { - // only cancel notification if it isn't being used in IM session - LLNotifications::instance().cancel(notification); - } - deleteToast(toast); - } - } - - // Call find() once more, because the mStoredToastList could have been changed - // via notification cancellation and the iterator could have become invalid. - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - if (it != mStoredToastList.end()) - { - mStoredToastList.erase(it); - } - } -} - -void LLScreenChannel::removeToastByNotificationID(LLUUID id) -{ - std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); - while( it != mToastList.end()) - { - deleteToast(it->getToast()); - mToastList.erase(it); - redrawToasts(); - // find next toast with matching id - it = find(mToastList.begin(), mToastList.end(), id); - } - - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - if (it != mStoredToastList.end()) - { - deleteToast(it->getToast()); - mStoredToastList.erase(it); - } -} - - -void LLScreenChannel::killMatchedToasts(const Matcher& matcher) -{ - std::list<const LLToast*> to_delete = findToasts(matcher); - for (std::list<const LLToast*>::iterator it = to_delete.begin(); it - != to_delete.end(); it++) - { - killToastByNotificationID((*it)-> getNotificationID()); - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) -{ - std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); - - LLPanel* panel_to_delete = panel; - - if( it != mToastList.end() && panel) - { - LLToast* toast = it->getToast(); - if (toast) - { - LLPanel* old_panel = toast->getPanel(); - toast->removeChild(old_panel); - panel_to_delete = old_panel; - toast->insertPanel(panel); - toast->startTimer(); - } - redrawToasts(); - } - - delete panel_to_delete; -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::redrawToasts() -{ - if (!getParent()) - { - // connect to floater snap region just to get resize events, we don't care about being a proper widget - mFloaterSnapRegion->addChild(this); - setFollows(FOLLOWS_ALL); - } - - if(mToastList.size() == 0) - return; - - switch(mToastAlignment) - { - case NA_TOP : - showToastsTop(); - break; - - case NA_CENTRE : - showToastsCentre(); - break; - - case NA_BOTTOM : - showToastsBottom(); - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::showToastsBottom() -{ - LLRect toast_rect; - S32 bottom = getRect().mBottom - gFloaterView->getRect().mBottom; - S32 toast_margin = 0; - std::vector<ToastElem>::reverse_iterator it; - - updateRect(); - - LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get()); - - // Use a local variable instead of mToastList. - // mToastList can be modified during recursive calls and then all iteratos will be invalidated. - std::vector<ToastElem> vToastList( mToastList ); - - for(it = vToastList.rbegin(); it != vToastList.rend(); ++it) - { - if(it != vToastList.rbegin()) - { - LLToast* toast = (it-1)->getToast(); - if (!toast) - { - LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL; - return; - } - - bottom = toast->getRect().mTop - toast->getTopPad(); - toast_margin = gSavedSettings.getS32("ToastGap"); - } - - LLToast* toast = it->getToast(); - if(!toast) - { - LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL; - return; - } - - toast_rect = toast->getRect(); - toast_rect.setOriginAndSize(getRect().mRight - toast_rect.getWidth(), - bottom + toast_margin, toast_rect.getWidth(), - toast_rect.getHeight()); - toast->setRect(toast_rect); - - if(floater && floater->overlapsScreenChannel()) - { - if(it == vToastList.rbegin()) - { - // move first toast above docked floater - S32 shift = floater->getRect().getHeight(); - if(floater->getDockControl()) - { - shift += floater->getDockControl()->getTongueHeight(); - } - toast->translate(0, shift); - } - - LLRect channel_rect = getChannelRect(); - // don't show toasts if there is not enough space - if(toast_rect.mTop > channel_rect.mTop) - { - break; - } - } - - bool stop_showing_toasts = toast->getRect().mTop > getRect().mTop; - - if(!stop_showing_toasts) - { - if( it != vToastList.rend()-1) - { - S32 toast_top = toast->getRect().mTop + gSavedSettings.getS32("ToastGap"); - stop_showing_toasts = toast_top > getRect().mTop; - } - } - - // at least one toast should be visible - - if(it == vToastList.rbegin()) - { - stop_showing_toasts = false; - } - - if(stop_showing_toasts) - break; - - if( !toast->getVisible() ) - { - // HACK - // EXT-2653: it is necessary to prevent overlapping for secondary showed toasts - toast->setVisible(true); - } - if(!toast->hasFocus()) - { - // Fixing Z-order of toasts (EXT-4862) - // Next toast will be positioned under this one. - gFloaterView->sendChildToBack(toast); - } - } - - // Dismiss toasts we don't have space for (STORM-391). - if(it != vToastList.rend()) - { - mHiddenToastsNum = 0; - - for(; it != vToastList.rend(); it++) - { - LLToast* toast = it->getToast(); - if (toast) - { - toast->hide(); - } - } - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::showToastsCentre() -{ - LLToast* toast = mToastList[0].getToast(); - if (!toast) - { - LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL; - return; - } - - LLRect toast_rect; - S32 bottom = (getRect().mTop - getRect().mBottom)/2 + toast->getRect().getHeight()/2; - std::vector<ToastElem>::reverse_iterator it; - - for(it = mToastList.rbegin(); it != mToastList.rend(); ++it) - { - LLToast* toast = it->getToast(); - if (!toast) - { - LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL; - return; - } - - toast_rect = toast->getRect(); - toast_rect.setLeftTopAndSize(getRect().mLeft - toast_rect.getWidth() / 2, bottom + toast_rect.getHeight() / 2 + gSavedSettings.getS32("ToastGap"), toast_rect.getWidth() ,toast_rect.getHeight()); - toast->setRect(toast_rect); - - toast->setVisible(true); - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::showToastsTop() -{ - LLRect channel_rect = getChannelRect(); - - LLRect toast_rect; - S32 top = channel_rect.mTop; - std::vector<ToastElem>::reverse_iterator it; - - updateRect(); - - LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get()); - - // Use a local variable instead of mToastList. - // mToastList can be modified during recursive calls and then all iteratos will be invalidated. - std::vector<ToastElem> vToastList( mToastList ); - - for(it = vToastList.rbegin(); it != vToastList.rend(); ++it) - { - if(it != vToastList.rbegin()) - { - LLToast* toast = (it-1)->getToast(); - if (!toast) - { - LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL; - return; - } - - top = toast->getRect().mBottom - toast->getTopPad(); - gSavedSettings.getS32("ToastGap"); - } - - LLToast* toast = it->getToast(); - if (!toast) - { - LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL; - return; - } - - toast_rect = toast->getRect(); - toast_rect.setLeftTopAndSize(channel_rect.mRight - toast_rect.getWidth(), - top, toast_rect.getWidth(), - toast_rect.getHeight()); - toast->setRect(toast_rect); - - if(floater && floater->overlapsScreenChannel()) - { - if(it == vToastList.rbegin()) - { - // move first toast above docked floater - S32 shift = -floater->getRect().getHeight(); - if(floater->getDockControl()) - { - shift -= floater->getDockControl()->getTongueHeight(); - } - toast->translate(0, shift); - } - - LLRect channel_rect = getChannelRect(); - // don't show toasts if there is not enough space - if(toast_rect.mBottom < channel_rect.mBottom) - { - break; - } - } - - bool stop_showing_toasts = toast->getRect().mBottom < channel_rect.mBottom; - - if(!stop_showing_toasts) - { - if( it != vToastList.rend()-1) - { - S32 toast_bottom = toast->getRect().mBottom - gSavedSettings.getS32("ToastGap"); - stop_showing_toasts = toast_bottom < channel_rect.mBottom; - } - } - - // at least one toast should be visible - if(it == vToastList.rbegin()) - { - stop_showing_toasts = false; - } - - if(stop_showing_toasts) - break; - - if (!toast->getVisible()) - { - // HACK - // EXT-2653: it is necessary to prevent overlapping for secondary showed toasts - toast->setVisible(true); - } - if (!toast->hasFocus()) - { - // Fixing Z-order of toasts (EXT-4862) - // Next toast will be positioned under this one. - gFloaterView->sendChildToBack(toast); - } - } - - // Dismiss toasts we don't have space for (STORM-391). - std::vector<LLToast*> toasts_to_hide; - - if(it != vToastList.rend()) - { - mHiddenToastsNum = 0; - - for(; it != vToastList.rend(); it++) - { - LLToast* toast = it->getToast(); - if (toast) - { - toasts_to_hide.push_back(toast); - } - } - } - - for (std::vector<LLToast*>::iterator it = toasts_to_hide.begin(), end_it = toasts_to_hide.end(); - it != end_it; - ++it) - { - (*it)->hide(); - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::createStartUpToast(S32 notif_num, F32 timer) -{ - LLScreenChannelBase::updateRect(); - - LLRect toast_rect; - LLToast::Params p; - p.lifetime_secs = timer; - p.enable_hide_btn = false; - mStartUpToastPanel = new LLToast(p); - - if(!mStartUpToastPanel) - return; - - mStartUpToastPanel->setOnFadeCallback(boost::bind(&LLScreenChannel::onStartUpToastHide, this)); - - LLPanel* wrapper_panel = mStartUpToastPanel->getChild<LLPanel>("wrapper_panel"); - LLTextBox* text_box = mStartUpToastPanel->getChild<LLTextBox>("toast_text"); - - std::string text = LLTrans::getString("StartUpNotifications"); - - toast_rect = mStartUpToastPanel->getRect(); - mStartUpToastPanel->reshape(getRect().getWidth(), toast_rect.getHeight(), true); - - text_box->setValue(text); - text_box->setVisible(true); - - text_box->reshapeToFitText(); - text_box->setOrigin(text_box->getRect().mLeft, (wrapper_panel->getRect().getHeight() - text_box->getRect().getHeight())/2); - - toast_rect.setLeftTopAndSize(0, getRect().getHeight() - gSavedSettings.getS32("ToastGap"), getRect().getWidth(), toast_rect.getHeight()); - mStartUpToastPanel->setRect(toast_rect); - - addChild(mStartUpToastPanel); - - 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) -{ - // *TODO: update string if notifications are arriving while the StartUp toast is on a screen -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::onStartUpToastHide() -{ - onCommit(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::closeStartUpToast() -{ - if(mStartUpToastPanel != NULL) - { - mStartUpToastPanel->setVisible(false); - mStartUpToastPanel = NULL; - } -} - -void LLNotificationsUI::LLScreenChannel::stopToastTimer(LLToast* toast) -{ - if (!toast || toast != mHoveredToast) return; - - // Pause fade timer of the hovered toast. - toast->stopTimer(); -} - -void LLNotificationsUI::LLScreenChannel::startToastTimer(LLToast* toast) -{ - if (!toast || toast == mHoveredToast) - { - return; - } - - // Reset its fade timer. - toast->startTimer(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::hideToastsFromScreen() -{ - for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++) - { - LLToast* toast = it->getToast(); - if (toast) - { - toast->setVisible(false); - } - else - { - LL_WARNS() << "Attempt to hide a deleted toast." << LL_ENDL; - } - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::hideToast(const LLUUID& notification_id) -{ - std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), notification_id); - if(mToastList.end() != it) - { - LLToast* toast = it->getToast(); - if (toast) - { - toast->hide(); - } - else - { - LL_WARNS() << "Attempt to hide a deleted toast." << LL_ENDL; - } - } -} - -void LLScreenChannel::closeHiddenToasts(const Matcher& matcher) -{ - // since we can't guarantee that close toast operation doesn't change mToastList - // we collect matched toasts that should be closed into separate list - std::list<LLToast*> toasts; - for (std::vector<ToastElem>::iterator it = mToastList.begin(); it - != mToastList.end(); it++) - { - LLToast* toast = it->getToast(); - // add to list valid toast that match to provided matcher criteria - if (toast != NULL && !toast->isDead() && toast->getNotification() != NULL - && !toast->getVisible() && matcher.matches(toast->getNotification())) - { - toasts.push_back(toast); - } - } - - // close collected toasts - for (std::list<LLToast*>::iterator it = toasts.begin(); it - != toasts.end(); it++) - { - LLToast* toast = *it; - toast->closeFloater(); - } -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::removeToastsFromChannel() -{ - hideToastsFromScreen(); - for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++) - { - deleteToast(it->getToast()); - } - mToastList.clear(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::removeAndStoreAllStorableToasts() -{ - if(mToastList.size() == 0) - return; - - hideToastsFromScreen(); - for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end();) - { - LLToast* toast = it->getToast(); - if(toast && toast->getCanBeStored()) - { - storeToast(*it); - it = mToastList.erase(it); - } - else - { - ++it; - } - } - redrawToasts(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::removeToastsBySessionID(LLUUID id) -{ - if(mToastList.size() == 0) - return; - - hideToastsFromScreen(); - for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end();) - { - LLToast* toast = it->getToast(); - if(toast && toast->getSessionID() == id) - { - deleteToast(toast); - it = mToastList.erase(it); - } - else - { - ++it; - } - } - redrawToasts(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::onToastHover(LLToast* toast, bool mouse_enter) -{ - // 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 - if (mouse_enter) - { - if (toast->isHovered()) - { - mHoveredToast = toast; - } - } - else if (mHoveredToast != NULL) - { - if (!mHoveredToast->isHovered()) - { - mHoveredToast = NULL; - } - } - - redrawToasts(); -} - -//-------------------------------------------------------------------------- -void LLScreenChannel::updateShowToastsState() -{ - LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get()); - - if(!floater) - { - setShowToasts(true); - return; - } - - updateRect(); -} - -//-------------------------------------------------------------------------- - -LLToast* LLScreenChannel::getToastByNotificationID(LLUUID id) -{ - std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), - mStoredToastList.end(), id); - - if (it == mStoredToastList.end()) - return NULL; - - return it->getToast(); -} +/**
+ * @file llscreenchannel.cpp
+ * @brief Class implements a channel on a screen in which appropriate toasts may appear.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "llviewerprecompiledheaders.h" // must be first include
+
+#include "lliconctrl.h"
+#include "lltextbox.h"
+#include "llscreenchannel.h"
+
+#include "lltoastpanel.h"
+#include "llviewercontrol.h"
+#include "llviewerwindow.h"
+#include "llfloaterreg.h"
+#include "lltrans.h"
+#include "llagent.h"
+#include "lldockablefloater.h"
+#include "llsyswellwindow.h"
+#include "llfloaterimsession.h"
+#include "llscriptfloater.h"
+#include "llrootview.h"
+
+#include <algorithm>
+
+using namespace LLNotificationsUI;
+
+bool LLScreenChannel::mWasStartUpToastShown = false;
+
+LLRect LLScreenChannelBase::getChannelRect()
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ if (mFloaterSnapRegion == NULL)
+ {
+ mFloaterSnapRegion = gViewerWindow->getRootView()->getChildView("floater_snap_region");
+ }
+
+ if (mChicletRegion == NULL)
+ {
+ mChicletRegion = gViewerWindow->getRootView()->getChildView("chiclet_container");
+ }
+
+ LLRect channel_rect;
+ LLRect chiclet_rect;
+
+ mFloaterSnapRegion->localRectToScreen(mFloaterSnapRegion->getLocalRect(), &channel_rect);
+ mChicletRegion->localRectToScreen(mChicletRegion->getLocalRect(), &chiclet_rect);
+
+ channel_rect.mTop = chiclet_rect.mBottom;
+ return channel_rect;
+}
+
+
+//--------------------------------------------------------------------------
+//////////////////////
+// LLScreenChannelBase
+//////////////////////
+
+LLScreenChannelBase::LLScreenChannelBase(const Params& p)
+: LLUICtrl(p),
+ mToastAlignment(p.toast_align),
+ mCanStoreToasts(true),
+ mHiddenToastsNum(0),
+ mHoveredToast(NULL),
+ mControlHovering(false),
+ mShowToasts(true),
+ mID(p.id),
+ mDisplayToastsAlways(p.display_toasts_always),
+ mChannelAlignment(p.channel_align),
+ mFloaterSnapRegion(NULL),
+ mChicletRegion(NULL)
+{
+ mID = p.id;
+
+ setMouseOpaque( false );
+ setVisible(false);
+}
+
+bool LLScreenChannelBase::postBuild()
+{
+ if (mFloaterSnapRegion == NULL)
+ {
+ mFloaterSnapRegion = gViewerWindow->getRootView()->getChildView("floater_snap_region");
+ }
+
+ if (mChicletRegion == NULL)
+ {
+ mChicletRegion = gViewerWindow->getRootView()->getChildView("chiclet_container");
+ }
+
+ return true;
+}
+
+void LLScreenChannelBase::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ if (mChannelAlignment == CA_CENTRE)
+ {
+ // Keep notifications and alerts centered
+ // WorldViewRectScaled is out of date at reshape but Window has same width
+ S32 channel_bound = gViewerWindow->getWindowRectScaled().getWidth() / 2;
+ setRect(LLRect(channel_bound, 0, channel_bound, 0));
+ updateRect(); //sets top and bottom only
+ }
+ redrawToasts();
+}
+
+bool LLScreenChannelBase::isHovering()
+{
+ if (!mHoveredToast)
+ {
+ return false;
+ }
+
+ return mHoveredToast->isHovered();
+}
+
+void LLScreenChannelBase::updatePositionAndSize(LLRect rect)
+{
+ LLRect this_rect = getRect();
+
+ this_rect.mTop = rect.mTop;
+ switch(mChannelAlignment)
+ {
+ case CA_LEFT :
+ break;
+ case CA_CENTRE :
+ this_rect.setCenterAndSize( (rect.getWidth()) / 2, rect.getHeight() / 2, this_rect.getWidth(), this_rect.getHeight());
+ break;
+ case CA_RIGHT :
+ this_rect.setLeftTopAndSize(rect.mRight - this_rect.getWidth(),
+ this_rect.mTop,
+ this_rect.getWidth(),
+ this_rect.getHeight());
+ }
+ setRect(this_rect);
+ redrawToasts();
+
+}
+
+void LLScreenChannelBase::init(S32 channel_left, S32 channel_right)
+{
+ // top and bottom set by updateRect()
+ setRect(LLRect(channel_left, 0, channel_right, 0));
+ updateRect();
+ setVisible(true);
+}
+
+void LLScreenChannelBase::updateRect()
+{
+ const S32 CHANNEL_BOTTOM_PANEL_MARGIN = 35;
+ S32 channel_top = getChannelRect().mTop;
+ S32 channel_bottom = getChannelRect().mBottom + CHANNEL_BOTTOM_PANEL_MARGIN;
+ S32 channel_left = getRect().mLeft;
+ S32 channel_right = getRect().mRight;
+ setRect(LLRect(channel_left, channel_top, channel_right, channel_bottom));
+}
+
+//--------------------------------------------------------------------------
+//////////////////////
+// LLScreenChannel
+//////////////////////
+//--------------------------------------------------------------------------
+LLScreenChannel::LLScreenChannel(const Params& p)
+: LLScreenChannelBase(p),
+ mStartUpToastPanel(NULL)
+{
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::init(S32 channel_left, S32 channel_right)
+{
+ LLScreenChannelBase::init(channel_left, channel_right);
+ LLRect channel_rect = getChannelRect();
+ updatePositionAndSize(channel_rect);
+}
+
+//--------------------------------------------------------------------------
+LLScreenChannel::~LLScreenChannel()
+{
+
+}
+
+std::list<const LLToast*> LLScreenChannel::findToasts(const Matcher& matcher)
+{
+ std::list<const LLToast*> res;
+
+ // collect stored toasts
+ for (std::vector<ToastElem>::iterator it = mStoredToastList.begin(); it
+ != mStoredToastList.end(); it++)
+ {
+ const LLToast* toast = it->getToast();
+ if (toast && matcher.matches(toast->getNotification()))
+ {
+ res.push_back(toast);
+ }
+ }
+
+ // collect displayed toasts
+ for (std::vector<ToastElem>::iterator it = mToastList.begin(); it
+ != mToastList.end(); it++)
+ {
+ const LLToast* toast = it->getToast();
+ if (toast && matcher.matches(toast->getNotification()))
+ {
+ res.push_back(toast);
+ }
+ }
+
+ return res;
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::updatePositionAndSize(LLRect new_world_rect)
+{
+ LLRect this_rect = getRect();
+
+ switch(mChannelAlignment)
+ {
+ case CA_LEFT :
+ this_rect.mTop = (S32) (new_world_rect.getHeight() * getHeightRatio());
+ break;
+ case CA_CENTRE :
+ LLScreenChannelBase::updatePositionAndSize(new_world_rect);
+ return;
+ case CA_RIGHT :
+ this_rect.mTop = (S32) (new_world_rect.getHeight() * getHeightRatio());
+ this_rect.setLeftTopAndSize(new_world_rect.mRight - this_rect.getWidth(),
+ this_rect.mTop,
+ this_rect.getWidth(),
+ this_rect.getHeight());
+ }
+ setRect(this_rect);
+ redrawToasts();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::addToast(const LLToast::Params& p)
+{
+ LL_PROFILE_ZONE_SCOPED
+ bool store_toast = false, show_toast = false;
+
+ if (mDisplayToastsAlways)
+ {
+ show_toast = true;
+ }
+ else
+ {
+ show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show);
+ }
+ store_toast = !show_toast && p.can_be_stored && mCanStoreToasts;
+
+ if(!show_toast && !store_toast)
+ {
+ if(gAgent.isDoNotDisturb())
+ {
+ return;
+ }
+ LLNotificationPtr notification = LLNotifications::instance().find(p.notif_id);
+
+ if (notification &&
+ (!notification->canLogToIM() || !notification->hasFormElements()))
+ {
+ // only cancel notification if it isn't being used in IM session
+ LLNotifications::instance().cancel(notification);
+ }
+
+ // It was assumed that the toast would take ownership of the panel pointer.
+ // But since we have decided not to display the toast, kill the panel to
+ // prevent the memory leak.
+ if (p.panel != NULL)
+ {
+ p.panel()->die();
+ }
+ return;
+ }
+
+ LLToast* toast = new LLToast(p);
+ ToastElem new_toast_elem(toast->getHandle());
+
+ toast->setOnFadeCallback(boost::bind(&LLScreenChannel::onToastFade, this, _1));
+ toast->setOnToastDestroyedCallback(boost::bind(&LLScreenChannel::onToastDestroyed, this, _1));
+ if(mControlHovering)
+ {
+ toast->setOnToastHoverCallback(boost::bind(&LLScreenChannel::onToastHover, this, _1, _2));
+ toast->setMouseEnterCallback(boost::bind(&LLScreenChannel::stopToastTimer, this, toast));
+ toast->setMouseLeaveCallback(boost::bind(&LLScreenChannel::startToastTimer, this, toast));
+ }
+
+ if(show_toast)
+ {
+ mToastList.push_back(new_toast_elem);
+ if(p.can_be_stored)
+ {
+ // store toasts immediately - EXT-3762
+ storeToast(new_toast_elem);
+ }
+ updateShowToastsState();
+ redrawToasts();
+ }
+ else // store_toast
+ {
+ mHiddenToastsNum++;
+ storeToast(new_toast_elem);
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::onToastDestroyed(LLToast* toast)
+{
+ std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), static_cast<LLPanel*>(toast));
+
+ if(it != mToastList.end())
+ {
+ mToastList.erase(it);
+ }
+
+ it = find(mStoredToastList.begin(), mStoredToastList.end(), static_cast<LLPanel*>(toast));
+
+ if(it != mStoredToastList.end())
+ {
+ mStoredToastList.erase(it);
+ }
+
+ // if destroyed toast is hovered - reset hovered
+ if (mHoveredToast == toast)
+ {
+ mHoveredToast = NULL;
+ }
+}
+
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::onToastFade(LLToast* toast)
+{
+ std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), static_cast<LLPanel*>(toast));
+
+ if(it != mToastList.end())
+ {
+ bool delete_toast = !mCanStoreToasts || !toast->getCanBeStored();
+ if(delete_toast)
+ {
+ mToastList.erase(it);
+ deleteToast(toast);
+ }
+ else
+ {
+ storeToast((*it));
+ mToastList.erase(it);
+ }
+
+ redrawToasts();
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::deleteToast(LLToast* toast)
+{
+ if (!toast || toast->isDead())
+ {
+ return;
+ }
+
+ // send signal to observers about destroying of a toast
+ toast->closeToast();
+
+ // update channel's Hovering state
+ // turning hovering off manually because onMouseLeave won't happen if a toast was closed using a keyboard
+ if(mHoveredToast == toast)
+ {
+ mHoveredToast = NULL;
+ }
+}
+
+//--------------------------------------------------------------------------
+
+void LLScreenChannel::storeToast(ToastElem& toast_elem)
+{
+ // do not store clones
+ std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), toast_elem.getID());
+ if( it != mStoredToastList.end() )
+ return;
+
+ const LLToast* toast = toast_elem.getToast();
+ if (toast)
+ {
+ mStoredToastList.push_back(toast_elem);
+ mOnStoreToast(toast->getPanel(), toast->getNotificationID());
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::loadStoredToastsToChannel()
+{
+ std::vector<ToastElem>::iterator it;
+
+ if(mStoredToastList.size() == 0)
+ return;
+
+ for(it = mStoredToastList.begin(); it != mStoredToastList.end(); ++it)
+ {
+ LLToast* toast = it->getToast();
+ if (toast)
+ {
+ toast->setIsHidden(false);
+ toast->startTimer();
+ mToastList.push_back(*it);
+ }
+ }
+
+ mStoredToastList.clear();
+ redrawToasts();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id)
+{
+ std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id);
+
+ if( it == mStoredToastList.end() )
+ return;
+
+ LLToast* toast = it->getToast();
+ if (toast)
+ {
+ if(toast->getVisible())
+ {
+ // toast is already in channel
+ return;
+ }
+
+ toast->setIsHidden(false);
+ toast->startTimer();
+ mToastList.push_back(*it);
+ }
+
+ redrawToasts();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::killToastByNotificationID(LLUUID id)
+{
+ // searching among toasts on a screen
+ std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id);
+ LLNotificationPtr notification = LLNotifications::instance().find(id);
+ if (!notification) return;
+
+ if( it != mToastList.end())
+ {
+ LLToast* toast = it->getToast();
+ // if it is a notification toast and notification is UnResponded - then respond on it
+ // else - simply destroy a toast
+ //
+ // NOTE: if a notification is unresponded this function will be called twice for the same toast.
+ // At first, the notification will be discarded, at second (it will be caused by discarding),
+ // the toast will be destroyed.
+ if(toast && toast->isNotificationValid())
+ {
+ if (!notification->canLogToIM() || !notification->hasFormElements())
+ {
+ // only cancel notification if it isn't being used in IM session
+ LLNotifications::instance().cancel(notification);
+ }
+ }
+ else
+ {
+ removeToastByNotificationID(id);
+ }
+ }
+ else
+ {
+ // searching among stored toasts
+ it = find(mStoredToastList.begin(), mStoredToastList.end(), id);
+
+ if( it != mStoredToastList.end() )
+ {
+ LLToast* toast = it->getToast();
+ if (toast)
+ {
+ if (!notification->canLogToIM() || !notification->hasFormElements())
+ {
+ // only cancel notification if it isn't being used in IM session
+ LLNotifications::instance().cancel(notification);
+ }
+ deleteToast(toast);
+ }
+ }
+
+ // Call find() once more, because the mStoredToastList could have been changed
+ // via notification cancellation and the iterator could have become invalid.
+ it = find(mStoredToastList.begin(), mStoredToastList.end(), id);
+ if (it != mStoredToastList.end())
+ {
+ mStoredToastList.erase(it);
+ }
+ }
+}
+
+void LLScreenChannel::removeToastByNotificationID(LLUUID id)
+{
+ std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id);
+ while( it != mToastList.end())
+ {
+ deleteToast(it->getToast());
+ mToastList.erase(it);
+ redrawToasts();
+ // find next toast with matching id
+ it = find(mToastList.begin(), mToastList.end(), id);
+ }
+
+ it = find(mStoredToastList.begin(), mStoredToastList.end(), id);
+ if (it != mStoredToastList.end())
+ {
+ deleteToast(it->getToast());
+ mStoredToastList.erase(it);
+ }
+}
+
+
+void LLScreenChannel::killMatchedToasts(const Matcher& matcher)
+{
+ std::list<const LLToast*> to_delete = findToasts(matcher);
+ for (std::list<const LLToast*>::iterator it = to_delete.begin(); it
+ != to_delete.end(); it++)
+ {
+ killToastByNotificationID((*it)-> getNotificationID());
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel)
+{
+ std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id);
+
+ LLPanel* panel_to_delete = panel;
+
+ if( it != mToastList.end() && panel)
+ {
+ LLToast* toast = it->getToast();
+ if (toast)
+ {
+ LLPanel* old_panel = toast->getPanel();
+ toast->removeChild(old_panel);
+ panel_to_delete = old_panel;
+ toast->insertPanel(panel);
+ toast->startTimer();
+ }
+ redrawToasts();
+ }
+
+ delete panel_to_delete;
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::redrawToasts()
+{
+ if (!getParent())
+ {
+ // connect to floater snap region just to get resize events, we don't care about being a proper widget
+ mFloaterSnapRegion->addChild(this);
+ setFollows(FOLLOWS_ALL);
+ }
+
+ if(mToastList.size() == 0)
+ return;
+
+ switch(mToastAlignment)
+ {
+ case NA_TOP :
+ showToastsTop();
+ break;
+
+ case NA_CENTRE :
+ showToastsCentre();
+ break;
+
+ case NA_BOTTOM :
+ showToastsBottom();
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::showToastsBottom()
+{
+ LLRect toast_rect;
+ S32 bottom = getRect().mBottom - gFloaterView->getRect().mBottom;
+ S32 toast_margin = 0;
+ std::vector<ToastElem>::reverse_iterator it;
+
+ updateRect();
+
+ LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get());
+
+ // Use a local variable instead of mToastList.
+ // mToastList can be modified during recursive calls and then all iteratos will be invalidated.
+ std::vector<ToastElem> vToastList( mToastList );
+
+ for(it = vToastList.rbegin(); it != vToastList.rend(); ++it)
+ {
+ if(it != vToastList.rbegin())
+ {
+ LLToast* toast = (it-1)->getToast();
+ if (!toast)
+ {
+ LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL;
+ return;
+ }
+
+ bottom = toast->getRect().mTop - toast->getTopPad();
+ toast_margin = gSavedSettings.getS32("ToastGap");
+ }
+
+ LLToast* toast = it->getToast();
+ if(!toast)
+ {
+ LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL;
+ return;
+ }
+
+ toast_rect = toast->getRect();
+ toast_rect.setOriginAndSize(getRect().mRight - toast_rect.getWidth(),
+ bottom + toast_margin, toast_rect.getWidth(),
+ toast_rect.getHeight());
+ toast->setRect(toast_rect);
+
+ if(floater && floater->overlapsScreenChannel())
+ {
+ if(it == vToastList.rbegin())
+ {
+ // move first toast above docked floater
+ S32 shift = floater->getRect().getHeight();
+ if(floater->getDockControl())
+ {
+ shift += floater->getDockControl()->getTongueHeight();
+ }
+ toast->translate(0, shift);
+ }
+
+ LLRect channel_rect = getChannelRect();
+ // don't show toasts if there is not enough space
+ if(toast_rect.mTop > channel_rect.mTop)
+ {
+ break;
+ }
+ }
+
+ bool stop_showing_toasts = toast->getRect().mTop > getRect().mTop;
+
+ if(!stop_showing_toasts)
+ {
+ if( it != vToastList.rend()-1)
+ {
+ S32 toast_top = toast->getRect().mTop + gSavedSettings.getS32("ToastGap");
+ stop_showing_toasts = toast_top > getRect().mTop;
+ }
+ }
+
+ // at least one toast should be visible
+
+ if(it == vToastList.rbegin())
+ {
+ stop_showing_toasts = false;
+ }
+
+ if(stop_showing_toasts)
+ break;
+
+ if( !toast->getVisible() )
+ {
+ // HACK
+ // EXT-2653: it is necessary to prevent overlapping for secondary showed toasts
+ toast->setVisible(true);
+ }
+ if(!toast->hasFocus())
+ {
+ // Fixing Z-order of toasts (EXT-4862)
+ // Next toast will be positioned under this one.
+ gFloaterView->sendChildToBack(toast);
+ }
+ }
+
+ // Dismiss toasts we don't have space for (STORM-391).
+ if(it != vToastList.rend())
+ {
+ mHiddenToastsNum = 0;
+
+ for(; it != vToastList.rend(); it++)
+ {
+ LLToast* toast = it->getToast();
+ if (toast)
+ {
+ toast->hide();
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::showToastsCentre()
+{
+ LLToast* toast = mToastList[0].getToast();
+ if (!toast)
+ {
+ LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL;
+ return;
+ }
+
+ LLRect toast_rect;
+ S32 bottom = (getRect().mTop - getRect().mBottom)/2 + toast->getRect().getHeight()/2;
+ std::vector<ToastElem>::reverse_iterator it;
+
+ for(it = mToastList.rbegin(); it != mToastList.rend(); ++it)
+ {
+ LLToast* toast = it->getToast();
+ if (!toast)
+ {
+ LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL;
+ return;
+ }
+
+ toast_rect = toast->getRect();
+ toast_rect.setLeftTopAndSize(getRect().mLeft - toast_rect.getWidth() / 2, bottom + toast_rect.getHeight() / 2 + gSavedSettings.getS32("ToastGap"), toast_rect.getWidth() ,toast_rect.getHeight());
+ toast->setRect(toast_rect);
+
+ toast->setVisible(true);
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::showToastsTop()
+{
+ LLRect channel_rect = getChannelRect();
+
+ LLRect toast_rect;
+ S32 top = channel_rect.mTop;
+ std::vector<ToastElem>::reverse_iterator it;
+
+ updateRect();
+
+ LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get());
+
+ // Use a local variable instead of mToastList.
+ // mToastList can be modified during recursive calls and then all iteratos will be invalidated.
+ std::vector<ToastElem> vToastList( mToastList );
+
+ for(it = vToastList.rbegin(); it != vToastList.rend(); ++it)
+ {
+ if(it != vToastList.rbegin())
+ {
+ LLToast* toast = (it-1)->getToast();
+ if (!toast)
+ {
+ LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL;
+ return;
+ }
+
+ top = toast->getRect().mBottom - toast->getTopPad();
+ gSavedSettings.getS32("ToastGap");
+ }
+
+ LLToast* toast = it->getToast();
+ if (!toast)
+ {
+ LL_WARNS() << "Attempt to display a deleted toast." << LL_ENDL;
+ return;
+ }
+
+ toast_rect = toast->getRect();
+ toast_rect.setLeftTopAndSize(channel_rect.mRight - toast_rect.getWidth(),
+ top, toast_rect.getWidth(),
+ toast_rect.getHeight());
+ toast->setRect(toast_rect);
+
+ if(floater && floater->overlapsScreenChannel())
+ {
+ if(it == vToastList.rbegin())
+ {
+ // move first toast above docked floater
+ S32 shift = -floater->getRect().getHeight();
+ if(floater->getDockControl())
+ {
+ shift -= floater->getDockControl()->getTongueHeight();
+ }
+ toast->translate(0, shift);
+ }
+
+ LLRect channel_rect = getChannelRect();
+ // don't show toasts if there is not enough space
+ if(toast_rect.mBottom < channel_rect.mBottom)
+ {
+ break;
+ }
+ }
+
+ bool stop_showing_toasts = toast->getRect().mBottom < channel_rect.mBottom;
+
+ if(!stop_showing_toasts)
+ {
+ if( it != vToastList.rend()-1)
+ {
+ S32 toast_bottom = toast->getRect().mBottom - gSavedSettings.getS32("ToastGap");
+ stop_showing_toasts = toast_bottom < channel_rect.mBottom;
+ }
+ }
+
+ // at least one toast should be visible
+ if(it == vToastList.rbegin())
+ {
+ stop_showing_toasts = false;
+ }
+
+ if(stop_showing_toasts)
+ break;
+
+ if (!toast->getVisible())
+ {
+ // HACK
+ // EXT-2653: it is necessary to prevent overlapping for secondary showed toasts
+ toast->setVisible(true);
+ }
+ if (!toast->hasFocus())
+ {
+ // Fixing Z-order of toasts (EXT-4862)
+ // Next toast will be positioned under this one.
+ gFloaterView->sendChildToBack(toast);
+ }
+ }
+
+ // Dismiss toasts we don't have space for (STORM-391).
+ std::vector<LLToast*> toasts_to_hide;
+
+ if(it != vToastList.rend())
+ {
+ mHiddenToastsNum = 0;
+
+ for(; it != vToastList.rend(); it++)
+ {
+ LLToast* toast = it->getToast();
+ if (toast)
+ {
+ toasts_to_hide.push_back(toast);
+ }
+ }
+ }
+
+ for (std::vector<LLToast*>::iterator it = toasts_to_hide.begin(), end_it = toasts_to_hide.end();
+ it != end_it;
+ ++it)
+ {
+ (*it)->hide();
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::createStartUpToast(S32 notif_num, F32 timer)
+{
+ LLScreenChannelBase::updateRect();
+
+ LLRect toast_rect;
+ LLToast::Params p;
+ p.lifetime_secs = timer;
+ p.enable_hide_btn = false;
+ mStartUpToastPanel = new LLToast(p);
+
+ if(!mStartUpToastPanel)
+ return;
+
+ mStartUpToastPanel->setOnFadeCallback(boost::bind(&LLScreenChannel::onStartUpToastHide, this));
+
+ LLPanel* wrapper_panel = mStartUpToastPanel->getChild<LLPanel>("wrapper_panel");
+ LLTextBox* text_box = mStartUpToastPanel->getChild<LLTextBox>("toast_text");
+
+ std::string text = LLTrans::getString("StartUpNotifications");
+
+ toast_rect = mStartUpToastPanel->getRect();
+ mStartUpToastPanel->reshape(getRect().getWidth(), toast_rect.getHeight(), true);
+
+ text_box->setValue(text);
+ text_box->setVisible(true);
+
+ text_box->reshapeToFitText();
+ text_box->setOrigin(text_box->getRect().mLeft, (wrapper_panel->getRect().getHeight() - text_box->getRect().getHeight())/2);
+
+ toast_rect.setLeftTopAndSize(0, getRect().getHeight() - gSavedSettings.getS32("ToastGap"), getRect().getWidth(), toast_rect.getHeight());
+ mStartUpToastPanel->setRect(toast_rect);
+
+ addChild(mStartUpToastPanel);
+
+ 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)
+{
+ // *TODO: update string if notifications are arriving while the StartUp toast is on a screen
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::onStartUpToastHide()
+{
+ onCommit();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::closeStartUpToast()
+{
+ if(mStartUpToastPanel != NULL)
+ {
+ mStartUpToastPanel->setVisible(false);
+ mStartUpToastPanel = NULL;
+ }
+}
+
+void LLNotificationsUI::LLScreenChannel::stopToastTimer(LLToast* toast)
+{
+ if (!toast || toast != mHoveredToast) return;
+
+ // Pause fade timer of the hovered toast.
+ toast->stopTimer();
+}
+
+void LLNotificationsUI::LLScreenChannel::startToastTimer(LLToast* toast)
+{
+ if (!toast || toast == mHoveredToast)
+ {
+ return;
+ }
+
+ // Reset its fade timer.
+ toast->startTimer();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::hideToastsFromScreen()
+{
+ for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++)
+ {
+ LLToast* toast = it->getToast();
+ if (toast)
+ {
+ toast->setVisible(false);
+ }
+ else
+ {
+ LL_WARNS() << "Attempt to hide a deleted toast." << LL_ENDL;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::hideToast(const LLUUID& notification_id)
+{
+ std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), notification_id);
+ if(mToastList.end() != it)
+ {
+ LLToast* toast = it->getToast();
+ if (toast)
+ {
+ toast->hide();
+ }
+ else
+ {
+ LL_WARNS() << "Attempt to hide a deleted toast." << LL_ENDL;
+ }
+ }
+}
+
+void LLScreenChannel::closeHiddenToasts(const Matcher& matcher)
+{
+ // since we can't guarantee that close toast operation doesn't change mToastList
+ // we collect matched toasts that should be closed into separate list
+ std::list<LLToast*> toasts;
+ for (std::vector<ToastElem>::iterator it = mToastList.begin(); it
+ != mToastList.end(); it++)
+ {
+ LLToast* toast = it->getToast();
+ // add to list valid toast that match to provided matcher criteria
+ if (toast != NULL && !toast->isDead() && toast->getNotification() != NULL
+ && !toast->getVisible() && matcher.matches(toast->getNotification()))
+ {
+ toasts.push_back(toast);
+ }
+ }
+
+ // close collected toasts
+ for (std::list<LLToast*>::iterator it = toasts.begin(); it
+ != toasts.end(); it++)
+ {
+ LLToast* toast = *it;
+ toast->closeFloater();
+ }
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::removeToastsFromChannel()
+{
+ hideToastsFromScreen();
+ for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++)
+ {
+ deleteToast(it->getToast());
+ }
+ mToastList.clear();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::removeAndStoreAllStorableToasts()
+{
+ if(mToastList.size() == 0)
+ return;
+
+ hideToastsFromScreen();
+ for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end();)
+ {
+ LLToast* toast = it->getToast();
+ if(toast && toast->getCanBeStored())
+ {
+ storeToast(*it);
+ it = mToastList.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ redrawToasts();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::removeToastsBySessionID(LLUUID id)
+{
+ if(mToastList.size() == 0)
+ return;
+
+ hideToastsFromScreen();
+ for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end();)
+ {
+ LLToast* toast = it->getToast();
+ if(toast && toast->getSessionID() == id)
+ {
+ deleteToast(toast);
+ it = mToastList.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ redrawToasts();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::onToastHover(LLToast* toast, bool mouse_enter)
+{
+ // 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
+ if (mouse_enter)
+ {
+ if (toast->isHovered())
+ {
+ mHoveredToast = toast;
+ }
+ }
+ else if (mHoveredToast != NULL)
+ {
+ if (!mHoveredToast->isHovered())
+ {
+ mHoveredToast = NULL;
+ }
+ }
+
+ redrawToasts();
+}
+
+//--------------------------------------------------------------------------
+void LLScreenChannel::updateShowToastsState()
+{
+ LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get());
+
+ if(!floater)
+ {
+ setShowToasts(true);
+ return;
+ }
+
+ updateRect();
+}
+
+//--------------------------------------------------------------------------
+
+LLToast* LLScreenChannel::getToastByNotificationID(LLUUID id)
+{
+ std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(),
+ mStoredToastList.end(), id);
+
+ if (it == mStoredToastList.end())
+ return NULL;
+
+ return it->getToast();
+}
|