PageRenderTime 49ms CodeModel.GetById 15ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/lltoast.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 573 lines | 418 code | 83 blank | 72 comment | 66 complexity | 746b2aa7a5cc0fcbca54cc92c74b34e8 MD5 | raw file
  1/** 
  2 * @file lltoast.cpp
  3 * @brief This class implements a placeholder for any notification panel.
  4 *
  5 * $LicenseInfo:firstyear=2000&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h" // must be first include
 28
 29#include "lltoast.h"
 30
 31#include "llbutton.h"
 32#include "llfocusmgr.h"
 33#include "llnotifications.h"
 34#include "llviewercontrol.h"
 35
 36using namespace LLNotificationsUI;
 37
 38//--------------------------------------------------------------------------
 39LLToastLifeTimer::LLToastLifeTimer(LLToast* toast, F32 period)
 40	: mToast(toast),
 41	  LLEventTimer(period)
 42{
 43}
 44
 45/*virtual*/
 46BOOL LLToastLifeTimer::tick()
 47{
 48	if (mEventTimer.hasExpired())
 49	{
 50		mToast->expire();
 51	}
 52	return FALSE;
 53}
 54
 55void LLToastLifeTimer::stop()
 56{
 57	mEventTimer.stop();
 58}
 59
 60void LLToastLifeTimer::start()
 61{
 62	mEventTimer.start();
 63}
 64
 65void LLToastLifeTimer::restart()
 66{
 67	mEventTimer.reset();
 68}
 69
 70BOOL LLToastLifeTimer::getStarted()
 71{
 72	return mEventTimer.getStarted();
 73}
 74
 75void LLToastLifeTimer::setPeriod(F32 period)
 76{
 77	mPeriod = period;
 78}
 79
 80F32 LLToastLifeTimer::getRemainingTimeF32()
 81{
 82	F32 et = mEventTimer.getElapsedTimeF32();
 83	if (!getStarted() || et > mPeriod) return 0.0f;
 84	return mPeriod - et;
 85}
 86
 87//--------------------------------------------------------------------------
 88LLToast::Params::Params() 
 89:	can_fade("can_fade", true),
 90	can_be_stored("can_be_stored", true),
 91	is_modal("is_modal", false),
 92	is_tip("is_tip", false),
 93	enable_hide_btn("enable_hide_btn", true),
 94	force_show("force_show", false),
 95	force_store("force_store", false),
 96	fading_time_secs("fading_time_secs", gSavedSettings.getS32("ToastFadingTime")),
 97	lifetime_secs("lifetime_secs", gSavedSettings.getS32("NotificationToastLifeTime"))
 98{};
 99
100LLToast::LLToast(const LLToast::Params& p) 
101:	LLModalDialog(LLSD(), p.is_modal),
102	mToastLifetime(p.lifetime_secs),
103	mToastFadingTime(p.fading_time_secs),
104	mNotificationID(p.notif_id),  
105	mSessionID(p.session_id),
106	mCanFade(p.can_fade),
107	mCanBeStored(p.can_be_stored),
108	mHideBtnEnabled(p.enable_hide_btn),
109	mHideBtn(NULL),
110	mPanel(NULL),
111	mNotification(p.notification),
112	mIsHidden(false),
113	mHideBtnPressed(false),
114	mIsTip(p.is_tip),
115	mWrapperPanel(NULL),
116	mIsFading(false),
117	mIsHovered(false)
118{
119	mTimer.reset(new LLToastLifeTimer(this, p.lifetime_secs));
120
121	buildFromFile("panel_toast.xml", NULL);
122
123	setCanDrag(FALSE);
124
125	mWrapperPanel = getChild<LLPanel>("wrapper_panel");
126
127	setBackgroundOpaque(TRUE); // *TODO: obsolete
128	updateTransparency();
129
130	if(p.panel())
131	{
132		insertPanel(p.panel);
133	}
134
135	if(mHideBtnEnabled)
136	{
137		mHideBtn = getChild<LLButton>("hide_btn");
138		mHideBtn->setClickedCallback(boost::bind(&LLToast::hide,this));
139	}
140
141	// init callbacks if present
142	if(!p.on_delete_toast().empty())
143	{
144		mOnDeleteToastSignal.connect(p.on_delete_toast());
145	}
146}
147
148void LLToast::reshape(S32 width, S32 height, BOOL called_from_parent)
149{
150	// We shouldn't  use reshape from LLModalDialog since it changes toasts position.
151	// Toasts position should be controlled only by toast screen channel, see LLScreenChannelBase.
152	// see EXT-8044
153	LLFloater::reshape(width, height, called_from_parent);
154}
155
156//--------------------------------------------------------------------------
157BOOL LLToast::postBuild()
158{
159	if(!mCanFade)
160	{
161		mTimer->stop();
162	}
163
164	return TRUE;
165}
166
167//--------------------------------------------------------------------------
168void LLToast::setHideButtonEnabled(bool enabled)
169{
170	if(mHideBtn)
171		mHideBtn->setEnabled(enabled);
172}
173
174//--------------------------------------------------------------------------
175LLToast::~LLToast()
176{	
177	mOnToastDestroyedSignal(this);
178}
179
180//--------------------------------------------------------------------------
181void LLToast::hide()
182{
183	setVisible(FALSE);
184	setFading(false);
185	mTimer->stop();
186	mIsHidden = true;
187	mOnFadeSignal(this); 
188}
189
190void LLToast::onFocusLost()
191{
192	if(mWrapperPanel && !isBackgroundVisible())
193	{
194		// Lets make wrapper panel behave like a floater
195		updateTransparency();
196	}
197}
198
199void LLToast::onFocusReceived()
200{
201	if(mWrapperPanel && !isBackgroundVisible())
202	{
203		// Lets make wrapper panel behave like a floater
204		updateTransparency();
205	}
206}
207
208void LLToast::setLifetime(S32 seconds)
209{
210	mToastLifetime = seconds;
211}
212
213void LLToast::setFadingTime(S32 seconds)
214{
215	mToastFadingTime = seconds;
216}
217
218S32 LLToast::getTopPad()
219{
220	if(mWrapperPanel)
221	{
222		return getRect().getHeight() - mWrapperPanel->getRect().getHeight();
223	}
224	return 0;
225}
226
227S32 LLToast::getRightPad()
228{
229	if(mWrapperPanel)
230	{
231		return getRect().getWidth() - mWrapperPanel->getRect().getWidth();
232	}
233	return 0;
234}
235
236//--------------------------------------------------------------------------
237void LLToast::setCanFade(bool can_fade) 
238{ 
239	mCanFade = can_fade; 
240	if(!mCanFade)
241	{
242		mTimer->stop();
243	}
244}
245
246//--------------------------------------------------------------------------
247void LLToast::expire()
248{
249	if (mCanFade)
250	{
251		if (mIsFading)
252		{
253			// Fade timer expired. Time to hide.
254			hide();
255		}
256		else
257		{
258			// "Life" time has ended. Time to fade.
259			setFading(true);
260			mTimer->restart();
261		}
262	}
263}
264
265void LLToast::setFading(bool transparent)
266{
267	mIsFading = transparent;
268	updateTransparency();
269
270	if (transparent)
271	{
272		mTimer->setPeriod(mToastFadingTime);
273	}
274	else
275	{
276		mTimer->setPeriod(mToastLifetime);
277	}
278}
279
280F32 LLToast::getTimeLeftToLive()
281{
282	F32 time_to_live = mTimer->getRemainingTimeF32();
283
284	if (!mIsFading)
285	{
286		time_to_live += mToastFadingTime;
287	}
288
289	return time_to_live;
290}
291//--------------------------------------------------------------------------
292
293void LLToast::reshapeToPanel()
294{
295	LLPanel* panel = getPanel();
296	if(!panel)
297		return;
298
299	LLRect panel_rect = panel->getRect();
300
301	panel_rect.setLeftTopAndSize(0, panel_rect.getHeight(), panel_rect.getWidth(), panel_rect.getHeight());
302	panel->setShape(panel_rect);
303	
304	LLRect toast_rect = getRect();
305
306	toast_rect.setLeftTopAndSize(toast_rect.mLeft, toast_rect.mTop,
307		panel_rect.getWidth() + getRightPad(), panel_rect.getHeight() + getTopPad());
308	setShape(toast_rect);
309}
310
311void LLToast::insertPanel(LLPanel* panel)
312{
313	mPanel = panel;
314	mWrapperPanel->addChild(panel);	
315	reshapeToPanel();
316}
317
318//--------------------------------------------------------------------------
319void LLToast::draw()
320{
321	LLFloater::draw();
322
323	if(!isBackgroundVisible())
324	{
325		// Floater background is invisible, lets make wrapper panel look like a 
326		// floater - draw shadow.
327		drawShadow(mWrapperPanel);
328
329		// Shadow will probably overlap close button, lets redraw the button
330		if(mHideBtn)
331		{
332			drawChild(mHideBtn);
333		}
334	}
335}
336
337//--------------------------------------------------------------------------
338void LLToast::setVisible(BOOL show)
339{
340	if(mIsHidden)
341	{
342		// this toast is invisible after fade until its ScreenChannel will allow it
343		//
344		// (EXT-1849) according to this bug a toast can be resurrected from
345		// invisible state if it faded during a teleportation
346		// then it fades a second time and causes a crash
347		return;
348	}
349
350	if (show && getVisible())
351	{
352		return;
353	}
354
355	if(show)
356	{
357		if(!mTimer->getStarted() && mCanFade)
358		{
359			mTimer->start();
360		}
361		if (!getVisible())
362		{
363			LLModalDialog::setFrontmost(FALSE);
364		}
365	}
366	else
367	{
368		//hide "hide" button in case toast was hidden without mouse_leave
369		if(mHideBtn)
370			mHideBtn->setVisible(show);
371	}
372	LLFloater::setVisible(show);
373	if(mPanel)
374	{
375		if(!mPanel->isDead())
376		{
377			mPanel->setVisible(show);
378		}
379	}
380}
381
382void LLToast::updateHoveredState()
383{
384	S32 x, y;
385	LLUI::getMousePositionScreen(&x, &y);
386
387	LLRect panel_rc = mWrapperPanel->calcScreenRect();
388	LLRect button_rc;
389	if(mHideBtn)
390	{
391		button_rc = mHideBtn->calcScreenRect();
392	}
393
394	if (!panel_rc.pointInRect(x, y) && !button_rc.pointInRect(x, y))
395	{
396		// mouse is not over this toast
397		mIsHovered = false;
398	}
399	else
400	{
401		bool is_overlapped_by_other_floater = false;
402
403		const child_list_t* child_list = gFloaterView->getChildList();
404
405		// find this toast in gFloaterView child list to check whether any floater
406		// with higher Z-order is visible under the mouse pointer overlapping this toast
407		child_list_const_reverse_iter_t r_iter = std::find(child_list->rbegin(), child_list->rend(), this);
408		if (r_iter != child_list->rend())
409		{
410			// skip this toast and proceed to views above in Z-order
411			for (++r_iter; r_iter != child_list->rend(); ++r_iter)
412			{
413				LLView* view = *r_iter;
414				is_overlapped_by_other_floater = view->isInVisibleChain() && view->calcScreenRect().pointInRect(x, y);
415				if (is_overlapped_by_other_floater)
416				{
417					break;
418				}
419			}
420		}
421
422		mIsHovered = !is_overlapped_by_other_floater;
423	}
424
425	LLToastLifeTimer* timer = getTimer();
426	
427	if (timer)
428	{	
429		// Started timer means the mouse had left the toast previously.
430		// If toast is hovered in the current frame we should handle
431		// a mouse enter event.
432		if(timer->getStarted() && mIsHovered)
433		{
434			mOnToastHoverSignal(this, MOUSE_ENTER);
435			
436			updateTransparency();
437			
438			//toasts fading is management by Screen Channel
439			
440			sendChildToFront(mHideBtn);
441			if(mHideBtn && mHideBtn->getEnabled())
442			{
443				mHideBtn->setVisible(TRUE);
444			}
445			
446			mToastMouseEnterSignal(this, getValue());
447		}
448		// Stopped timer means the mouse had entered the toast previously.
449		// If the toast is not hovered in the current frame we should handle
450		// a mouse leave event.
451		else if(!timer->getStarted() && !mIsHovered)
452		{
453			mOnToastHoverSignal(this, MOUSE_LEAVE);
454			
455			updateTransparency();
456			
457			//toasts fading is management by Screen Channel
458			
459			if(mHideBtn && mHideBtn->getEnabled())
460			{
461				if( mHideBtnPressed )
462				{
463					mHideBtnPressed = false;
464					return;
465				}
466				mHideBtn->setVisible(FALSE);
467			}
468			
469			mToastMouseLeaveSignal(this, getValue());
470		}
471	}
472}
473
474void LLToast::setBackgroundOpaque(BOOL b)
475{
476	if(mWrapperPanel && !isBackgroundVisible())
477	{
478		mWrapperPanel->setBackgroundOpaque(b);
479	}
480	else
481	{
482		LLModalDialog::setBackgroundOpaque(b);
483	}
484}
485
486void LLToast::updateTransparency()
487{
488	ETypeTransparency transparency_type;
489
490	if (mCanFade)
491	{
492		// Notification toasts (including IM/chat toasts) change their transparency on hover.
493		if (isHovered())
494		{
495			transparency_type = TT_ACTIVE;
496		}
497		else
498		{
499			transparency_type = mIsFading ? TT_FADING : TT_INACTIVE;
500		}
501	}
502	else
503	{
504		// Transparency of alert toasts depends on focus.
505		transparency_type = hasFocus() ? TT_ACTIVE : TT_INACTIVE;
506	}
507
508	LLFloater::updateTransparency(transparency_type);
509}
510
511void LLNotificationsUI::LLToast::stopTimer()
512{
513	if(mCanFade)
514	{
515		setFading(false);
516		mTimer->stop();
517	}
518}
519
520void LLNotificationsUI::LLToast::startTimer()
521{
522	if(mCanFade)
523	{
524		setFading(false);
525		mTimer->start();
526	}
527}
528
529//--------------------------------------------------------------------------
530
531BOOL LLToast::handleMouseDown(S32 x, S32 y, MASK mask)
532{
533	if(mHideBtn && mHideBtn->getEnabled())
534	{
535		mHideBtnPressed = mHideBtn->getRect().pointInRect(x, y);
536	}
537
538	return LLFloater::handleMouseDown(x, y, mask);
539}
540
541//--------------------------------------------------------------------------
542bool LLToast::isNotificationValid()
543{
544	if(mNotification)
545	{
546		return !mNotification->isCancelled();
547	}
548	return false;
549}
550
551//--------------------------------------------------------------------------
552
553S32	LLToast::notifyParent(const LLSD& info)
554{
555	if (info.has("action") && "hide_toast" == info["action"].asString())
556	{
557		hide();
558		return 1;
559	}
560
561	return LLModalDialog::notifyParent(info);
562}
563
564//static
565void LLToast::updateClass()
566{
567	for (LLInstanceTracker<LLToast>::instance_iter iter = LLInstanceTracker<LLToast>::beginInstances(); iter != LLInstanceTracker<LLToast>::endInstances(); ) 
568	{
569		LLToast& toast = *iter++;
570		
571		toast.updateHoveredState();
572	}
573}