PageRenderTime 230ms CodeModel.GetById 12ms app.highlight 195ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llchiclet.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2002 lines | 1540 code | 325 blank | 137 comment | 213 complexity | d63ca6a6b3fe58ce6fe91108d9d78a9c MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llchiclet.cpp
   3 * @brief LLChiclet class implementation
   4 *
   5 * $LicenseInfo:firstyear=2002&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#include "llchiclet.h"
  29
  30#include "llagent.h"
  31#include "llavataractions.h"
  32#include "llchicletbar.h"
  33#include "lleventtimer.h"
  34#include "llgroupactions.h"
  35#include "lliconctrl.h"
  36#include "llimfloater.h"
  37#include "llimview.h"
  38#include "llfloaterreg.h"
  39#include "lllocalcliprect.h"
  40#include "llmenugl.h"
  41#include "llnotifications.h"
  42#include "llnotificationsutil.h"
  43#include "lloutputmonitorctrl.h"
  44#include "llscriptfloater.h"
  45#include "llspeakers.h"
  46#include "lltextbox.h"
  47#include "llvoiceclient.h"
  48#include "llgroupmgr.h"
  49#include "llnotificationmanager.h"
  50#include "lltransientfloatermgr.h"
  51#include "llsyswellwindow.h"
  52
  53static LLDefaultChildRegistry::Register<LLChicletPanel> t1("chiclet_panel");
  54static LLDefaultChildRegistry::Register<LLIMWellChiclet> t2_0("chiclet_im_well");
  55static LLDefaultChildRegistry::Register<LLNotificationChiclet> t2("chiclet_notification");
  56static LLDefaultChildRegistry::Register<LLIMP2PChiclet> t3("chiclet_im_p2p");
  57static LLDefaultChildRegistry::Register<LLIMGroupChiclet> t4("chiclet_im_group");
  58static LLDefaultChildRegistry::Register<LLAdHocChiclet> t5("chiclet_im_adhoc");
  59static LLDefaultChildRegistry::Register<LLScriptChiclet> t6("chiclet_script");
  60static LLDefaultChildRegistry::Register<LLInvOfferChiclet> t7("chiclet_offer");
  61
  62boost::signals2::signal<LLChiclet* (const LLUUID&),
  63		LLIMChiclet::CollectChicletCombiner<std::list<LLChiclet*> > >
  64		LLIMChiclet::sFindChicletsSignal;
  65//////////////////////////////////////////////////////////////////////////
  66//////////////////////////////////////////////////////////////////////////
  67//////////////////////////////////////////////////////////////////////////
  68
  69/**
  70 * Updates the Well's 'Lit' state to flash it when "new messages" are come.
  71 *
  72 * It gets callback which will be called 2*N times with passed period. See EXT-3147
  73 */
  74class LLSysWellChiclet::FlashToLitTimer : public LLEventTimer
  75{
  76public:
  77	typedef boost::function<void()> callback_t;
  78
  79	/**
  80	 * Constructor.
  81	 *
  82	 * @param count - how many times callback should be called (twice to not change original state)
  83	 * @param period - how frequently callback should be called
  84	 * @param cb - callback to be called each tick
  85	 */
  86	FlashToLitTimer(S32 count, F32 period, callback_t cb)
  87		: LLEventTimer(period)
  88		, mCallback(cb)
  89		, mFlashCount(2 * count)
  90		, mCurrentFlashCount(0)
  91	{
  92		mEventTimer.stop();
  93	}
  94
  95	BOOL tick()
  96	{
  97		mCallback();
  98
  99		if (++mCurrentFlashCount == mFlashCount) mEventTimer.stop();
 100		return FALSE;
 101	}
 102
 103	void flash()
 104	{
 105		mCurrentFlashCount = 0;
 106		mEventTimer.start();
 107	}
 108
 109	void stopFlashing()
 110	{
 111		mEventTimer.stop();
 112	}
 113
 114private:
 115	callback_t		mCallback;
 116
 117	/**
 118	 * How many times Well will blink.
 119	 */
 120	S32 mFlashCount;
 121	S32 mCurrentFlashCount;
 122};
 123
 124LLSysWellChiclet::Params::Params()
 125: button("button")
 126, unread_notifications("unread_notifications")
 127, max_displayed_count("max_displayed_count", 99)
 128{
 129	button.name = "button";
 130	button.tab_stop = FALSE;
 131	button.label = LLStringUtil::null;
 132}
 133
 134LLSysWellChiclet::LLSysWellChiclet(const Params& p)
 135: LLChiclet(p)
 136, mButton(NULL)
 137, mCounter(0)
 138, mMaxDisplayedCount(p.max_displayed_count)
 139, mIsNewMessagesState(false)
 140, mFlashToLitTimer(NULL)
 141, mContextMenu(NULL)
 142{
 143	LLButton::Params button_params = p.button;
 144	mButton = LLUICtrlFactory::create<LLButton>(button_params);
 145	addChild(mButton);
 146
 147	// use settings from settings.xml to be able change them via Debug settings. See EXT-5973.
 148	// Due to Timer is implemented as derived class from EventTimer it is impossible to change period
 149	// in runtime. So, both settings are made as required restart.
 150	static S32 flash_to_lit_count = gSavedSettings.getS32("WellIconFlashCount");
 151	static F32 flash_period = gSavedSettings.getF32("WellIconFlashPeriod");
 152
 153	mFlashToLitTimer = new FlashToLitTimer(flash_to_lit_count, flash_period, boost::bind(&LLSysWellChiclet::changeLitState, this));
 154}
 155
 156LLSysWellChiclet::~LLSysWellChiclet()
 157{
 158	delete mFlashToLitTimer;
 159}
 160
 161void LLSysWellChiclet::setCounter(S32 counter)
 162{
 163	// do nothing if the same counter is coming. EXT-3678.
 164	if (counter == mCounter) return;
 165
 166	// note same code in LLChicletNotificationCounterCtrl::setCounter(S32 counter)
 167	std::string s_count;
 168	if(counter != 0)
 169	{
 170		static std::string more_messages_exist("+");
 171		std::string more_messages(counter > mMaxDisplayedCount ? more_messages_exist : "");
 172		s_count = llformat("%d%s"
 173			, llmin(counter, mMaxDisplayedCount)
 174			, more_messages.c_str()
 175			);
 176	}
 177
 178	mButton->setLabel(s_count);
 179
 180	mCounter = counter;
 181}
 182
 183boost::signals2::connection LLSysWellChiclet::setClickCallback(
 184	const commit_callback_t& cb)
 185{
 186	return mButton->setClickedCallback(cb);
 187}
 188
 189void LLSysWellChiclet::setToggleState(BOOL toggled) {
 190	mButton->setToggleState(toggled);
 191}
 192
 193void LLSysWellChiclet::changeLitState()
 194{
 195	setNewMessagesState(!mIsNewMessagesState);
 196}
 197
 198void LLSysWellChiclet::setNewMessagesState(bool new_messages)
 199{
 200	/*
 201	Emulate 4 states of button by background images, see detains in EXT-3147
 202	xml attribute           Description
 203	image_unselected        "Unlit" - there are no new messages
 204	image_selected          "Unlit" + "Selected" - there are no new messages and the Well is open
 205	image_pressed           "Lit" - there are new messages
 206	image_pressed_selected  "Lit" + "Selected" - there are new messages and the Well is open
 207	*/
 208	mButton->setForcePressedState(new_messages);
 209
 210	mIsNewMessagesState = new_messages;
 211}
 212
 213void LLSysWellChiclet::updateWidget(bool is_window_empty)
 214{
 215	mButton->setEnabled(!is_window_empty);
 216
 217	if (LLChicletBar::instanceExists())
 218	{
 219		LLChicletBar::getInstance()->showWellButton(getName(), !is_window_empty);
 220	}
 221}
 222// virtual
 223BOOL LLSysWellChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
 224{
 225	if(!mContextMenu)
 226	{
 227		createMenu();
 228	}
 229	if (mContextMenu)
 230	{
 231		mContextMenu->show(x, y);
 232		LLMenuGL::showPopup(this, mContextMenu, x, y);
 233	}
 234	return TRUE;
 235}
 236
 237/************************************************************************/
 238/*               LLIMWellChiclet implementation                         */
 239/************************************************************************/
 240LLIMWellChiclet::LLIMWellChiclet(const Params& p)
 241: LLSysWellChiclet(p)
 242{
 243	LLIMModel::instance().addNewMsgCallback(boost::bind(&LLIMWellChiclet::messageCountChanged, this, _1));
 244	LLIMModel::instance().addNoUnreadMsgsCallback(boost::bind(&LLIMWellChiclet::messageCountChanged, this, _1));
 245
 246	LLIMMgr::getInstance()->addSessionObserver(this);
 247
 248	LLIMWellWindow::getInstance()->setSysWellChiclet(this);
 249}
 250
 251LLIMWellChiclet::~LLIMWellChiclet()
 252{
 253	LLIMWellWindow* im_well_window = LLIMWellWindow::findInstance();
 254	if (im_well_window)
 255	{
 256		im_well_window->setSysWellChiclet(NULL);
 257	}
 258
 259	LLIMMgr::getInstance()->removeSessionObserver(this);
 260}
 261
 262void LLIMWellChiclet::onMenuItemClicked(const LLSD& user_data)
 263{
 264	std::string action = user_data.asString();
 265	if("close all" == action)
 266	{
 267		LLIMWellWindow::getInstance()->closeAll();
 268	}
 269}
 270
 271bool LLIMWellChiclet::enableMenuItem(const LLSD& user_data)
 272{
 273	std::string item = user_data.asString();
 274	if (item == "can close all")
 275	{
 276		return !LLIMWellWindow::getInstance()->isWindowEmpty();
 277	}
 278	return true;
 279}
 280
 281void LLIMWellChiclet::createMenu()
 282{
 283	if(mContextMenu)
 284	{
 285		llwarns << "Menu already exists" << llendl;
 286		return;
 287	}
 288
 289	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 290	registrar.add("IMWellChicletMenu.Action",
 291		boost::bind(&LLIMWellChiclet::onMenuItemClicked, this, _2));
 292
 293	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
 294	enable_registrar.add("IMWellChicletMenu.EnableItem",
 295		boost::bind(&LLIMWellChiclet::enableMenuItem, this, _2));
 296
 297	mContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>
 298		("menu_im_well_button.xml",
 299		 LLMenuGL::sMenuContainer,
 300		 LLViewerMenuHolderGL::child_registry_t::instance());
 301}
 302
 303void LLIMWellChiclet::messageCountChanged(const LLSD& session_data)
 304{
 305	// The singleton class LLChicletBar instance might be already deleted
 306	// so don't create a new one.
 307	if (!LLChicletBar::instanceExists())
 308	{
 309		return;
 310	}
 311
 312	const LLUUID& session_id = session_data["session_id"];
 313	const S32 counter = LLChicletBar::getInstance()->getTotalUnreadIMCount();
 314	const bool im_not_visible = !LLFloaterReg::instanceVisible("im_container")
 315		&& !LLFloaterReg::instanceVisible("impanel", session_id);
 316
 317	setNewMessagesState(counter > mCounter	&& im_not_visible);
 318
 319	// we have to flash to 'Lit' state each time new unread message is coming.
 320	if (counter > mCounter && im_not_visible)
 321	{
 322		mFlashToLitTimer->flash();
 323	}
 324	else if (counter == 0)
 325	{
 326		// if notification is resolved while well is flashing it can leave in the 'Lit' state
 327		// when flashing finishes itself. Let break flashing here.
 328		mFlashToLitTimer->stopFlashing();
 329	}
 330
 331	setCounter(counter);
 332}
 333
 334/************************************************************************/
 335/*               LLNotificationChiclet implementation                   */
 336/************************************************************************/
 337LLNotificationChiclet::LLNotificationChiclet(const Params& p)
 338: LLSysWellChiclet(p)
 339, mUreadSystemNotifications(0)
 340{
 341	// connect counter handlers to the signals
 342	connectCounterUpdatersToSignal("notify");
 343	connectCounterUpdatersToSignal("groupnotify");
 344	connectCounterUpdatersToSignal("offer");
 345
 346	// ensure that notification well window exists, to synchronously
 347	// handle toast add/delete events.
 348	LLNotificationWellWindow::getInstance()->setSysWellChiclet(this);
 349}
 350
 351void LLNotificationChiclet::connectCounterUpdatersToSignal(const std::string& notification_type)
 352{
 353	LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance();
 354	LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type);
 355	if(n_handler)
 356	{
 357		n_handler->setNewNotificationCallback(boost::bind(&LLNotificationChiclet::incUreadSystemNotifications, this));
 358		n_handler->setDelNotification(boost::bind(&LLNotificationChiclet::decUreadSystemNotifications, this));
 359	}
 360}
 361
 362void LLNotificationChiclet::onMenuItemClicked(const LLSD& user_data)
 363{
 364	std::string action = user_data.asString();
 365	if("close all" == action)
 366	{
 367		LLNotificationWellWindow::getInstance()->closeAll();
 368	}
 369}
 370
 371bool LLNotificationChiclet::enableMenuItem(const LLSD& user_data)
 372{
 373	std::string item = user_data.asString();
 374	if (item == "can close all")
 375	{
 376		return mUreadSystemNotifications != 0;
 377	}
 378	return true;
 379}
 380
 381void LLNotificationChiclet::createMenu()
 382{
 383	if(mContextMenu)
 384	{
 385		llwarns << "Menu already exists" << llendl;
 386		return;
 387	}
 388
 389	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 390	registrar.add("NotificationWellChicletMenu.Action",
 391		boost::bind(&LLNotificationChiclet::onMenuItemClicked, this, _2));
 392
 393	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
 394	enable_registrar.add("NotificationWellChicletMenu.EnableItem",
 395		boost::bind(&LLNotificationChiclet::enableMenuItem, this, _2));
 396
 397	mContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>
 398		("menu_notification_well_button.xml",
 399		 LLMenuGL::sMenuContainer,
 400		 LLViewerMenuHolderGL::child_registry_t::instance());
 401}
 402
 403/*virtual*/
 404void LLNotificationChiclet::setCounter(S32 counter)
 405{
 406	LLSysWellChiclet::setCounter(counter);
 407	updateWidget(getCounter() == 0);
 408	
 409}
 410//////////////////////////////////////////////////////////////////////////
 411//////////////////////////////////////////////////////////////////////////
 412//////////////////////////////////////////////////////////////////////////
 413
 414LLChiclet::Params::Params()
 415 : show_counter("show_counter", true)
 416 , enable_counter("enable_counter", false)
 417{
 418}
 419
 420LLChiclet::LLChiclet(const Params& p)
 421: LLUICtrl(p)
 422, mSessionId(LLUUID::null)
 423, mShowCounter(p.show_counter)
 424{
 425
 426}
 427
 428LLChiclet::~LLChiclet()
 429{
 430
 431}
 432
 433boost::signals2::connection LLChiclet::setLeftButtonClickCallback(
 434	const commit_callback_t& cb)
 435{
 436	return setCommitCallback(cb);
 437}
 438
 439BOOL LLChiclet::handleMouseDown(S32 x, S32 y, MASK mask)
 440{
 441	onCommit();
 442	childrenHandleMouseDown(x,y,mask);
 443	return TRUE;
 444}
 445
 446boost::signals2::connection LLChiclet::setChicletSizeChangedCallback(
 447	const chiclet_size_changed_callback_t& cb)
 448{
 449	return mChicletSizeChangedSignal.connect(cb);
 450}
 451
 452void LLChiclet::onChicletSizeChanged()
 453{
 454	mChicletSizeChangedSignal(this, getValue());
 455}
 456
 457LLSD LLChiclet::getValue() const
 458{
 459	return LLSD(getSessionId());
 460}
 461
 462void LLChiclet::setValue(const LLSD& value)
 463{
 464	if(value.isUUID())
 465		setSessionId(value.asUUID());
 466}
 467
 468//////////////////////////////////////////////////////////////////////////
 469//////////////////////////////////////////////////////////////////////////
 470//////////////////////////////////////////////////////////////////////////
 471
 472LLIMChiclet::LLIMChiclet(const LLIMChiclet::Params& p)
 473: LLChiclet(p)
 474, mShowSpeaker(false)
 475, mDefaultWidth(p.rect().getWidth())
 476, mNewMessagesIcon(NULL)
 477, mSpeakerCtrl(NULL)
 478, mCounterCtrl(NULL)
 479, mChicletButton(NULL)
 480, mPopupMenu(NULL)
 481{
 482	enableCounterControl(p.enable_counter);
 483}
 484
 485/* virtual*/
 486BOOL LLIMChiclet::postBuild()
 487{
 488	mChicletButton = getChild<LLButton>("chiclet_button");
 489	mChicletButton->setCommitCallback(boost::bind(&LLIMChiclet::onMouseDown, this));
 490	mChicletButton->setDoubleClickCallback(boost::bind(&LLIMChiclet::onMouseDown, this));
 491	return TRUE;
 492}
 493void LLIMChiclet::setShowSpeaker(bool show)
 494{
 495	bool needs_resize = getShowSpeaker() != show;
 496	if(needs_resize)
 497	{		
 498		mShowSpeaker = show;
 499	}
 500
 501	toggleSpeakerControl();
 502}
 503
 504void LLIMChiclet::enableCounterControl(bool enable) 
 505{
 506	mCounterEnabled = enable;
 507	if(!enable)
 508	{
 509		LLChiclet::setShowCounter(false);
 510	}
 511}
 512
 513void LLIMChiclet::setShowCounter(bool show)
 514{
 515	if(!mCounterEnabled)
 516	{
 517		return;
 518	}
 519
 520	bool needs_resize = getShowCounter() != show;
 521	if(needs_resize)
 522	{		
 523		LLChiclet::setShowCounter(show);
 524		toggleCounterControl();
 525	}
 526}
 527
 528void LLIMChiclet::initSpeakerControl()
 529{
 530	// virtual
 531}
 532
 533void LLIMChiclet::setRequiredWidth()
 534{
 535	bool show_speaker = getShowSpeaker();
 536	bool show_counter = getShowCounter();
 537	S32 required_width = mDefaultWidth;
 538
 539	if (show_counter)
 540	{
 541		required_width += mCounterCtrl->getRect().getWidth();
 542	}
 543	if (show_speaker)
 544	{
 545		required_width += mSpeakerCtrl->getRect().getWidth();
 546	} 
 547
 548	reshape(required_width, getRect().getHeight());
 549
 550	onChicletSizeChanged();
 551}
 552
 553void LLIMChiclet::toggleSpeakerControl()
 554{
 555	if(getShowSpeaker())
 556	{
 557		// move speaker to the right of chiclet icon
 558		LLRect speaker_rc = mSpeakerCtrl->getRect();
 559		speaker_rc.setLeftTopAndSize(mDefaultWidth, speaker_rc.mTop, speaker_rc.getWidth(), speaker_rc.getHeight());
 560		mSpeakerCtrl->setRect(speaker_rc);
 561
 562		if(getShowCounter())
 563		{
 564			// move speaker to the right of counter
 565			mSpeakerCtrl->translate(mCounterCtrl->getRect().getWidth(), 0);
 566		}
 567
 568		initSpeakerControl();		
 569	}
 570
 571	setRequiredWidth();
 572	mSpeakerCtrl->setSpeakerId(LLUUID::null);
 573	mSpeakerCtrl->setVisible(getShowSpeaker());
 574}
 575
 576void LLIMChiclet::setCounter(S32 counter)
 577{
 578	if (mCounterCtrl->getCounter() == counter)
 579	{
 580		return;
 581	}
 582
 583	mCounterCtrl->setCounter(counter);
 584	setShowCounter(counter);
 585	setShowNewMessagesIcon(counter);
 586}
 587
 588void LLIMChiclet::toggleCounterControl()
 589{
 590	setRequiredWidth();
 591	mCounterCtrl->setVisible(getShowCounter());
 592}
 593
 594void LLIMChiclet::setShowNewMessagesIcon(bool show)
 595{
 596	if(mNewMessagesIcon)
 597	{
 598		mNewMessagesIcon->setVisible(show);
 599	}
 600	setRequiredWidth();
 601}
 602
 603bool LLIMChiclet::getShowNewMessagesIcon()
 604{
 605	return mNewMessagesIcon->getVisible();
 606}
 607
 608void LLIMChiclet::onMouseDown()
 609{
 610	LLIMFloater::toggle(getSessionId());
 611	setCounter(0);
 612}
 613
 614void LLIMChiclet::setToggleState(bool toggle)
 615{
 616	mChicletButton->setToggleState(toggle);
 617}
 618
 619void LLIMChiclet::draw()
 620{
 621	LLUICtrl::draw();
 622}
 623
 624// static
 625LLIMChiclet::EType LLIMChiclet::getIMSessionType(const LLUUID& session_id)
 626{
 627	EType				type	= TYPE_UNKNOWN;
 628
 629	if(session_id.isNull())
 630		return type;
 631
 632	EInstantMessage im_type = LLIMModel::getInstance()->getType(session_id);
 633	if (IM_COUNT == im_type)
 634	{
 635		llassert_always(0 && "IM session not found"); // should never happen
 636		return type;
 637	}
 638
 639	switch(im_type)
 640	{
 641	case IM_NOTHING_SPECIAL:
 642	case IM_SESSION_P2P_INVITE:
 643		type = TYPE_IM;
 644		break;
 645	case IM_SESSION_GROUP_START:
 646	case IM_SESSION_INVITE:
 647		if (gAgent.isInGroup(session_id))
 648		{
 649			type = TYPE_GROUP;
 650		}
 651		else
 652		{
 653			type = TYPE_AD_HOC;
 654		}
 655		break;
 656	case IM_SESSION_CONFERENCE_START:
 657		type = TYPE_AD_HOC;
 658	default:
 659		break;
 660	}
 661
 662	return type;
 663}
 664
 665BOOL LLIMChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
 666{
 667	if(!mPopupMenu)
 668	{
 669		createPopupMenu();
 670	}
 671
 672	if (mPopupMenu)
 673	{
 674		updateMenuItems();
 675		mPopupMenu->arrangeAndClear();
 676		LLMenuGL::showPopup(this, mPopupMenu, x, y);
 677	}
 678
 679	return TRUE;
 680}
 681
 682bool LLIMChiclet::canCreateMenu()
 683{
 684	if(mPopupMenu)
 685	{
 686		llwarns << "Menu already exists" << llendl;
 687		return false;
 688	}
 689	if(getSessionId().isNull())
 690	{
 691		return false;
 692	}
 693	return true;
 694}
 695
 696//////////////////////////////////////////////////////////////////////////
 697//////////////////////////////////////////////////////////////////////////
 698//////////////////////////////////////////////////////////////////////////
 699
 700LLIMP2PChiclet::Params::Params()
 701: avatar_icon("avatar_icon")
 702, chiclet_button("chiclet_button")
 703, unread_notifications("unread_notifications")
 704, speaker("speaker")
 705, new_message_icon("new_message_icon")
 706, show_speaker("show_speaker")
 707{
 708}
 709
 710LLIMP2PChiclet::LLIMP2PChiclet(const Params& p)
 711: LLIMChiclet(p)
 712, mChicletIconCtrl(NULL)
 713{
 714	LLButton::Params button_params = p.chiclet_button;
 715	mChicletButton = LLUICtrlFactory::create<LLButton>(button_params);
 716	addChild(mChicletButton);
 717
 718	LLIconCtrl::Params new_msg_params = p.new_message_icon;
 719	mNewMessagesIcon = LLUICtrlFactory::create<LLIconCtrl>(new_msg_params);
 720	addChild(mNewMessagesIcon);
 721
 722	LLChicletAvatarIconCtrl::Params avatar_params = p.avatar_icon;
 723	mChicletIconCtrl = LLUICtrlFactory::create<LLChicletAvatarIconCtrl>(avatar_params);
 724	addChild(mChicletIconCtrl);
 725
 726	LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications;
 727	mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params);
 728	addChild(mCounterCtrl);
 729
 730	setCounter(getCounter());
 731	setShowCounter(getShowCounter());
 732
 733	LLChicletSpeakerCtrl::Params speaker_params = p.speaker;
 734	mSpeakerCtrl = LLUICtrlFactory::create<LLChicletSpeakerCtrl>(speaker_params);
 735	addChild(mSpeakerCtrl);
 736
 737	sendChildToFront(mNewMessagesIcon);
 738	setShowSpeaker(p.show_speaker);
 739}
 740
 741void LLIMP2PChiclet::initSpeakerControl()
 742{
 743	mSpeakerCtrl->setSpeakerId(getOtherParticipantId());
 744}
 745
 746void LLIMP2PChiclet::setOtherParticipantId(const LLUUID& other_participant_id)
 747{
 748	LLIMChiclet::setOtherParticipantId(other_participant_id);
 749	mChicletIconCtrl->setValue(getOtherParticipantId());
 750}
 751
 752void LLIMP2PChiclet::updateMenuItems()
 753{
 754	if(!mPopupMenu)
 755		return;
 756	if(getSessionId().isNull())
 757		return;
 758
 759	LLIMFloater* open_im_floater = LLIMFloater::findInstance(getSessionId());
 760	bool open_window_exists = open_im_floater && open_im_floater->getVisible();
 761	mPopupMenu->getChild<LLUICtrl>("Send IM")->setEnabled(!open_window_exists);
 762	
 763	bool is_friend = LLAvatarActions::isFriend(getOtherParticipantId());
 764	mPopupMenu->getChild<LLUICtrl>("Add Friend")->setEnabled(!is_friend);
 765}
 766
 767void LLIMP2PChiclet::createPopupMenu()
 768{
 769	if(!canCreateMenu())
 770		return;
 771
 772	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 773	registrar.add("IMChicletMenu.Action", boost::bind(&LLIMP2PChiclet::onMenuItemClicked, this, _2));
 774
 775	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
 776		("menu_imchiclet_p2p.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 777}
 778
 779void LLIMP2PChiclet::onMenuItemClicked(const LLSD& user_data)
 780{
 781	std::string level = user_data.asString();
 782	LLUUID other_participant_id = getOtherParticipantId();
 783
 784	if("profile" == level)
 785	{
 786		LLAvatarActions::showProfile(other_participant_id);
 787	}
 788	else if("im" == level)
 789	{
 790		LLAvatarActions::startIM(other_participant_id);
 791	}
 792	else if("add" == level)
 793	{
 794		LLAvatarActions::requestFriendshipDialog(other_participant_id);
 795	}
 796	else if("end" == level)
 797	{
 798		LLAvatarActions::endIM(other_participant_id);
 799	}
 800}
 801
 802//////////////////////////////////////////////////////////////////////////
 803//////////////////////////////////////////////////////////////////////////
 804//////////////////////////////////////////////////////////////////////////
 805
 806LLAdHocChiclet::Params::Params()
 807: avatar_icon("avatar_icon")
 808, chiclet_button("chiclet_button")
 809, unread_notifications("unread_notifications")
 810, speaker("speaker")
 811, new_message_icon("new_message_icon")
 812, show_speaker("show_speaker")
 813, avatar_icon_color("avatar_icon_color", LLColor4::green)
 814{
 815}
 816
 817LLAdHocChiclet::LLAdHocChiclet(const Params& p)
 818: LLIMChiclet(p)
 819, mChicletIconCtrl(NULL)
 820{
 821	LLButton::Params button_params = p.chiclet_button;
 822	mChicletButton = LLUICtrlFactory::create<LLButton>(button_params);
 823	addChild(mChicletButton);
 824
 825	LLIconCtrl::Params new_msg_params = p.new_message_icon;
 826	mNewMessagesIcon = LLUICtrlFactory::create<LLIconCtrl>(new_msg_params);
 827	addChild(mNewMessagesIcon);
 828
 829	LLChicletAvatarIconCtrl::Params avatar_params = p.avatar_icon;
 830	mChicletIconCtrl = LLUICtrlFactory::create<LLChicletAvatarIconCtrl>(avatar_params);
 831	//Make the avatar modified
 832	mChicletIconCtrl->setColor(p.avatar_icon_color);
 833	addChild(mChicletIconCtrl);
 834
 835	LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications;
 836	mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params);
 837	addChild(mCounterCtrl);
 838
 839	setCounter(getCounter());
 840	setShowCounter(getShowCounter());
 841
 842	LLChicletSpeakerCtrl::Params speaker_params = p.speaker;
 843	mSpeakerCtrl = LLUICtrlFactory::create<LLChicletSpeakerCtrl>(speaker_params);
 844	addChild(mSpeakerCtrl);
 845
 846	sendChildToFront(mNewMessagesIcon);
 847	setShowSpeaker(p.show_speaker);
 848}
 849
 850void LLAdHocChiclet::setSessionId(const LLUUID& session_id)
 851{
 852	LLChiclet::setSessionId(session_id);
 853	LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
 854	mChicletIconCtrl->setValue(im_session->mOtherParticipantID);
 855}
 856
 857void LLAdHocChiclet::draw()
 858{
 859	switchToCurrentSpeaker();
 860	LLIMChiclet::draw();
 861}
 862
 863void LLAdHocChiclet::initSpeakerControl()
 864{
 865	switchToCurrentSpeaker();
 866}
 867
 868void LLAdHocChiclet::switchToCurrentSpeaker()
 869{
 870	LLUUID speaker_id;
 871	LLSpeakerMgr::speaker_list_t speaker_list;
 872
 873	LLIMModel::getInstance()->findIMSession(getSessionId())->mSpeakers->getSpeakerList(&speaker_list, FALSE);
 874	for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i)
 875	{
 876		LLPointer<LLSpeaker> s = *i;
 877		if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING)
 878		{
 879			speaker_id = s->mID;
 880			break;
 881		}
 882	}
 883
 884	mSpeakerCtrl->setSpeakerId(speaker_id);
 885}
 886
 887void LLAdHocChiclet::createPopupMenu()
 888{
 889	if(!canCreateMenu())
 890		return;
 891
 892	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 893	registrar.add("IMChicletMenu.Action", boost::bind(&LLAdHocChiclet::onMenuItemClicked, this, _2));
 894
 895	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
 896		("menu_imchiclet_adhoc.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 897}
 898
 899void LLAdHocChiclet::onMenuItemClicked(const LLSD& user_data)
 900{
 901	std::string level = user_data.asString();
 902	LLUUID group_id = getSessionId();
 903
 904	if("end" == level)
 905	{
 906		LLGroupActions::endIM(group_id);
 907	}
 908}
 909
 910//////////////////////////////////////////////////////////////////////////
 911//////////////////////////////////////////////////////////////////////////
 912//////////////////////////////////////////////////////////////////////////
 913
 914LLIMGroupChiclet::Params::Params()
 915: group_icon("group_icon")
 916, chiclet_button("chiclet_button")
 917, unread_notifications("unread_notifications")
 918, speaker("speaker")
 919, new_message_icon("new_message_icon")
 920, show_speaker("show_speaker")
 921{
 922}
 923
 924LLIMGroupChiclet::LLIMGroupChiclet(const Params& p)
 925: LLIMChiclet(p)
 926, LLGroupMgrObserver(LLUUID::null)
 927, mChicletIconCtrl(NULL)
 928{
 929	LLButton::Params button_params = p.chiclet_button;
 930	mChicletButton = LLUICtrlFactory::create<LLButton>(button_params);
 931	addChild(mChicletButton);
 932
 933	LLIconCtrl::Params new_msg_params = p.new_message_icon;
 934	mNewMessagesIcon = LLUICtrlFactory::create<LLIconCtrl>(new_msg_params);
 935	addChild(mNewMessagesIcon);
 936
 937	LLChicletGroupIconCtrl::Params avatar_params = p.group_icon;
 938	mChicletIconCtrl = LLUICtrlFactory::create<LLChicletGroupIconCtrl>(avatar_params);
 939	addChild(mChicletIconCtrl);
 940
 941	LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications;
 942	mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params);
 943	addChild(mCounterCtrl);
 944
 945	setCounter(getCounter());
 946	setShowCounter(getShowCounter());
 947
 948	LLChicletSpeakerCtrl::Params speaker_params = p.speaker;
 949	mSpeakerCtrl = LLUICtrlFactory::create<LLChicletSpeakerCtrl>(speaker_params);
 950	addChild(mSpeakerCtrl);
 951
 952	sendChildToFront(mNewMessagesIcon);
 953	setShowSpeaker(p.show_speaker);
 954}
 955
 956LLIMGroupChiclet::~LLIMGroupChiclet()
 957{
 958	LLGroupMgr::getInstance()->removeObserver(this);
 959}
 960
 961void LLIMGroupChiclet::draw()
 962{
 963	if(getShowSpeaker())
 964	{
 965		switchToCurrentSpeaker();
 966	}
 967	LLIMChiclet::draw();
 968}
 969
 970void LLIMGroupChiclet::initSpeakerControl()
 971{
 972	switchToCurrentSpeaker();
 973}
 974
 975void LLIMGroupChiclet::switchToCurrentSpeaker()
 976{
 977	LLUUID speaker_id;
 978	LLSpeakerMgr::speaker_list_t speaker_list;
 979
 980	LLIMModel::getInstance()->findIMSession(getSessionId())->mSpeakers->getSpeakerList(&speaker_list, FALSE);
 981	for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i)
 982	{
 983		LLPointer<LLSpeaker> s = *i;
 984		if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING)
 985		{
 986			speaker_id = s->mID;
 987			break;
 988		}
 989	}
 990
 991	mSpeakerCtrl->setSpeakerId(speaker_id);
 992}
 993
 994void LLIMGroupChiclet::setSessionId(const LLUUID& session_id)
 995{
 996	LLChiclet::setSessionId(session_id);
 997
 998	LLGroupMgr* grp_mgr = LLGroupMgr::getInstance();
 999	LLGroupMgrGroupData* group_data = grp_mgr->getGroupData(session_id);
1000	if (group_data && group_data->mInsigniaID.notNull())
1001	{
1002		mChicletIconCtrl->setValue(group_data->mInsigniaID);
1003	}
1004	else
1005	{
1006		if(getSessionId() != mID)
1007		{
1008			grp_mgr->removeObserver(this);
1009			mID = getSessionId();
1010			grp_mgr->addObserver(this);
1011		}
1012		grp_mgr->sendGroupPropertiesRequest(session_id);
1013	}
1014}
1015
1016void LLIMGroupChiclet::changed(LLGroupChange gc)
1017{
1018	if (GC_PROPERTIES == gc)
1019	{
1020		LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(getSessionId());
1021		if (group_data)
1022		{
1023			mChicletIconCtrl->setValue(group_data->mInsigniaID);
1024		}
1025	}
1026}
1027
1028void LLIMGroupChiclet::updateMenuItems()
1029{
1030	if(!mPopupMenu)
1031		return;
1032	if(getSessionId().isNull())
1033		return;
1034
1035	LLIMFloater* open_im_floater = LLIMFloater::findInstance(getSessionId());
1036	bool open_window_exists = open_im_floater && open_im_floater->getVisible();
1037	mPopupMenu->getChild<LLUICtrl>("Chat")->setEnabled(!open_window_exists);
1038}
1039
1040void LLIMGroupChiclet::createPopupMenu()
1041{
1042	if(!canCreateMenu())
1043		return;
1044
1045	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
1046	registrar.add("IMChicletMenu.Action", boost::bind(&LLIMGroupChiclet::onMenuItemClicked, this, _2));
1047
1048	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
1049		("menu_imchiclet_group.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
1050}
1051
1052void LLIMGroupChiclet::onMenuItemClicked(const LLSD& user_data)
1053{
1054	std::string level = user_data.asString();
1055	LLUUID group_id = getSessionId();
1056
1057	if("group chat" == level)
1058	{
1059		LLGroupActions::startIM(group_id);
1060	}
1061	else if("info" == level)
1062	{
1063		LLGroupActions::show(group_id);
1064	}
1065	else if("end" == level)
1066	{
1067		LLGroupActions::endIM(group_id);
1068	}
1069}
1070
1071
1072//////////////////////////////////////////////////////////////////////////
1073//////////////////////////////////////////////////////////////////////////
1074//////////////////////////////////////////////////////////////////////////
1075
1076LLChicletPanel::Params::Params()
1077: chiclet_padding("chiclet_padding")
1078, scrolling_offset("scrolling_offset")
1079, scroll_button_hpad("scroll_button_hpad")
1080, scroll_ratio("scroll_ratio")
1081, min_width("min_width")
1082{
1083};
1084
1085LLChicletPanel::LLChicletPanel(const Params&p)
1086: LLPanel(p)
1087, mScrollArea(NULL)
1088, mLeftScrollButton(NULL)
1089, mRightScrollButton(NULL)
1090, mChicletPadding(p.chiclet_padding)
1091, mScrollingOffset(p.scrolling_offset)
1092, mScrollButtonHPad(p.scroll_button_hpad)
1093, mScrollRatio(p.scroll_ratio)
1094, mMinWidth(p.min_width)
1095, mShowControls(true)
1096{
1097	LLPanel::Params panel_params;
1098	panel_params.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT);
1099	mScrollArea = LLUICtrlFactory::create<LLPanel>(panel_params,this);
1100
1101	// important for Show/Hide Camera and Move controls menu in bottom tray to work properly
1102	mScrollArea->setMouseOpaque(false);
1103
1104	addChild(mScrollArea);
1105}
1106
1107LLChicletPanel::~LLChicletPanel()
1108{
1109	if(LLTransientFloaterMgr::instanceExists())
1110	{
1111		LLTransientFloaterMgr::getInstance()->removeControlView(mLeftScrollButton);
1112		LLTransientFloaterMgr::getInstance()->removeControlView(mRightScrollButton);
1113	}
1114}
1115
1116void im_chiclet_callback(LLChicletPanel* panel, const LLSD& data){
1117	
1118	LLUUID session_id = data["session_id"].asUUID();
1119	S32 unread = data["participant_unread"].asInteger();
1120
1121	LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
1122	if (im_floater && im_floater->getVisible() && im_floater->hasFocus())
1123	{
1124		unread = 0;
1125	}
1126
1127	std::list<LLChiclet*> chiclets = LLIMChiclet::sFindChicletsSignal(session_id);
1128	std::list<LLChiclet *>::iterator iter;
1129	for (iter = chiclets.begin(); iter != chiclets.end(); iter++) {
1130		LLChiclet* chiclet = *iter;
1131		if (chiclet != NULL)
1132		{
1133			chiclet->setCounter(unread);
1134		}
1135	    else
1136	    {
1137	    	llwarns << "Unable to set counter for chiclet " << session_id << llendl;
1138	    }
1139	}
1140}
1141
1142void object_chiclet_callback(const LLSD& data)
1143{
1144	LLUUID notification_id = data["notification_id"];
1145	bool new_message = data["new_message"];
1146
1147	std::list<LLChiclet*> chiclets = LLIMChiclet::sFindChicletsSignal(notification_id);
1148	std::list<LLChiclet *>::iterator iter;
1149	for (iter = chiclets.begin(); iter != chiclets.end(); iter++)
1150	{
1151		LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*iter);
1152		if (chiclet != NULL)
1153		{
1154			if(data.has("unread"))
1155			{
1156				chiclet->setCounter(data["unread"]);
1157			}
1158			chiclet->setShowNewMessagesIcon(new_message);
1159		}
1160	}
1161}
1162
1163BOOL LLChicletPanel::postBuild()
1164{
1165	LLPanel::postBuild();
1166	LLIMModel::instance().addNewMsgCallback(boost::bind(im_chiclet_callback, this, _1));
1167	LLIMModel::instance().addNoUnreadMsgsCallback(boost::bind(im_chiclet_callback, this, _1));
1168	LLScriptFloaterManager::getInstance()->addNewObjectCallback(boost::bind(object_chiclet_callback, _1));
1169	LLScriptFloaterManager::getInstance()->addToggleObjectFloaterCallback(boost::bind(object_chiclet_callback, _1));
1170	LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLChicletPanel::findChiclet<LLChiclet>, this, _1));
1171	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLChicletPanel::onCurrentVoiceChannelChanged, this, _1));
1172
1173	mLeftScrollButton=getChild<LLButton>("chicklet_left_scroll_button");
1174	LLTransientFloaterMgr::getInstance()->addControlView(mLeftScrollButton);
1175	mLeftScrollButton->setMouseDownCallback(boost::bind(&LLChicletPanel::onLeftScrollClick,this));
1176	mLeftScrollButton->setHeldDownCallback(boost::bind(&LLChicletPanel::onLeftScrollHeldDown,this));
1177	mLeftScrollButton->setEnabled(false);
1178
1179	mRightScrollButton=getChild<LLButton>("chicklet_right_scroll_button");
1180	LLTransientFloaterMgr::getInstance()->addControlView(mRightScrollButton);
1181	mRightScrollButton->setMouseDownCallback(boost::bind(&LLChicletPanel::onRightScrollClick,this));
1182	mRightScrollButton->setHeldDownCallback(boost::bind(&LLChicletPanel::onRightScrollHeldDown,this));
1183	mRightScrollButton->setEnabled(false);	
1184
1185	return TRUE;
1186}
1187
1188void LLChicletPanel::onCurrentVoiceChannelChanged(const LLUUID& session_id)
1189{
1190	static LLUUID s_previous_active_voice_session_id;
1191
1192	std::list<LLChiclet*> chiclets = LLIMChiclet::sFindChicletsSignal(session_id);
1193
1194	for(std::list<LLChiclet *>::iterator it = chiclets.begin(); it != chiclets.end(); ++it)
1195	{
1196		LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*it);
1197		if(chiclet)
1198		{
1199			chiclet->setShowSpeaker(true);
1200			if (gSavedSettings.getBOOL("OpenIMOnVoice"))
1201			{
1202				LLIMFloater::show(chiclet->getSessionId());
1203			}
1204		}
1205	}
1206
1207	if(!s_previous_active_voice_session_id.isNull() && s_previous_active_voice_session_id != session_id)
1208	{
1209		chiclets = LLIMChiclet::sFindChicletsSignal(s_previous_active_voice_session_id);
1210
1211		for(std::list<LLChiclet *>::iterator it = chiclets.begin(); it != chiclets.end(); ++it)
1212		{
1213			LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*it);
1214			if(chiclet)
1215			{
1216				chiclet->setShowSpeaker(false);
1217			}
1218		}		
1219	}
1220
1221	s_previous_active_voice_session_id = session_id;
1222}
1223
1224bool LLChicletPanel::addChiclet(LLChiclet* chiclet, S32 index)
1225{
1226	if(mScrollArea->addChild(chiclet))
1227	{
1228		// chiclets should be aligned to right edge of scroll panel
1229		S32 left_shift = 0;
1230
1231		if (!canScrollLeft())
1232		{
1233			// init left shift for the first chiclet in the list...
1234			if (mChicletList.empty())
1235			{
1236				// ...start from the right border of the scroll area for the first added chiclet 
1237				left_shift = mScrollArea->getRect().getWidth();
1238			}
1239			else
1240			{
1241				// ... start from the left border of the first chiclet minus padding
1242				left_shift = getChiclet(0)->getRect().mLeft - getChicletPadding();
1243			}
1244
1245			// take into account width of the being added chiclet
1246			left_shift -= chiclet->getRequiredRect().getWidth();
1247
1248			// if we overflow the scroll area we do not need to shift chiclets
1249			if (left_shift < 0)
1250			{
1251				left_shift = 0;
1252			}
1253		}
1254
1255		mChicletList.insert(mChicletList.begin() + index, chiclet);
1256
1257		// shift first chiclet to place it in correct position. 
1258		// rest ones will be placed in arrange()
1259		if (!canScrollLeft())
1260		{
1261			getChiclet(0)->translate(left_shift - getChiclet(0)->getRect().mLeft, 0);
1262		}
1263
1264		chiclet->setLeftButtonClickCallback(boost::bind(&LLChicletPanel::onChicletClick, this, _1, _2));
1265		chiclet->setChicletSizeChangedCallback(boost::bind(&LLChicletPanel::onChicletSizeChanged, this, _1, index));
1266
1267		arrange();
1268		LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, chiclet);
1269
1270		return true;
1271	}
1272
1273	return false;
1274}
1275
1276void LLChicletPanel::onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param)
1277{
1278	arrange();
1279}
1280
1281void LLChicletPanel::onChicletClick(LLUICtrl*ctrl,const LLSD&param)
1282{
1283	if (mCommitSignal)
1284	{
1285		(*mCommitSignal)(ctrl,param);
1286	}
1287}
1288
1289void LLChicletPanel::removeChiclet(chiclet_list_t::iterator it)
1290{
1291	LLChiclet* chiclet = *it;
1292	mScrollArea->removeChild(chiclet);
1293	mChicletList.erase(it);
1294	
1295	arrange();
1296	LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, chiclet);
1297	chiclet->die();
1298}
1299
1300void LLChicletPanel::removeChiclet(S32 index)
1301{
1302	if(index >= 0 && index < getChicletCount())
1303	{
1304		removeChiclet(mChicletList.begin() + index);
1305	}
1306}
1307
1308S32 LLChicletPanel::getChicletIndex(const LLChiclet* chiclet)
1309{
1310	if(mChicletList.empty())
1311		return -1;
1312
1313	S32 size = getChicletCount();
1314	for(int n = 0; n < size; ++n)
1315	{
1316		if(chiclet == mChicletList[n])
1317			return n;
1318	}
1319
1320	return -1;
1321}
1322
1323void LLChicletPanel::removeChiclet(LLChiclet*chiclet)
1324{
1325	chiclet_list_t::iterator it = mChicletList.begin();
1326	for( ; mChicletList.end() != it; ++it)
1327	{
1328		LLChiclet* temp = *it;
1329		if(temp == chiclet)
1330		{
1331			removeChiclet(it);
1332			return;
1333		}
1334	}
1335}
1336
1337void LLChicletPanel::removeChiclet(const LLUUID& im_session_id)
1338{
1339	chiclet_list_t::iterator it = mChicletList.begin();
1340	for( ; mChicletList.end() != it; ++it)
1341	{
1342		LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*it);
1343
1344		if(chiclet->getSessionId() == im_session_id)
1345		{
1346			removeChiclet(it);
1347			return;
1348		}
1349	}
1350}
1351
1352void LLChicletPanel::removeAll()
1353{
1354	S32 size = getChicletCount();
1355	for(S32 n = 0; n < size; ++n)
1356	{
1357		mScrollArea->removeChild(mChicletList[n]);
1358	}
1359
1360	mChicletList.erase(mChicletList.begin(), mChicletList.end());
1361
1362	showScrollButtonsIfNeeded();
1363}
1364
1365void LLChicletPanel::scrollToChiclet(const LLChiclet* chiclet)
1366{
1367	const LLRect& rect = chiclet->getRect();
1368
1369	if (rect.mLeft < 0)
1370	{
1371		scroll(llabs(rect.mLeft));
1372		showScrollButtonsIfNeeded();
1373	}
1374	else
1375	{
1376		S32 scrollWidth = mScrollArea->getRect().getWidth();
1377
1378		if (rect.mRight > scrollWidth)
1379		{
1380			scroll(-llabs(rect.mRight - scrollWidth));
1381			showScrollButtonsIfNeeded();
1382		}
1383	}
1384}
1385
1386void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent )
1387{
1388	LLPanel::reshape(width,height,called_from_parent);
1389
1390	//Needed once- to avoid error at first call of reshape() before postBuild()
1391	if(!mLeftScrollButton||!mRightScrollButton)
1392		return;
1393	
1394	LLRect scroll_button_rect = mLeftScrollButton->getRect();
1395	mLeftScrollButton->setRect(LLRect(0,scroll_button_rect.mTop,scroll_button_rect.getWidth(),
1396		scroll_button_rect.mBottom));
1397	scroll_button_rect = mRightScrollButton->getRect();
1398	mRightScrollButton->setRect(LLRect(width - scroll_button_rect.getWidth(),scroll_button_rect.mTop,
1399		width, scroll_button_rect.mBottom));
1400	
1401
1402	bool need_show_scroll = needShowScroll();
1403	if(need_show_scroll)
1404	{
1405		mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + mScrollButtonHPad,
1406			height, width - scroll_button_rect.getWidth() - mScrollButtonHPad, 0));
1407	}
1408	else
1409	{
1410		mScrollArea->setRect(LLRect(0,height, width, 0));
1411	}
1412	
1413	mShowControls = width >= mMinWidth;
1414	
1415	mScrollArea->setVisible(mShowControls);
1416
1417	trimChiclets();
1418	showScrollButtonsIfNeeded();
1419
1420}
1421
1422S32	LLChicletPanel::notifyParent(const LLSD& info)
1423{
1424	if(info.has("notification"))
1425	{
1426		std::string str_notification = info["notification"];
1427		if(str_notification == "size_changes")
1428		{
1429			arrange();
1430			return 1;
1431		}
1432	}
1433	return LLPanel::notifyParent(info);
1434}
1435
1436void LLChicletPanel::setChicletToggleState(const LLUUID& session_id, bool toggle)
1437{
1438	if(session_id.isNull())
1439	{
1440		llwarns << "Null Session ID" << llendl;
1441	}
1442
1443	// toggle off all chiclets, except specified
1444	S32 size = getChicletCount();
1445	for(int n = 0; n < size; ++n)
1446	{
1447		LLIMChiclet* chiclet = getChiclet<LLIMChiclet>(n);
1448		if(chiclet && chiclet->getSessionId() != session_id)
1449		{
1450			chiclet->setToggleState(false);
1451		}
1452	}
1453
1454	// toggle specified chiclet
1455	LLIMChiclet* chiclet = findChiclet<LLIMChiclet>(session_id);
1456	if(chiclet)
1457	{
1458		chiclet->setToggleState(toggle);
1459	}
1460}
1461
1462void LLChicletPanel::arrange()
1463{
1464	if(mChicletList.empty())
1465		return;
1466
1467	//initial arrange of chicklets positions
1468	S32 chiclet_left = getChiclet(0)->getRect().mLeft;
1469	S32 size = getChicletCount();
1470	for( int n = 0; n < size; ++n)
1471	{
1472		LLChiclet* chiclet = getChiclet(n);
1473
1474		S32 chiclet_width = chiclet->getRequiredRect().getWidth();
1475		LLRect rect = chiclet->getRect();
1476		rect.set(chiclet_left, rect.mTop, chiclet_left + chiclet_width, rect.mBottom);
1477
1478		chiclet->setRect(rect);
1479
1480		chiclet_left += chiclet_width + getChicletPadding();
1481	}
1482
1483	//reset size and pos on mScrollArea
1484	LLRect rect = getRect();
1485	LLRect scroll_button_rect = mLeftScrollButton->getRect();
1486	
1487	bool need_show_scroll = needShowScroll();
1488	if(need_show_scroll)
1489	{
1490		mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + mScrollButtonHPad,
1491			rect.getHeight(), rect.getWidth() - scroll_button_rect.getWidth() - mScrollButtonHPad, 0));
1492	}
1493	else
1494	{
1495		mScrollArea->setRect(LLRect(0,rect.getHeight(), rect.getWidth(), 0));
1496	}
1497	
1498	trimChiclets();
1499	showScrollButtonsIfNeeded();
1500}
1501
1502void LLChicletPanel::trimChiclets()
1503{
1504	// trim right
1505	if(!mChicletList.empty())
1506	{
1507		S32 last_chiclet_right = (*mChicletList.rbegin())->getRect().mRight;
1508		S32 first_chiclet_left = getChiclet(0)->getRect().mLeft;
1509		S32 scroll_width = mScrollArea->getRect().getWidth();
1510		if(last_chiclet_right < scroll_width || first_chiclet_left > 0)
1511		{
1512			shiftChiclets(scroll_width - last_chiclet_right);
1513		}
1514	}
1515}
1516
1517bool LLChicletPanel::needShowScroll()
1518{
1519	if(mChicletList.empty())
1520		return false;
1521	
1522	S32 chicklet_width  = (*mChicletList.rbegin())->getRect().mRight - (*mChicletList.begin())->getRect().mLeft;
1523
1524	return chicklet_width>getRect().getWidth();
1525}
1526
1527
1528void LLChicletPanel::showScrollButtonsIfNeeded()
1529{
1530	bool can_scroll_left = canScrollLeft();
1531	bool can_scroll_right = canScrollRight();
1532
1533	mLeftScrollButton->setEnabled(can_scroll_left);
1534	mRightScrollButton->setEnabled(can_scroll_right);
1535
1536	bool show_scroll_buttons = (can_scroll_left || can_scroll_right) && mShowControls;
1537
1538	mLeftScrollButton->setVisible(show_scroll_buttons);
1539	mRightScrollButton->setVisible(show_scroll_buttons);
1540}
1541
1542void LLChicletPanel::draw()
1543{
1544	child_list_const_iter_t it = getChildList()->begin();
1545	for( ; getChildList()->end() != it; ++it)
1546	{
1547		LLView* child = *it;
1548		if(child == dynamic_cast<LLView*>(mScrollArea))
1549		{
1550			LLLocalClipRect clip(mScrollArea->getRect());
1551			drawChild(mScrollArea);
1552		}
1553		else
1554		{
1555			drawChild(child);
1556		}
1557	}
1558}
1559
1560bool LLChicletPanel::canScrollRight()
1561{
1562	if(mChicletList.empty())
1563		return false;
1564
1565	S32 scroll_width = mScrollArea->getRect().getWidth();
1566	S32 last_chiclet_right = (*mChicletList.rbegin())->getRect().mRight;
1567
1568	if(last_chiclet_right > scroll_width)
1569		return true;
1570
1571	return false;
1572}
1573
1574bool LLChicletPanel::canScrollLeft()
1575{
1576	if(mChicletList.empty())
1577		return false;
1578
1579	return getChiclet(0)->getRect().mLeft < 0;
1580}
1581
1582void LLChicletPanel::scroll(S32 offset)
1583{
1584	shiftChiclets(offset);
1585}
1586
1587void LLChicletPanel::shiftChiclets(S32 offset, S32 start_index /* = 0 */)
1588{
1589	if(start_index < 0 || start_index >= getChicletCount())
1590	{
1591		return;
1592	}
1593
1594	chiclet_list_t::const_iterator it = mChicletList.begin() + start_index;
1595	for(;mChicletList.end() != it; ++it)
1596	{
1597		LLChiclet* chiclet = *it;
1598		chiclet->translate(offset,0);
1599	}
1600}
1601
1602void LLChicletPanel::scrollLeft()
1603{
1604	if(canScrollLeft())
1605	{
1606		S32 offset = getScrollingOffset();
1607		LLRect first_chiclet_rect = getChiclet(0)->getRect();
1608
1609		// shift chiclets in case first chiclet is partially visible
1610		if(first_chiclet_rect.mLeft < 0 && first_chiclet_rect.mRight > 0)
1611		{
1612			offset = llabs(first_chiclet_rect.mLeft);
1613		}
1614
1615		scroll(offset);
1616		
1617		showScrollButtonsIfNeeded();
1618	}
1619}
1620
1621void LLChicletPanel::scrollRight()
1622{
1623	if(canScrollRight())
1624	{
1625		S32 offset = - getScrollingOffset();
1626
1627		S32 last_chiclet_right = (*mChicletList.rbegin())->getRect().mRight;
1628		S32 scroll_rect_width = mScrollArea->getRect().getWidth();
1629		// if after scrolling, the last chiclet will not be aligned to 
1630		// scroll area right side - align it.
1631		if( last_chiclet_right + offset < scroll_rect_width )
1632		{
1633			offset = scroll_rect_width - last_chiclet_right;
1634		}
1635
1636		scroll(offset);
1637		
1638		showScrollButtonsIfNeeded();
1639	}
1640}
1641
1642void LLChicletPanel::onLeftScrollClick()
1643{
1644	scrollLeft();
1645}
1646
1647void LLChicletPanel::onRightScrollClick()
1648{
1649	scrollRight();
1650}
1651
1652void LLChicletPanel::onLeftScrollHeldDown()
1653{
1654	S32 offset = mScrollingOffset;
1655	mScrollingOffset = mScrollingOffset / mScrollRatio;
1656	scrollLeft();
1657	mScrollingOffset = offset;
1658}
1659
1660void LLChicletPanel::onRightScrollHeldDown()
1661{
1662	S32 offset = mScrollingOffset;
1663	mScrollingOffset = mScrollingOffset / mScrollRatio;
1664	scrollRight();
1665	mScrollingOffset = offset;
1666}
1667
1668boost::signals2::connection LLChicletPanel::setChicletClickedCallback(
1669	const commit_callback_t& cb)
1670{
1671	return setCommitCallback(cb);
1672}
1673
1674BOOL LLChicletPanel::handleScrollWheel(S32 x, S32 y, S32 clicks)
1675{
1676	if(clicks > 0)
1677	{
1678		scrollRight();
1679	}
1680	else
1681	{
1682		scrollLeft();
1683	}
1684	return TRUE;
1685}
1686
1687bool LLChicletPanel::isAnyIMFloaterDoked()
1688{
1689	bool res = false;
1690	for (chiclet_list_t::iterator it = mChicletList.begin(); it
1691			!= mChicletList.end(); it++)
1692	{
1693		LLIMFloater* im_floater = LLFloaterReg::findTypedInstance<LLIMFloater>(
1694				"impanel", (*it)->getSessionId());
1695		if (im_floater != NULL && im_floater->getVisible()
1696				&& !im_floater->isMinimized() && im_floater->isDocked())
1697		{
1698			res = true;
1699			break;
1700		}
1701	}
1702
1703	return res;
1704}
1705
1706S32 LLChicletPanel::getTotalUnreadIMCount()
1707{
1708	S32 count = 0;
1709	chiclet_list_t::const_iterator it = mChicletList.begin();
1710	for( ; mChicletList.end() != it; ++it)
1711	{
1712		LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*it);
1713		if(chiclet)
1714		{
1715			count += chiclet->getCounter();
1716		}
1717	}
1718	return count;
1719}
1720
1721//////////////////////////////////////////////////////////////////////////
1722//////////////////////////////////////////////////////////////////////////
1723//////////////////////////////////////////////////////////////////////////
1724LLChicletNotificationCounterCtrl::Params::Params()
1725: max_displayed_count("max_displayed_count", 99)
1726{
1727}
1728
1729LLChicletNotificationCounterCtrl::LLChicletNotificationCounterCtrl(const Params& p)
1730 : LLTextBox(p)
1731 , mCounter(0)
1732 , mInitialWidth(0)
1733 , mMaxDisplayedCount(p.max_displayed_count)
1734{
1735	mInitialWidth = getRect().getWidth();
1736}
1737
1738void LLChicletNotificationCounterCtrl::setCounter(S32 counter)
1739{
1740	mCounter = counter;
1741
1742	// note same code in LLSysWellChiclet::setCounter(S32 counter)
1743	std::string s_count;
1744	if(counter != 0)
1745	{
1746		static std::string more_messages_exist("+");
1747		std::string more_messages(counter > mMaxDisplayedCount ? more_messages_exist : "");
1748		s_count = llformat("%d%s"
1749			, llmin(counter, mMaxDisplayedCount)
1750			, more_messages.c_str()
1751			);
1752	}
1753
1754	if(mCounter != 0)
1755	{
1756		setText(s_count);
1757	}
1758	else
1759	{
1760		setText(std::string(""));
1761	}
1762}
1763
1764LLRect LLChicletNotificationCounterCtrl::getRequiredRect()
1765{
1766	LLRect rc;
1767	S32 text_width = getTextPixelWidth();
1768
1769	rc.mRight = rc.mLeft + llmax(text_width, mInitialWidth);
1770	
1771	return rc;
1772}
1773
1774void LLChicletNotificationCounterCtrl::setValue(const LLSD& value)
1775{
1776	if(value.isInteger())
1777		setCounter(value.asInteger());
1778}
1779
1780LLSD LLChicletNotificationCounterCtrl::getValue() const
1781{
1782	return LLSD(getCounter());
1783}
1784
1785//////////////////////////////////////////////////////////////////////////
1786//////////////////////////////////////////////////////////////////////////
1787//////////////////////////////////////////////////////////////////////////
1788
1789LLChicletAvatarIconCtrl::LLChicletAvatarIconCtrl(const Params& p)
1790 : LLAvatarIconCtrl(p)
1791{
1792}
1793
1794//////////////////////////////////////////////////////////////////////////
1795//////////////////////////////////////////////////////////////////////////
1796//////////////////////////////////////////////////////////////////////////
1797
1798LLChicletGroupIconCtrl::LLChicletGroupIconCtrl(const Params& p)
1799: LLIconCtrl(p)
1800, mDefaultIcon(p.default_icon)
1801{
1802	setValue(LLUUID::null);
1803}
1804
1805void LLChicletGroupIconCtrl::setValue(const LLSD& value )
1806{
1807	if(value.asUUID().isNull())
1808	{
1809		LLIconCtrl::setValue(mDefaultIcon);
1810	}
1811	else
1812	{
1813		LLIconCtrl::setValue(value);
1814	}
1815}
1816
1817//////////////////////////////////////////////////////////////////////////
1818//////////////////////////////////////////////////////////////////////////
1819//////////////////////////////////////////////////////////////////////////
1820
1821LLChicletInvOfferIconCtrl::LLChicletInvOfferIconCtrl(const Params& p)
1822: LLChicletAvatarIconCtrl(p)
1823 , mDefaultIcon(p.default_icon)
1824{
1825}
1826
1827void LLChicletInvOfferIconCtrl::setValue(const LLSD& value )
1828{
1829	if(value.asUUID().isNull())
1830	{
1831		LLIconCtrl::setValue(mDefaultIcon);
1832	}
1833	else
1834	{
1835		LLChicletAvatarIconCtrl::setValue(value);
1836	}
1837}
1838
1839//////////////////////////////////////////////////////////////////////////
1840//////////////////////////////////////////////////////////////////////////
1841//////////////////////////////////////////////////////////////////////////
1842
1843LLChicletSpeakerCtrl::LLChicletSpeakerCtrl(const Params&p)
1844 : LLOutputMonitorCtrl(p)
1845{
1846}
1847
1848//////////////////////////////////////////////////////////////////////////
1849//////////////////////////////////////////////////////////////////////////
1850//////////////////////////////////////////////////////////////////////////
1851
1852LLScriptChiclet::Params::Params()
1853 : icon("icon")
1854 , chiclet_button("chiclet_button")
1855 , new_message_icon("new_message_icon")
1856{
1857}
1858
1859LLScriptChiclet::LLScriptChiclet(const Params&p)
1860 : LLIMChiclet(p)
1861 , mChicletIconCtrl(NULL)
1862{
1863	LLButton::Params button_params = p.chiclet_button;
1864	mChicletButton = LLUICtrlFactory::create<LLButton>(button_params);
1865	addChild(mChicletButton);
1866
1867	LLIconCtrl::Params new_msg_params = p.new_message_icon;
1868	mNewMessagesIcon = LLUICtrlFactory::create<LLIconCtrl>(new_msg_params);
1869	addChild(mNewMessagesIcon);
1870
1871	LLIconCtrl::Params icon_params = p.icon;
1872	mChicletIconCtrl = LLUICtrlFactory::create<LLIconCtrl>(icon_params);
1873	addChild(mChicletIconCtrl);
1874
1875	sendChildToFront(mNewMessagesIcon);
1876}
1877
1878void LLScriptChiclet::setSessionId(const LLUUID& session_id)
1879{
1880	setShowNewMessagesIcon( getSessionId() != session_id );
1881
1882	LLIMChiclet::setSessionId(session_id);
1883
1884	setToolTip(LLScriptFloaterManager::getObjectName(session_id));
1885}
1886
1887void LLScriptChiclet::setCounter(S32 counter)
1888{
1889	setShowNewMessagesIcon( counter > 0 );
1890}
1891
1892void LLScriptChiclet::onMouseDown()
1893{
1894	LLScriptFloaterManager::getInstance()->toggleScriptFloater(getSessionId());
1895}
1896
1897void LLScriptChiclet::onMenuItemClicked(const LLSD& user_data)
1898{
1899	std::string action = user_data.asString();
1900
1901	if("end" == action)
1902	{
1903		LLScriptFloaterManager::instance().removeNotification(getSessionId());
1904	}
1905}
1906
1907void LLScriptChiclet::createPopupMenu()
1908{
1909	if(!canCreateMenu())
1910		return;
1911
1912	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
1913	registrar.add("ScriptChiclet.Action", boost::bind(&LLScriptChiclet::onMenuItemClicked, this, _2));
1914
1915	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
1916		("menu_script_chiclet.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
1917}
1918
1919/////////////////////////////////////////////////…

Large files files are truncated, but you can click here to view the full file