/** * @file lltoast.cpp * @brief This class implements a placeholder for any notification panel. * * $LicenseInfo:firstyear=2000&license=viewergpl$ * * Copyright (c) 2000-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" // must be first include #include "lltoast.h" #include "llbutton.h" #include "llfocusmgr.h" #include "llnotifications.h" #include "llviewercontrol.h" using namespace LLNotificationsUI; /*virtual*/ BOOL LLToastLifeTimer::tick() { if (mEventTimer.hasExpired()) { mToast->expire(); } return FALSE; } //-------------------------------------------------------------------------- LLToast::Params::Params() : can_fade("can_fade", true), can_be_stored("can_be_stored", true), is_modal("is_modal", false), is_tip("is_tip", false), enable_hide_btn("enable_hide_btn", true), force_show("force_show", false), force_store("force_store", false), fading_time_secs("fading_time_secs", gSavedSettings.getS32("ToastFadingTime")), lifetime_secs("lifetime_secs", gSavedSettings.getS32("NotificationToastLifeTime")) {}; LLToast::LLToast(const LLToast::Params& p) : LLModalDialog(LLSD(), p.is_modal), mPanel(p.panel), mToastLifetime(p.lifetime_secs), mToastFadingTime(p.fading_time_secs), mNotificationID(p.notif_id), mSessionID(p.session_id), mCanFade(p.can_fade), mCanBeStored(p.can_be_stored), mHideBtnEnabled(p.enable_hide_btn), mHideBtn(NULL), mNotification(p.notification), mIsHidden(false), mHideBtnPressed(false), mIsTip(p.is_tip), mWrapperPanel(NULL) { mTimer.reset(new LLToastLifeTimer(this, p.lifetime_secs)); LLUICtrlFactory::getInstance()->buildFloater(this, "panel_toast.xml", NULL); setCanDrag(FALSE); mWrapperPanel = getChild("wrapper_panel"); mWrapperPanel->setMouseEnterCallback(boost::bind(&LLToast::onToastMouseEnter, this)); mWrapperPanel->setMouseLeaveCallback(boost::bind(&LLToast::onToastMouseLeave, this)); if(mPanel) { insertPanel(mPanel); } if(mHideBtnEnabled) { mHideBtn = getChild("hide_btn"); mHideBtn->setClickedCallback(boost::bind(&LLToast::hide,this)); mHideBtn->setMouseEnterCallback(boost::bind(&LLToast::onToastMouseEnter, this)); mHideBtn->setMouseLeaveCallback(boost::bind(&LLToast::onToastMouseLeave, this)); } // init callbacks if present if(!p.on_delete_toast().empty()) mOnDeleteToastSignal.connect(p.on_delete_toast()); if(!p.on_mouse_enter().empty()) mOnMouseEnterSignal.connect(p.on_mouse_enter()); } void LLToast::reshape(S32 width, S32 height, BOOL called_from_parent) { // We shouldn't use reshape from LLModalDialog since it changes toasts position. // Toasts position should be controlled only by toast screen channel, see LLScreenChannelBase. // see EXT-8044 LLFloater::reshape(width, height, called_from_parent); } //-------------------------------------------------------------------------- BOOL LLToast::postBuild() { if(!mCanFade) { mTimer->stop(); } return TRUE; } //-------------------------------------------------------------------------- void LLToast::setHideButtonEnabled(bool enabled) { if(mHideBtn) mHideBtn->setEnabled(enabled); } //-------------------------------------------------------------------------- LLToast::~LLToast() { mOnToastDestroyedSignal(this); } //-------------------------------------------------------------------------- void LLToast::hide() { setVisible(FALSE); mTimer->stop(); mIsHidden = true; mOnFadeSignal(this); } void LLToast::onFocusLost() { if(mWrapperPanel && !isBackgroundVisible()) { // Lets make wrapper panel behave like a floater setBackgroundOpaque(FALSE); } } void LLToast::onFocusReceived() { if(mWrapperPanel && !isBackgroundVisible()) { // Lets make wrapper panel behave like a floater setBackgroundOpaque(TRUE); } } S32 LLToast::getTopPad() { if(mWrapperPanel) { return getRect().getHeight() - mWrapperPanel->getRect().getHeight(); } return 0; } S32 LLToast::getRightPad() { if(mWrapperPanel) { return getRect().getWidth() - mWrapperPanel->getRect().getWidth(); } return 0; } //-------------------------------------------------------------------------- void LLToast::setCanFade(bool can_fade) { mCanFade = can_fade; if(!mCanFade) mTimer->stop(); } //-------------------------------------------------------------------------- void LLToast::expire() { // if toast has fade property - hide it if(mCanFade) { hide(); } } //-------------------------------------------------------------------------- void LLToast::reshapeToPanel() { LLPanel* panel = getPanel(); if(!panel) return; LLRect panel_rect = panel->getRect(); panel_rect.setLeftTopAndSize(0, panel_rect.getHeight(), panel_rect.getWidth(), panel_rect.getHeight()); panel->setShape(panel_rect); LLRect toast_rect = getRect(); toast_rect.setLeftTopAndSize(toast_rect.mLeft, toast_rect.mTop, panel_rect.getWidth() + getRightPad(), panel_rect.getHeight() + getTopPad()); setShape(toast_rect); } void LLToast::insertPanel(LLPanel* panel) { mWrapperPanel->addChild(panel); reshapeToPanel(); } //-------------------------------------------------------------------------- void LLToast::draw() { LLFloater::draw(); if(!isBackgroundVisible()) { // Floater background is invisible, lets make wrapper panel look like a // floater - draw shadow. drawShadow(mWrapperPanel); // Shadow will probably overlap close button, lets redraw the button if(mHideBtn) { drawChild(mHideBtn); } } // if timer started and remaining time <= fading time if (mTimer->getStarted() && (mToastLifetime - mTimer->getEventTimer().getElapsedTimeF32()) <= mToastFadingTime) { setBackgroundOpaque(FALSE); } } //-------------------------------------------------------------------------- void LLToast::setVisible(BOOL show) { if(mIsHidden) { // this toast is invisible after fade until its ScreenChannel will allow it // // (EXT-1849) according to this bug a toast can be resurrected from // invisible state if it faded during a teleportation // then it fades a second time and causes a crash return; } if(show) { setBackgroundOpaque(TRUE); if(!mTimer->getStarted() && mCanFade) { mTimer->start(); } if (!getVisible()) { LLModalDialog::setFrontmost(FALSE); } } else { //hide "hide" button in case toast was hidden without mouse_leave if(mHideBtn) mHideBtn->setVisible(show); } LLFloater::setVisible(show); if(mPanel) { if(!mPanel->isDead()) { mPanel->setVisible(show); } } } void LLToast::onToastMouseEnter() { LLRect panel_rc = mWrapperPanel->calcScreenRect(); LLRect button_rc; if(mHideBtn) { button_rc = mHideBtn->calcScreenRect(); } S32 x, y; LLUI::getMousePositionScreen(&x, &y); if(panel_rc.pointInRect(x, y) || button_rc.pointInRect(x, y)) { mOnToastHoverSignal(this, MOUSE_ENTER); setBackgroundOpaque(TRUE); //toasts fading is management by Screen Channel sendChildToFront(mHideBtn); if(mHideBtn && mHideBtn->getEnabled()) { mHideBtn->setVisible(TRUE); } mOnMouseEnterSignal(this); mToastMouseEnterSignal(this, getValue()); } } void LLToast::onToastMouseLeave() { LLRect panel_rc = mWrapperPanel->calcScreenRect(); LLRect button_rc; if(mHideBtn) { button_rc = mHideBtn->calcScreenRect(); } S32 x, y; LLUI::getMousePositionScreen(&x, &y); if( !panel_rc.pointInRect(x, y) && !button_rc.pointInRect(x, y)) { mOnToastHoverSignal(this, MOUSE_LEAVE); //toasts fading is management by Screen Channel if(mHideBtn && mHideBtn->getEnabled()) { if( mHideBtnPressed ) { mHideBtnPressed = false; return; } mHideBtn->setVisible(FALSE); } mToastMouseLeaveSignal(this, getValue()); } } void LLToast::setBackgroundOpaque(BOOL b) { if(mWrapperPanel && !isBackgroundVisible()) { mWrapperPanel->setBackgroundOpaque(b); } else { LLModalDialog::setBackgroundOpaque(b); } } void LLNotificationsUI::LLToast::stopFading() { if(mCanFade) { stopTimer(); } } void LLNotificationsUI::LLToast::startFading() { if(mCanFade) { resetTimer(); } } bool LLToast::isHovered() { S32 x, y; LLUI::getMousePositionScreen(&x, &y); return mWrapperPanel->calcScreenRect().pointInRect(x, y); } //-------------------------------------------------------------------------- BOOL LLToast::handleMouseDown(S32 x, S32 y, MASK mask) { if(mHideBtn && mHideBtn->getEnabled()) { mHideBtnPressed = mHideBtn->getRect().pointInRect(x, y); } return LLFloater::handleMouseDown(x, y, mask); } //-------------------------------------------------------------------------- bool LLToast::isNotificationValid() { if(mNotification) { return !mNotification->isCancelled(); } return false; } //-------------------------------------------------------------------------- S32 LLToast::notifyParent(const LLSD& info) { if (info.has("action") && "hide_toast" == info["action"].asString()) { hide(); return 1; } return LLModalDialog::notifyParent(info); }