PageRenderTime 185ms CodeModel.GetById 47ms app.highlight 121ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llui/llfloater.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2611 lines | 1989 code | 336 blank | 286 comment | 388 complexity | 0ec00b87906808552fa937704443b4eb MD5 | raw file
Possible License(s): LGPL-2.1
   1/** 
   2
   3 * @file llfloater.cpp
   4 * @brief LLFloater base class
   5 *
   6 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
   7 * Second Life Viewer Source Code
   8 * Copyright (C) 2010, Linden Research, Inc.
   9 * 
  10 * This library is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU Lesser General Public
  12 * License as published by the Free Software Foundation;
  13 * version 2.1 of the License only.
  14 * 
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 * 
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  23 * 
  24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  25 * $/LicenseInfo$
  26 */
  27
  28// Floating "windows" within the GL display, like the inventory floater,
  29// mini-map floater, etc.
  30
  31#include "linden_common.h"
  32
  33#include "llfloater.h"
  34
  35#include "llfocusmgr.h"
  36
  37#include "lluictrlfactory.h"
  38#include "llbutton.h"
  39#include "llcheckboxctrl.h"
  40#include "lldir.h"
  41#include "lldraghandle.h"
  42#include "llfloaterreg.h"
  43#include "llfocusmgr.h"
  44#include "llresizebar.h"
  45#include "llresizehandle.h"
  46#include "llkeyboard.h"
  47#include "llmenugl.h"	// MENU_BAR_HEIGHT
  48#include "llmodaldialog.h"
  49#include "lltextbox.h"
  50#include "llresmgr.h"
  51#include "llui.h"
  52#include "llwindow.h"
  53#include "llstl.h"
  54#include "llcontrol.h"
  55#include "lltabcontainer.h"
  56#include "v2math.h"
  57#include "lltrans.h"
  58#include "llhelp.h"
  59#include "llmultifloater.h"
  60#include "llsdutil.h"
  61#include <boost/foreach.hpp>
  62
  63
  64// use this to control "jumping" behavior when Ctrl-Tabbing
  65const S32 TABBED_FLOATER_OFFSET = 0;
  66
  67namespace LLInitParam
  68{
  69	void TypeValues<LLFloaterEnums::EOpenPositioning>::declareValues()
  70	{
  71		declare("none",       LLFloaterEnums::OPEN_POSITIONING_NONE);
  72		declare("cascading",  LLFloaterEnums::OPEN_POSITIONING_CASCADING);
  73		declare("centered",   LLFloaterEnums::OPEN_POSITIONING_CENTERED);
  74		declare("specified",  LLFloaterEnums::OPEN_POSITIONING_SPECIFIED);
  75	}
  76}
  77
  78std::string	LLFloater::sButtonNames[BUTTON_COUNT] = 
  79{
  80	"llfloater_close_btn",		//BUTTON_CLOSE
  81	"llfloater_restore_btn",	//BUTTON_RESTORE
  82	"llfloater_minimize_btn",	//BUTTON_MINIMIZE
  83	"llfloater_tear_off_btn",	//BUTTON_TEAR_OFF
  84	"llfloater_dock_btn",		//BUTTON_DOCK
  85	"llfloater_help_btn"		//BUTTON_HELP
  86};
  87
  88std::string LLFloater::sButtonToolTips[BUTTON_COUNT];
  89
  90std::string LLFloater::sButtonToolTipsIndex[BUTTON_COUNT]=
  91{
  92#ifdef LL_DARWIN
  93	"BUTTON_CLOSE_DARWIN",	//"Close (Cmd-W)",	//BUTTON_CLOSE
  94#else
  95	"BUTTON_CLOSE_WIN",		//"Close (Ctrl-W)",	//BUTTON_CLOSE
  96#endif
  97	"BUTTON_RESTORE",		//"Restore",	//BUTTON_RESTORE
  98	"BUTTON_MINIMIZE",		//"Minimize",	//BUTTON_MINIMIZE
  99	"BUTTON_TEAR_OFF",		//"Tear Off",	//BUTTON_TEAR_OFF
 100	"BUTTON_DOCK",
 101	"BUTTON_HELP"
 102};
 103
 104LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
 105{
 106	LLFloater::onClickClose,	//BUTTON_CLOSE
 107	LLFloater::onClickMinimize, //BUTTON_RESTORE
 108	LLFloater::onClickMinimize, //BUTTON_MINIMIZE
 109	LLFloater::onClickTearOff,	//BUTTON_TEAR_OFF
 110	LLFloater::onClickDock,		//BUTTON_DOCK
 111	LLFloater::onClickHelp		//BUTTON_HELP
 112};
 113
 114LLMultiFloater* LLFloater::sHostp = NULL;
 115BOOL			LLFloater::sQuitting = FALSE; // Flag to prevent storing visibility controls while quitting
 116
 117LLFloaterView* gFloaterView = NULL;
 118
 119/*==========================================================================*|
 120// DEV-38598: The fundamental problem with this operation is that it can only
 121// support a subset of LLSD values. While it's plausible to compare two arrays
 122// lexicographically, what strict ordering can you impose on maps?
 123// (LLFloaterTOS's current key is an LLSD map.)
 124
 125// Of course something like this is necessary if you want to build a std::set
 126// or std::map with LLSD keys. Fortunately we're getting by with other
 127// container types for now.
 128
 129//static
 130bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b)
 131{
 132	if (a.type() != b.type())
 133	{
 134		//llerrs << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << llendl;
 135		return false;
 136	}
 137	else if (a.isUndefined())
 138		return false;
 139	else if (a.isInteger())
 140		return a.asInteger() < b.asInteger();
 141	else if (a.isReal())
 142		return a.asReal() < b.asReal();
 143	else if (a.isString())
 144		return a.asString() < b.asString();
 145	else if (a.isUUID())
 146		return a.asUUID() < b.asUUID();
 147	else if (a.isDate())
 148		return a.asDate() < b.asDate();
 149	else if (a.isURI())
 150		return a.asString() < b.asString(); // compare URIs as strings
 151	else if (a.isBoolean())
 152		return a.asBoolean() < b.asBoolean();
 153	else
 154		return false; // no valid operation for Binary
 155}
 156|*==========================================================================*/
 157
 158bool LLFloater::KeyCompare::equate(const LLSD& a, const LLSD& b)
 159{
 160	return llsd_equals(a, b);
 161}
 162
 163//************************************
 164
 165LLFloater::Params::Params()
 166:	title("title"),
 167	short_title("short_title"),
 168	single_instance("single_instance", false),
 169	reuse_instance("reuse_instance", false),
 170	can_resize("can_resize", false),
 171	can_minimize("can_minimize", true),
 172	can_close("can_close", true),
 173	can_drag_on_left("can_drag_on_left", false),
 174	can_tear_off("can_tear_off", true),
 175	save_dock_state("save_dock_state", false),
 176	save_rect("save_rect", false),
 177	save_visibility("save_visibility", false),
 178	can_dock("can_dock", false),
 179	show_title("show_title", true),
 180	open_positioning("open_positioning", LLFloaterEnums::OPEN_POSITIONING_NONE),
 181	specified_left("specified_left"),
 182	specified_bottom("specified_bottom"),
 183	header_height("header_height", 0),
 184	legacy_header_height("legacy_header_height", 0),
 185	close_image("close_image"),
 186	restore_image("restore_image"),
 187	minimize_image("minimize_image"),
 188	tear_off_image("tear_off_image"),
 189	dock_image("dock_image"),
 190	help_image("help_image"),
 191	close_pressed_image("close_pressed_image"),
 192	restore_pressed_image("restore_pressed_image"),
 193	minimize_pressed_image("minimize_pressed_image"),
 194	tear_off_pressed_image("tear_off_pressed_image"),
 195	dock_pressed_image("dock_pressed_image"),
 196	help_pressed_image("help_pressed_image"),
 197	open_callback("open_callback"),
 198	close_callback("close_callback"),
 199	follows("follows")
 200{
 201	changeDefault(visible, false);
 202}
 203
 204
 205//static 
 206const LLFloater::Params& LLFloater::getDefaultParams()
 207{
 208	return LLUICtrlFactory::getDefaultParams<LLFloater>();
 209}
 210
 211//static
 212void LLFloater::initClass()
 213{
 214	// translate tooltips for floater buttons
 215	for (S32 i = 0; i < BUTTON_COUNT; i++)
 216	{
 217		sButtonToolTips[i] = LLTrans::getString( sButtonToolTipsIndex[i] );
 218	}
 219
 220	LLControlVariable* ctrl = LLUI::sSettingGroups["config"]->getControl("ActiveFloaterTransparency").get();
 221	if (ctrl)
 222	{
 223		ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency));
 224		updateActiveFloaterTransparency();
 225	}
 226
 227	ctrl = LLUI::sSettingGroups["config"]->getControl("InactiveFloaterTransparency").get();
 228	if (ctrl)
 229	{
 230		ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency));
 231		updateInactiveFloaterTransparency();
 232	}
 233
 234}
 235
 236// defaults for floater param block pulled from widgets/floater.xml
 237static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFloater::Params), "floater");
 238
 239LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
 240:	LLPanel(),	// intentionally do not pass params here, see initFromParams
 241 	mDragHandle(NULL),
 242	mTitle(p.title),
 243	mShortTitle(p.short_title),
 244	mSingleInstance(p.single_instance),
 245	mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default
 246	mKey(key),
 247	mCanTearOff(p.can_tear_off),
 248	mCanMinimize(p.can_minimize),
 249	mCanClose(p.can_close),
 250	mDragOnLeft(p.can_drag_on_left),
 251	mResizable(p.can_resize),
 252	mOpenPositioning(p.open_positioning),
 253	mSpecifiedLeft(p.specified_left),
 254	mSpecifiedBottom(p.specified_bottom),
 255	mMinWidth(p.min_width),
 256	mMinHeight(p.min_height),
 257	mHeaderHeight(p.header_height),
 258	mLegacyHeaderHeight(p.legacy_header_height),
 259	mMinimized(FALSE),
 260	mForeground(FALSE),
 261	mFirstLook(TRUE),
 262	mButtonScale(1.0f),
 263	mAutoFocus(TRUE), // automatically take focus when opened
 264	mCanDock(false),
 265	mDocked(false),
 266	mTornOff(false),
 267	mHasBeenDraggedWhileMinimized(FALSE),
 268	mPreviousMinimizedBottom(0),
 269	mPreviousMinimizedLeft(0),
 270	mMinimizeSignal(NULL)
 271//	mNotificationContext(NULL)
 272{
 273//	mNotificationContext = new LLFloaterNotificationContext(getHandle());
 274
 275	// Clicks stop here.
 276	setMouseOpaque(TRUE);
 277	
 278	// Floaters always draw their background, unlike every other panel.
 279	setBackgroundVisible(TRUE);
 280
 281	// Floaters start not minimized.  When minimized, they save their
 282	// prior rectangle to be used on restore.
 283	mExpandedRect.set(0,0,0,0);
 284	
 285	memset(mButtonsEnabled, 0, BUTTON_COUNT * sizeof(bool));
 286	memset(mButtons, 0, BUTTON_COUNT * sizeof(LLButton*));
 287	
 288	addDragHandle();
 289	addResizeCtrls();
 290	
 291	initFromParams(p);
 292	
 293	initFloater(p);
 294}
 295
 296// Note: Floaters constructed from XML call init() twice!
 297void LLFloater::initFloater(const Params& p)
 298{
 299	// Close button.
 300	if (mCanClose)
 301	{
 302		mButtonsEnabled[BUTTON_CLOSE] = TRUE;
 303	}
 304
 305	// Help button: '?'
 306	if ( !mHelpTopic.empty() )
 307	{
 308		mButtonsEnabled[BUTTON_HELP] = TRUE;
 309	}
 310
 311	// Minimize button only for top draggers
 312	if ( !mDragOnLeft && mCanMinimize )
 313	{
 314		mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
 315	}
 316
 317	if(mCanDock)
 318	{
 319		mButtonsEnabled[BUTTON_DOCK] = TRUE;
 320	}
 321
 322	buildButtons(p);
 323
 324	// Floaters are created in the invisible state	
 325	setVisible(FALSE);
 326
 327	if (!getParent())
 328	{
 329		gFloaterView->addChild(this);
 330	}
 331}
 332
 333void LLFloater::addDragHandle()
 334{
 335	if (!mDragHandle)
 336	{
 337		if (mDragOnLeft)
 338		{
 339			LLDragHandleLeft::Params p;
 340			p.name("drag");
 341			p.follows.flags(FOLLOWS_ALL);
 342			p.label(mTitle);
 343			mDragHandle = LLUICtrlFactory::create<LLDragHandleLeft>(p);
 344		}
 345		else // drag on top
 346		{
 347			LLDragHandleTop::Params p;
 348			p.name("Drag Handle");
 349			p.follows.flags(FOLLOWS_ALL);
 350			p.label(mTitle);
 351			mDragHandle = LLUICtrlFactory::create<LLDragHandleTop>(p);
 352		}
 353		addChild(mDragHandle);
 354	}
 355	layoutDragHandle();
 356	applyTitle();
 357}
 358
 359void LLFloater::layoutDragHandle()
 360{
 361	static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
 362	S32 close_box_size = mCanClose ? floater_close_box_size : 0;
 363	
 364	LLRect rect;
 365	if (mDragOnLeft)
 366	{
 367		rect.setLeftTopAndSize(0, 0, DRAG_HANDLE_WIDTH, getRect().getHeight() - LLPANEL_BORDER_WIDTH - close_box_size);
 368	}
 369	else // drag on top
 370	{
 371		rect = getLocalRect();
 372	}
 373	mDragHandle->setShape(rect);
 374	updateTitleButtons();
 375}
 376
 377// static
 378void LLFloater::updateActiveFloaterTransparency()
 379{
 380	sActiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency");
 381}
 382
 383// static
 384void LLFloater::updateInactiveFloaterTransparency()
 385{
 386	sInactiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency");
 387}
 388
 389void LLFloater::addResizeCtrls()
 390{	
 391	// Resize bars (sides)
 392	LLResizeBar::Params p;
 393	p.name("resizebar_left");
 394	p.resizing_view(this);
 395	p.min_size(mMinWidth);
 396	p.side(LLResizeBar::LEFT);
 397	mResizeBar[LLResizeBar::LEFT] = LLUICtrlFactory::create<LLResizeBar>(p);
 398	addChild( mResizeBar[LLResizeBar::LEFT] );
 399
 400	p.name("resizebar_top");
 401	p.min_size(mMinHeight);
 402	p.side(LLResizeBar::TOP);
 403
 404	mResizeBar[LLResizeBar::TOP] = LLUICtrlFactory::create<LLResizeBar>(p);
 405	addChild( mResizeBar[LLResizeBar::TOP] );
 406
 407	p.name("resizebar_right");
 408	p.min_size(mMinWidth);
 409	p.side(LLResizeBar::RIGHT);	
 410	mResizeBar[LLResizeBar::RIGHT] = LLUICtrlFactory::create<LLResizeBar>(p);
 411	addChild( mResizeBar[LLResizeBar::RIGHT] );
 412
 413	p.name("resizebar_bottom");
 414	p.min_size(mMinHeight);
 415	p.side(LLResizeBar::BOTTOM);
 416	mResizeBar[LLResizeBar::BOTTOM] = LLUICtrlFactory::create<LLResizeBar>(p);
 417	addChild( mResizeBar[LLResizeBar::BOTTOM] );
 418
 419	// Resize handles (corners)
 420	LLResizeHandle::Params handle_p;
 421	// handles must not be mouse-opaque, otherwise they block hover events
 422	// to other buttons like the close box. JC
 423	handle_p.mouse_opaque(false);
 424	handle_p.min_width(mMinWidth);
 425	handle_p.min_height(mMinHeight);
 426	handle_p.corner(LLResizeHandle::RIGHT_BOTTOM);
 427	mResizeHandle[0] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
 428	addChild(mResizeHandle[0]);
 429
 430	handle_p.corner(LLResizeHandle::RIGHT_TOP);
 431	mResizeHandle[1] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
 432	addChild(mResizeHandle[1]);
 433	
 434	handle_p.corner(LLResizeHandle::LEFT_BOTTOM);
 435	mResizeHandle[2] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
 436	addChild(mResizeHandle[2]);
 437
 438	handle_p.corner(LLResizeHandle::LEFT_TOP);
 439	mResizeHandle[3] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
 440	addChild(mResizeHandle[3]);
 441
 442	layoutResizeCtrls();
 443}
 444
 445void LLFloater::layoutResizeCtrls()
 446{
 447	LLRect rect;
 448
 449	// Resize bars (sides)
 450	const S32 RESIZE_BAR_THICKNESS = 3;
 451	rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0);
 452	mResizeBar[LLResizeBar::LEFT]->setRect(rect);
 453
 454	rect = LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS);
 455	mResizeBar[LLResizeBar::TOP]->setRect(rect);
 456
 457	rect = LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0);
 458	mResizeBar[LLResizeBar::RIGHT]->setRect(rect);
 459
 460	rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0);
 461	mResizeBar[LLResizeBar::BOTTOM]->setRect(rect);
 462
 463	// Resize handles (corners)
 464	rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0);
 465	mResizeHandle[0]->setRect(rect);
 466
 467	rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT);
 468	mResizeHandle[1]->setRect(rect);
 469	
 470	rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 );
 471	mResizeHandle[2]->setRect(rect);
 472
 473	rect = LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT );
 474	mResizeHandle[3]->setRect(rect);
 475}
 476
 477void LLFloater::enableResizeCtrls(bool enable, bool width, bool height)
 478{
 479	mResizeBar[LLResizeBar::LEFT]->setVisible(enable && width);
 480	mResizeBar[LLResizeBar::LEFT]->setEnabled(enable && width);
 481
 482	mResizeBar[LLResizeBar::TOP]->setVisible(enable && height);
 483	mResizeBar[LLResizeBar::TOP]->setEnabled(enable && height);
 484	
 485	mResizeBar[LLResizeBar::RIGHT]->setVisible(enable && width);
 486	mResizeBar[LLResizeBar::RIGHT]->setEnabled(enable && width);
 487	
 488	mResizeBar[LLResizeBar::BOTTOM]->setVisible(enable && height);
 489	mResizeBar[LLResizeBar::BOTTOM]->setEnabled(enable && height);
 490
 491	for (S32 i = 0; i < 4; ++i)
 492	{
 493		mResizeHandle[i]->setVisible(enable && width && height);
 494		mResizeHandle[i]->setEnabled(enable && width && height);
 495	}
 496}
 497
 498void LLFloater::destroy()
 499{
 500	// LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before
 501	// it was deleted via LLMortician::updateClass(). See EXT-8458.
 502	LLFloaterReg::removeInstance(mInstanceName, mKey);
 503	die();
 504}
 505
 506// virtual
 507LLFloater::~LLFloater()
 508{
 509	LLFloaterReg::removeInstance(mInstanceName, mKey);
 510	
 511//	delete mNotificationContext;
 512//	mNotificationContext = NULL;
 513
 514	//// am I not hosted by another floater?
 515	//if (mHostHandle.isDead())
 516	//{
 517	//	LLFloaterView* parent = (LLFloaterView*) getParent();
 518
 519	//	if( parent )
 520	//	{
 521	//		parent->removeChild( this );
 522	//	}
 523	//}
 524
 525	// Just in case we might still have focus here, release it.
 526	releaseFocus();
 527
 528	// This is important so that floaters with persistent rects (i.e., those
 529	// created with rect control rather than an LLRect) are restored in their
 530	// correct, non-minimized positions.
 531	setMinimized( FALSE );
 532
 533	delete mDragHandle;
 534	for (S32 i = 0; i < 4; i++) 
 535	{
 536		delete mResizeBar[i];
 537		delete mResizeHandle[i];
 538	}
 539
 540	setVisible(false); // We're not visible if we're destroyed
 541	storeVisibilityControl();
 542	storeDockStateControl();
 543
 544	delete mMinimizeSignal;
 545}
 546
 547void LLFloater::storeRectControl()
 548{
 549	if( mRectControl.size() > 1 )
 550	{
 551		getControlGroup()->setRect( mRectControl, getRect() );
 552	}
 553}
 554
 555void LLFloater::storeVisibilityControl()
 556{
 557	if( !sQuitting && mVisibilityControl.size() > 1 )
 558	{
 559		getControlGroup()->setBOOL( mVisibilityControl, getVisible() );
 560	}
 561}
 562
 563void LLFloater::storeDockStateControl()
 564{
 565	if( !sQuitting && mDocStateControl.size() > 1 )
 566	{
 567		getControlGroup()->setBOOL( mDocStateControl, isDocked() );
 568	}
 569}
 570
 571LLRect LLFloater::getSavedRect() const
 572{
 573	LLRect rect;
 574
 575	if (mRectControl.size() > 1)
 576	{
 577		rect = getControlGroup()->getRect(mRectControl);
 578	}
 579
 580	return rect;
 581}
 582
 583bool LLFloater::hasSavedRect() const
 584{
 585	return !getSavedRect().isEmpty();
 586}
 587
 588// static
 589std::string LLFloater::getControlName(const std::string& name, const LLSD& key)
 590{
 591	std::string ctrl_name = name;
 592
 593	// Add the key to the control name if appropriate.
 594	if (key.isString() && !key.asString().empty())
 595	{
 596		ctrl_name += "_" + key.asString();
 597	}
 598
 599	return ctrl_name;
 600}
 601
 602// static
 603LLControlGroup*	LLFloater::getControlGroup()
 604{
 605	// Floater size, position, visibility, etc are saved in per-account settings.
 606	return LLUI::sSettingGroups["account"];
 607}
 608
 609void LLFloater::setVisible( BOOL visible )
 610{
 611	LLPanel::setVisible(visible); // calls handleVisibilityChange()
 612	if( visible && mFirstLook )
 613	{
 614		mFirstLook = FALSE;
 615	}
 616
 617	if( !visible )
 618	{
 619		LLUI::removePopup(this);
 620
 621		if( gFocusMgr.childHasMouseCapture( this ) )
 622		{
 623			gFocusMgr.setMouseCapture(NULL);
 624		}
 625	}
 626
 627	for(handle_set_iter_t dependent_it = mDependents.begin();
 628		dependent_it != mDependents.end(); )
 629	{
 630		LLFloater* floaterp = dependent_it->get();
 631
 632		if (floaterp)
 633		{
 634			floaterp->setVisible(visible);
 635		}
 636		++dependent_it;
 637	}
 638
 639	storeVisibilityControl();
 640}
 641
 642// virtual
 643void LLFloater::handleVisibilityChange ( BOOL new_visibility )
 644{
 645	if (new_visibility)
 646	{
 647		if (getHost())
 648			getHost()->setFloaterFlashing(this, FALSE);
 649	}
 650	LLPanel::handleVisibilityChange ( new_visibility );
 651}
 652
 653void LLFloater::openFloater(const LLSD& key)
 654{
 655	llinfos << "Opening floater " << getName() << llendl;
 656	mKey = key; // in case we need to open ourselves again
 657	
 658	if (getSoundFlags() != SILENT 
 659	// don't play open sound for hosted (tabbed) windows
 660		&& !getHost() 
 661		&& !getFloaterHost()
 662		&& (!getVisible() || isMinimized()))
 663	{
 664		make_ui_sound("UISndWindowOpen");
 665	}
 666
 667	//RN: for now, we don't allow rehosting from one multifloater to another
 668	// just need to fix the bugs
 669	if (getFloaterHost() != NULL && getHost() == NULL)
 670	{
 671		// needs a host
 672		// only select tabs if window they are hosted in is visible
 673		getFloaterHost()->addFloater(this, getFloaterHost()->getVisible());
 674	}
 675
 676	if (getHost() != NULL)
 677	{
 678		getHost()->setMinimized(FALSE);
 679		getHost()->setVisibleAndFrontmost(mAutoFocus);
 680		getHost()->showFloater(this);
 681	}
 682	else
 683	{
 684		LLFloater* floater_to_stack = LLFloaterReg::getLastFloaterInGroup(mInstanceName);
 685		if (!floater_to_stack)
 686		{
 687			floater_to_stack = LLFloaterReg::getLastFloaterCascading();
 688		}
 689		applyControlsAndPosition(floater_to_stack);
 690		setMinimized(FALSE);
 691		setVisibleAndFrontmost(mAutoFocus);
 692	}
 693
 694	mOpenSignal(this, key);
 695	onOpen(key);
 696
 697	dirtyRect();
 698}
 699
 700void LLFloater::closeFloater(bool app_quitting)
 701{
 702	llinfos << "Closing floater " << getName() << llendl;
 703	if (app_quitting)
 704	{
 705		LLFloater::sQuitting = true;
 706	}
 707	
 708	// Always unminimize before trying to close.
 709	// Most of the time the user will never see this state.
 710	setMinimized(FALSE);
 711
 712	if (canClose())
 713	{
 714		if (getHost())
 715		{
 716			((LLMultiFloater*)getHost())->removeFloater(this);
 717			gFloaterView->addChild(this);
 718		}
 719
 720		if (getSoundFlags() != SILENT
 721			&& getVisible()
 722			&& !getHost()
 723			&& !app_quitting)
 724		{
 725			make_ui_sound("UISndWindowClose");
 726		}
 727
 728		// now close dependent floater
 729		for(handle_set_iter_t dependent_it = mDependents.begin();
 730			dependent_it != mDependents.end(); )
 731		{
 732			
 733			LLFloater* floaterp = dependent_it->get();
 734			if (floaterp)
 735			{
 736				++dependent_it;
 737				floaterp->closeFloater(app_quitting);
 738			}
 739			else
 740			{
 741				mDependents.erase(dependent_it++);
 742			}
 743		}
 744		
 745		cleanupHandles();
 746		gFocusMgr.clearLastFocusForGroup(this);
 747
 748		if (hasFocus())
 749		{
 750			// Do this early, so UI controls will commit before the
 751			// window is taken down.
 752			releaseFocus();
 753
 754			// give focus to dependee floater if it exists, and we had focus first
 755			if (isDependent())
 756			{
 757				LLFloater* dependee = mDependeeHandle.get();
 758				if (dependee && !dependee->isDead())
 759				{
 760					dependee->setFocus(TRUE);
 761				}
 762			}
 763		}
 764
 765		dirtyRect();
 766
 767		// Close callbacks
 768		onClose(app_quitting);
 769		mCloseSignal(this, LLSD(app_quitting));
 770		
 771		// Hide or Destroy
 772		if (mSingleInstance)
 773		{
 774			// Hide the instance
 775			if (getHost())
 776			{
 777				getHost()->setVisible(FALSE);
 778			}
 779			else
 780			{
 781				setVisible(FALSE);
 782				if (!mReuseInstance)
 783				{
 784					destroy();
 785				}
 786			}
 787		}
 788		else
 789		{
 790			setVisible(FALSE); // hide before destroying (so handleVisibilityChange() gets called)
 791			if (!mReuseInstance)
 792			{
 793				destroy();
 794			}
 795		}
 796	}
 797}
 798
 799/*virtual*/
 800void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent)
 801{
 802	LLPanel::reshape(width, height, called_from_parent);
 803}
 804
 805void LLFloater::releaseFocus()
 806{
 807	LLUI::removePopup(this);
 808
 809	setFocus(FALSE);
 810
 811	if( gFocusMgr.childHasMouseCapture( this ) )
 812	{
 813		gFocusMgr.setMouseCapture(NULL);
 814	}
 815}
 816
 817
 818void LLFloater::setResizeLimits( S32 min_width, S32 min_height )
 819{
 820	mMinWidth = min_width;
 821	mMinHeight = min_height;
 822
 823	for( S32 i = 0; i < 4; i++ )
 824	{
 825		if( mResizeBar[i] )
 826		{
 827			if (i == LLResizeBar::LEFT || i == LLResizeBar::RIGHT)
 828			{
 829				mResizeBar[i]->setResizeLimits( min_width, S32_MAX );
 830			}
 831			else
 832			{
 833				mResizeBar[i]->setResizeLimits( min_height, S32_MAX );
 834			}
 835		}
 836		if( mResizeHandle[i] )
 837		{
 838			mResizeHandle[i]->setResizeLimits( min_width, min_height );
 839		}
 840	}
 841}
 842
 843
 844void LLFloater::center()
 845{
 846	if(getHost())
 847	{
 848		// hosted floaters can't move
 849		return;
 850	}
 851	centerWithin(gFloaterView->getRect());
 852}
 853
 854LLMultiFloater* LLFloater::getHost()
 855{ 
 856	return (LLMultiFloater*)mHostHandle.get(); 
 857}
 858
 859void LLFloater::applyControlsAndPosition(LLFloater* other)
 860{
 861	if (!applyDockState())
 862	{
 863		if (!applyRectControl())
 864		{
 865			applyPositioning(other);
 866		}
 867	}
 868}
 869
 870bool LLFloater::applyRectControl()
 871{
 872	bool saved_rect = false;
 873
 874	LLFloater* last_in_group = LLFloaterReg::getLastFloaterInGroup(mInstanceName);
 875	if (last_in_group && last_in_group != this)
 876	{
 877		// other floaters in our group, position ourselves relative to them and don't save the rect
 878		mRectControl.clear();
 879		mOpenPositioning = LLFloaterEnums::OPEN_POSITIONING_CASCADE_GROUP;
 880	}
 881	else if (mRectControl.size() > 1)
 882	{
 883		// If we have a saved rect, use it
 884		const LLRect& rect = getControlGroup()->getRect(mRectControl);
 885		saved_rect = rect.notEmpty();
 886		if (saved_rect)
 887		{
 888			setOrigin(rect.mLeft, rect.mBottom);
 889
 890			if (mResizable)
 891			{
 892				reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
 893			}
 894		}
 895	}
 896
 897	return saved_rect;
 898}
 899
 900bool LLFloater::applyDockState()
 901{
 902	bool docked = false;
 903
 904	if (mDocStateControl.size() > 1)
 905	{
 906		docked = getControlGroup()->getBOOL(mDocStateControl);
 907		setDocked(docked);
 908	}
 909
 910	return docked;
 911}
 912
 913void LLFloater::applyPositioning(LLFloater* other)
 914{
 915	// Otherwise position according to the positioning code
 916	switch (mOpenPositioning)
 917	{
 918	case LLFloaterEnums::OPEN_POSITIONING_CENTERED:
 919		center();
 920		break;
 921
 922	case LLFloaterEnums::OPEN_POSITIONING_SPECIFIED:
 923		{
 924			// Translate relative to snap rect
 925			setOrigin(mSpecifiedLeft, mSpecifiedBottom);
 926			const LLRect& snap_rect = gFloaterView->getSnapRect();
 927			translate(snap_rect.mLeft, snap_rect.mBottom);
 928			translateIntoRect(snap_rect, FALSE);
 929		}
 930		break;
 931
 932	case LLFloaterEnums::OPEN_POSITIONING_CASCADE_GROUP:
 933	case LLFloaterEnums::OPEN_POSITIONING_CASCADING:
 934		if (other != NULL && other != this)
 935		{
 936			stackWith(*other);
 937		}
 938		else
 939		{
 940			static const U32 CASCADING_FLOATER_HOFFSET = 0;
 941			static const U32 CASCADING_FLOATER_VOFFSET = 0;
 942			
 943			const LLRect& snap_rect = gFloaterView->getSnapRect();
 944
 945			const S32 horizontal_offset = CASCADING_FLOATER_HOFFSET;
 946			const S32 vertical_offset = snap_rect.getHeight() - CASCADING_FLOATER_VOFFSET;
 947
 948			S32 rect_height = getRect().getHeight();
 949			setOrigin(horizontal_offset, vertical_offset - rect_height);
 950
 951			translate(snap_rect.mLeft, snap_rect.mBottom);
 952			translateIntoRect(snap_rect, FALSE);
 953		}
 954		break;
 955
 956	case LLFloaterEnums::OPEN_POSITIONING_NONE:
 957	default:
 958		// Do nothing
 959		break;
 960	}
 961}
 962
 963void LLFloater::applyTitle()
 964{
 965	if (!mDragHandle)
 966	{
 967		return;
 968	}
 969
 970	if (isMinimized() && !mShortTitle.empty())
 971	{
 972		mDragHandle->setTitle( mShortTitle );
 973	}
 974	else
 975	{
 976		mDragHandle->setTitle ( mTitle );
 977	}
 978
 979	if (getHost())
 980	{
 981		getHost()->updateFloaterTitle(this);	
 982	}
 983}
 984
 985std::string LLFloater::getCurrentTitle() const
 986{
 987	return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
 988}
 989
 990void LLFloater::setTitle( const std::string& title )
 991{
 992	mTitle = title;
 993	applyTitle();
 994}
 995
 996std::string LLFloater::getTitle() const
 997{
 998	if (mTitle.empty())
 999	{
1000		return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
1001	}
1002	else
1003	{
1004		return mTitle;
1005	}
1006}
1007
1008void LLFloater::setShortTitle( const std::string& short_title )
1009{
1010	mShortTitle = short_title;
1011	applyTitle();
1012}
1013
1014std::string LLFloater::getShortTitle() const
1015{
1016	if (mShortTitle.empty())
1017	{
1018		return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
1019	}
1020	else
1021	{
1022		return mShortTitle;
1023	}
1024}
1025
1026BOOL LLFloater::canSnapTo(const LLView* other_view)
1027{
1028	if (NULL == other_view)
1029	{
1030		llwarns << "other_view is NULL" << llendl;
1031		return FALSE;
1032	}
1033
1034	if (other_view != getParent())
1035	{
1036		const LLFloater* other_floaterp = dynamic_cast<const LLFloater*>(other_view);		
1037		if (other_floaterp 
1038			&& other_floaterp->getSnapTarget() == getHandle() 
1039			&& mDependents.find(other_floaterp->getHandle()) != mDependents.end())
1040		{
1041			// this is a dependent that is already snapped to us, so don't snap back to it
1042			return FALSE;
1043		}
1044	}
1045
1046	return LLPanel::canSnapTo(other_view);
1047}
1048
1049void LLFloater::setSnappedTo(const LLView* snap_view)
1050{
1051	if (!snap_view || snap_view == getParent())
1052	{
1053		clearSnapTarget();
1054	}
1055	else
1056	{
1057		//RN: assume it's a floater as it must be a sibling to our parent floater
1058		const LLFloater* floaterp = dynamic_cast<const LLFloater*>(snap_view);
1059		if (floaterp)
1060		{
1061			setSnapTarget(floaterp->getHandle());
1062		}
1063	}
1064}
1065
1066void LLFloater::handleReshape(const LLRect& new_rect, bool by_user)
1067{
1068	const LLRect old_rect = getRect();
1069	LLView::handleReshape(new_rect, by_user);
1070
1071	if (by_user && !isMinimized())
1072	{
1073		storeRectControl();
1074		mOpenPositioning = LLFloaterEnums::OPEN_POSITIONING_NONE;
1075	}
1076
1077	// if not minimized, adjust all snapped dependents to new shape
1078	if (!isMinimized())
1079	{
1080		// gather all snapped dependents
1081		for(handle_set_iter_t dependent_it = mDependents.begin();
1082			dependent_it != mDependents.end(); ++dependent_it)
1083		{
1084			LLFloater* floaterp = dependent_it->get();
1085			// is a dependent snapped to us?
1086			if (floaterp && floaterp->getSnapTarget() == getHandle())
1087			{
1088				S32 delta_x = 0;
1089				S32 delta_y = 0;
1090				// check to see if it snapped to right or top, and move if dependee floater is resizing
1091				LLRect dependent_rect = floaterp->getRect();
1092				if (dependent_rect.mLeft - getRect().mLeft >= old_rect.getWidth() || // dependent on my right?
1093					dependent_rect.mRight == getRect().mLeft + old_rect.getWidth()) // dependent aligned with my right
1094				{
1095					// was snapped directly onto right side or aligned with it
1096					delta_x += new_rect.getWidth() - old_rect.getWidth();
1097				}
1098				if (dependent_rect.mBottom - getRect().mBottom >= old_rect.getHeight() ||
1099					dependent_rect.mTop == getRect().mBottom + old_rect.getHeight())
1100				{
1101					// was snapped directly onto top side or aligned with it
1102					delta_y += new_rect.getHeight() - old_rect.getHeight();
1103				}
1104
1105				// take translation of dependee floater into account as well
1106				delta_x += new_rect.mLeft - old_rect.mLeft;
1107				delta_y += new_rect.mBottom - old_rect.mBottom;
1108
1109				dependent_rect.translate(delta_x, delta_y);
1110				floaterp->setShape(dependent_rect, by_user);
1111			}
1112		}
1113	}
1114	else
1115	{
1116		// If minimized, and origin has changed, set
1117		// mHasBeenDraggedWhileMinimized to TRUE
1118		if ((new_rect.mLeft != old_rect.mLeft) ||
1119			(new_rect.mBottom != old_rect.mBottom))
1120		{
1121			mHasBeenDraggedWhileMinimized = TRUE;
1122		}
1123	}
1124}
1125
1126void LLFloater::setMinimized(BOOL minimize)
1127{
1128	const LLFloater::Params& default_params = LLFloater::getDefaultParams();
1129	S32 floater_header_size = default_params.header_height;
1130	static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0);
1131
1132	if (minimize == mMinimized) return;
1133
1134	if (mMinimizeSignal)
1135	{
1136		(*mMinimizeSignal)(this, LLSD(minimize));
1137	}
1138
1139	if (minimize)
1140	{
1141		// minimized flag should be turned on before release focus
1142		mMinimized = TRUE;
1143
1144		mExpandedRect = getRect();
1145
1146		// If the floater has been dragged while minimized in the
1147		// past, then locate it at its previous minimized location.
1148		// Otherwise, ask the view for a minimize position.
1149		if (mHasBeenDraggedWhileMinimized)
1150		{
1151			setOrigin(mPreviousMinimizedLeft, mPreviousMinimizedBottom);
1152		}
1153		else
1154		{
1155			S32 left, bottom;
1156			gFloaterView->getMinimizePosition(&left, &bottom);
1157			setOrigin( left, bottom );
1158		}
1159
1160		if (mButtonsEnabled[BUTTON_MINIMIZE])
1161		{
1162			mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
1163			mButtonsEnabled[BUTTON_RESTORE] = TRUE;
1164		}
1165
1166		setBorderVisible(TRUE);
1167
1168		for(handle_set_iter_t dependent_it = mDependents.begin();
1169			dependent_it != mDependents.end();
1170			++dependent_it)
1171		{
1172			LLFloater* floaterp = dependent_it->get();
1173			if (floaterp)
1174			{
1175				if (floaterp->isMinimizeable())
1176				{
1177					floaterp->setMinimized(TRUE);
1178				}
1179				else if (!floaterp->isMinimized())
1180				{
1181					floaterp->setVisible(FALSE);
1182				}
1183			}
1184		}
1185
1186		// Lose keyboard focus when minimized
1187		releaseFocus();
1188
1189		for (S32 i = 0; i < 4; i++)
1190		{
1191			if (mResizeBar[i] != NULL)
1192			{
1193				mResizeBar[i]->setEnabled(FALSE);
1194			}
1195			if (mResizeHandle[i] != NULL)
1196			{
1197				mResizeHandle[i]->setEnabled(FALSE);
1198			}
1199		}
1200		
1201		// Reshape *after* setting mMinimized
1202		reshape( minimized_width, floater_header_size, TRUE);
1203	}
1204	else
1205	{
1206		// If this window has been dragged while minimized (at any time),
1207		// remember its position for the next time it's minimized.
1208		if (mHasBeenDraggedWhileMinimized)
1209		{
1210			const LLRect& currentRect = getRect();
1211			mPreviousMinimizedLeft = currentRect.mLeft;
1212			mPreviousMinimizedBottom = currentRect.mBottom;
1213		}
1214
1215		setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom );
1216
1217		if (mButtonsEnabled[BUTTON_RESTORE])
1218		{
1219			mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
1220			mButtonsEnabled[BUTTON_RESTORE] = FALSE;
1221		}
1222
1223		// show dependent floater
1224		for(handle_set_iter_t dependent_it = mDependents.begin();
1225			dependent_it != mDependents.end();
1226			++dependent_it)
1227		{
1228			LLFloater* floaterp = dependent_it->get();
1229			if (floaterp)
1230			{
1231				floaterp->setMinimized(FALSE);
1232				floaterp->setVisible(TRUE);
1233			}
1234		}
1235
1236		for (S32 i = 0; i < 4; i++)
1237		{
1238			if (mResizeBar[i] != NULL)
1239			{
1240				mResizeBar[i]->setEnabled(isResizable());
1241			}
1242			if (mResizeHandle[i] != NULL)
1243			{
1244				mResizeHandle[i]->setEnabled(isResizable());
1245			}
1246		}
1247		
1248		mMinimized = FALSE;
1249
1250		// Reshape *after* setting mMinimized
1251		reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE );
1252	}
1253
1254	make_ui_sound("UISndWindowClose");
1255	updateTitleButtons();
1256	applyTitle ();
1257}
1258
1259void LLFloater::setFocus( BOOL b )
1260{
1261	if (b && getIsChrome())
1262	{
1263		return;
1264	}
1265	LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this);
1266	// a descendent already has focus
1267	BOOL child_had_focus = hasFocus();
1268
1269	// give focus to first valid descendent
1270	LLPanel::setFocus(b);
1271
1272	if (b)
1273	{
1274		// only push focused floaters to front of stack if not in midst of ctrl-tab cycle
1275		if (!getHost() && !((LLFloaterView*)getParent())->getCycleMode())
1276		{
1277			if (!isFrontmost())
1278			{
1279				setFrontmost();
1280			}
1281		}
1282
1283		// when getting focus, delegate to last descendent which had focus
1284		if (last_focus && !child_had_focus && 
1285			last_focus->isInEnabledChain() &&
1286			last_focus->isInVisibleChain())
1287		{
1288			// *FIX: should handle case where focus doesn't stick
1289			last_focus->setFocus(TRUE);
1290		}
1291	}
1292	updateTransparency(b ? TT_ACTIVE : TT_INACTIVE);
1293}
1294
1295// virtual
1296void LLFloater::setRect(const LLRect &rect)
1297{
1298	LLPanel::setRect(rect);
1299	layoutDragHandle();
1300	layoutResizeCtrls();
1301}
1302
1303// virtual
1304void LLFloater::setIsChrome(BOOL is_chrome)
1305{
1306	// chrome floaters don't take focus at all
1307	if (is_chrome)
1308	{
1309		// remove focus if we're changing to chrome
1310		setFocus(FALSE);
1311		// can't Ctrl-Tab to "chrome" floaters
1312		setFocusRoot(FALSE);
1313		mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome)));
1314	}
1315	
1316	LLPanel::setIsChrome(is_chrome);
1317}
1318
1319// Change the draw style to account for the foreground state.
1320void LLFloater::setForeground(BOOL front)
1321{
1322	if (front != mForeground)
1323	{
1324		mForeground = front;
1325		if (mDragHandle)
1326			mDragHandle->setForeground( front );
1327
1328		if (!front)
1329		{
1330			releaseFocus();
1331		}
1332
1333		setBackgroundOpaque( front ); 
1334	}
1335}
1336
1337void LLFloater::cleanupHandles()
1338{
1339	// remove handles to non-existent dependents
1340	for(handle_set_iter_t dependent_it = mDependents.begin();
1341		dependent_it != mDependents.end(); )
1342	{
1343		LLFloater* floaterp = dependent_it->get();
1344		if (!floaterp)
1345		{
1346			mDependents.erase(dependent_it++);
1347		}
1348		else
1349		{
1350			++dependent_it;
1351		}
1352	}
1353}
1354
1355void LLFloater::setHost(LLMultiFloater* host)
1356{
1357	if (mHostHandle.isDead() && host)
1358	{
1359		// make buttons smaller for hosted windows to differentiate from parent
1360		mButtonScale = 0.9f;
1361
1362		// add tear off button
1363		if (mCanTearOff)
1364		{
1365			mButtonsEnabled[BUTTON_TEAR_OFF] = TRUE;
1366		}
1367	}
1368	else if (!mHostHandle.isDead() && !host)
1369	{
1370		mButtonScale = 1.f;
1371		//mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE;
1372	}
1373	updateTitleButtons();
1374	if (host)
1375	{
1376		mHostHandle = host->getHandle();
1377		mLastHostHandle = host->getHandle();
1378	}
1379	else
1380	{
1381		mHostHandle.markDead();
1382	}
1383}
1384
1385void LLFloater::moveResizeHandlesToFront()
1386{
1387	for( S32 i = 0; i < 4; i++ )
1388	{
1389		if( mResizeBar[i] )
1390		{
1391			sendChildToFront(mResizeBar[i]);
1392		}
1393	}
1394
1395	for( S32 i = 0; i < 4; i++ )
1396	{
1397		if( mResizeHandle[i] )
1398		{
1399			sendChildToFront(mResizeHandle[i]);
1400		}
1401	}
1402}
1403
1404BOOL LLFloater::isFrontmost()
1405{
1406	LLFloaterView* floater_view = getParentByType<LLFloaterView>();
1407	return getVisible()
1408			&& (floater_view 
1409				&& floater_view->getFrontmost() == this);
1410}
1411
1412void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
1413{
1414	mDependents.insert(floaterp->getHandle());
1415	floaterp->mDependeeHandle = getHandle();
1416
1417	if (reposition)
1418	{
1419		floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp));
1420		floaterp->setSnapTarget(getHandle());
1421	}
1422	gFloaterView->adjustToFitScreen(floaterp, FALSE);
1423	if (floaterp->isFrontmost())
1424	{
1425		// make sure to bring self and sibling floaters to front
1426		gFloaterView->bringToFront(floaterp);
1427	}
1428}
1429
1430void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, BOOL reposition)
1431{
1432	LLFloater* dependent_floaterp = dependent.get();
1433	if(dependent_floaterp)
1434	{
1435		addDependentFloater(dependent_floaterp, reposition);
1436	}
1437}
1438
1439void LLFloater::removeDependentFloater(LLFloater* floaterp)
1440{
1441	mDependents.erase(floaterp->getHandle());
1442	floaterp->mDependeeHandle = LLHandle<LLFloater>();
1443}
1444
1445BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index)
1446{
1447	if( mButtonsEnabled[index] )
1448	{
1449		LLButton* my_butt = mButtons[index];
1450		S32 local_x = x - my_butt->getRect().mLeft;
1451		S32 local_y = y - my_butt->getRect().mBottom;
1452
1453		if (
1454			my_butt->pointInView(local_x, local_y) &&
1455			my_butt->handleMouseDown(local_x, local_y, mask))
1456		{
1457			// the button handled it
1458			return TRUE;
1459		}
1460	}
1461	return FALSE;
1462}
1463
1464BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks)
1465{
1466	LLPanel::handleScrollWheel(x,y,clicks);
1467	return TRUE;//always
1468}
1469
1470// virtual
1471BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
1472{
1473	if( mMinimized )
1474	{
1475		// Offer the click to titlebar buttons.
1476		// Note: this block and the offerClickToButton helper method can be removed
1477		// because the parent container will handle it for us but we'll keep it here
1478		// for safety until after reworking the panel code to manage hidden children.
1479		if(offerClickToButton(x, y, mask, BUTTON_CLOSE)) return TRUE;
1480		if(offerClickToButton(x, y, mask, BUTTON_RESTORE)) return TRUE;
1481		if(offerClickToButton(x, y, mask, BUTTON_TEAR_OFF)) return TRUE;
1482		if(offerClickToButton(x, y, mask, BUTTON_DOCK)) return TRUE;
1483
1484		// Otherwise pass to drag handle for movement
1485		return mDragHandle->handleMouseDown(x, y, mask);
1486	}
1487	else
1488	{
1489		bringToFront( x, y );
1490		return LLPanel::handleMouseDown( x, y, mask );
1491	}
1492}
1493
1494// virtual
1495BOOL LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
1496{
1497	BOOL was_minimized = mMinimized;
1498	bringToFront( x, y );
1499	return was_minimized || LLPanel::handleRightMouseDown( x, y, mask );
1500}
1501
1502BOOL LLFloater::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
1503{
1504	bringToFront( x, y );
1505	return LLPanel::handleMiddleMouseDown( x, y, mask );
1506}
1507
1508
1509// virtual
1510BOOL LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)
1511{
1512	BOOL was_minimized = mMinimized;
1513	setMinimized(FALSE);
1514	return was_minimized || LLPanel::handleDoubleClick(x, y, mask);
1515}
1516
1517void LLFloater::bringToFront( S32 x, S32 y )
1518{
1519	if (getVisible() && pointInView(x, y))
1520	{
1521		LLMultiFloater* hostp = getHost();
1522		if (hostp)
1523		{
1524			hostp->showFloater(this);
1525		}
1526		else
1527		{
1528			LLFloaterView* parent = (LLFloaterView*) getParent();
1529			if (parent)
1530			{
1531				parent->bringToFront( this );
1532			}
1533		}
1534	}
1535}
1536
1537
1538// virtual
1539void LLFloater::setVisibleAndFrontmost(BOOL take_focus)
1540{
1541	setVisible(TRUE);
1542	setFrontmost(take_focus);
1543}
1544
1545void LLFloater::setFrontmost(BOOL take_focus)
1546{
1547	LLMultiFloater* hostp = getHost();
1548	if (hostp)
1549	{
1550		// this will bring the host floater to the front and select
1551		// the appropriate panel
1552		hostp->showFloater(this);
1553	}
1554	else
1555	{
1556		// there are more than one floater view
1557		// so we need to query our parent directly
1558		((LLFloaterView*)getParent())->bringToFront(this, take_focus);
1559
1560		// Make sure to set the appropriate transparency type (STORM-732).
1561		updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE);
1562	}
1563}
1564
1565void LLFloater::setCanDock(bool b)
1566{
1567	if(b != mCanDock)
1568	{
1569		mCanDock = b;
1570		if(mCanDock)
1571		{
1572			mButtonsEnabled[BUTTON_DOCK] = !mDocked;
1573		}
1574		else
1575		{
1576			mButtonsEnabled[BUTTON_DOCK] = FALSE;
1577		}
1578	}
1579	updateTitleButtons();
1580}
1581
1582void LLFloater::setDocked(bool docked, bool pop_on_undock)
1583{
1584	if(docked != mDocked && mCanDock)
1585	{
1586		mDocked = docked;
1587		mButtonsEnabled[BUTTON_DOCK] = !mDocked;
1588
1589		if (mDocked)
1590		{
1591			setMinimized(FALSE);
1592			mOpenPositioning = LLFloaterEnums::OPEN_POSITIONING_NONE;
1593		}
1594
1595		updateTitleButtons();
1596
1597		storeDockStateControl();
1598	}
1599	
1600}
1601
1602// static
1603void LLFloater::onClickMinimize(LLFloater* self)
1604{
1605	if (!self)
1606		return;
1607	self->setMinimized( !self->isMinimized() );
1608}
1609
1610void LLFloater::onClickTearOff(LLFloater* self)
1611{
1612	if (!self)
1613		return;
1614	S32 floater_header_size = self->mHeaderHeight;
1615	LLMultiFloater* host_floater = self->getHost();
1616	if (host_floater) //Tear off
1617	{
1618		LLRect new_rect;
1619		host_floater->removeFloater(self);
1620		// reparent to floater view
1621		gFloaterView->addChild(self);
1622
1623		self->openFloater(self->getKey());
1624		
1625		// only force position for floaters that don't have that data saved
1626		if (self->mRectControl.size() <= 1)
1627		{
1628			new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - floater_header_size - 5, self->getRect().getWidth(), self->getRect().getHeight());
1629			self->setRect(new_rect);
1630		}
1631		gFloaterView->adjustToFitScreen(self, FALSE);
1632		// give focus to new window to keep continuity for the user
1633		self->setFocus(TRUE);
1634		self->setTornOff(true);
1635	}
1636	else  //Attach to parent.
1637	{
1638		LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get();
1639		if (new_host)
1640		{
1641			self->setMinimized(FALSE); // to reenable minimize button if it was minimized
1642			new_host->showFloater(self);
1643			// make sure host is visible
1644			new_host->openFloater(new_host->getKey());
1645		}
1646		self->setTornOff(false);
1647	}
1648	self->updateTitleButtons();
1649}
1650
1651// static
1652void LLFloater::onClickDock(LLFloater* self)
1653{
1654	if(self && self->mCanDock)
1655	{
1656		self->setDocked(!self->mDocked, true);
1657	}
1658}
1659
1660// static
1661void LLFloater::onClickHelp( LLFloater* self )
1662{
1663	if (self && LLUI::sHelpImpl)
1664	{
1665		// find the current help context for this floater
1666		std::string help_topic;
1667		if (self->findHelpTopic(help_topic))
1668		{
1669			LLUI::sHelpImpl->showTopic(help_topic);
1670		}
1671	}
1672}
1673
1674// static 
1675LLFloater* LLFloater::getClosableFloaterFromFocus()
1676{
1677	LLFloater* focused_floater = NULL;
1678	LLInstanceTracker<LLFloater>::instance_iter it = beginInstances();
1679	LLInstanceTracker<LLFloater>::instance_iter end_it = endInstances();
1680	for (; it != end_it; ++it)
1681	{
1682		if (it->hasFocus())
1683		{
1684			break;
1685		}
1686	}
1687
1688	if (it == endInstances())
1689	{
1690		// nothing found, return
1691		return NULL;
1692	}
1693
1694	// The focused floater may not be closable,
1695	// Find and close a parental floater that is closeable, if any.
1696	LLFloater* prev_floater = NULL;
1697	for(LLFloater* floater_to_close = focused_floater;
1698		NULL != floater_to_close; 
1699		floater_to_close = gFloaterView->getParentFloater(floater_to_close))
1700	{
1701		if(floater_to_close->isCloseable())
1702		{
1703			return floater_to_close;
1704		}
1705
1706		// If floater has as parent root view
1707		// gFloaterView->getParentFloater(floater_to_close) returns
1708		// the same floater_to_close, so we need to check this.
1709		if (prev_floater == floater_to_close) {
1710			break;
1711		}
1712		prev_floater = floater_to_close;
1713	}
1714
1715	return NULL;
1716}
1717
1718// static
1719void LLFloater::closeFocusedFloater()
1720{
1721	LLFloater* floater_to_close = LLFloater::getClosableFloaterFromFocus();
1722	if(floater_to_close)
1723	{
1724		floater_to_close->closeFloater();
1725	}
1726
1727	// if nothing took focus after closing focused floater
1728	// give it to next floater (to allow closing multiple windows via keyboard in rapid succession)
1729	if (gFocusMgr.getKeyboardFocus() == NULL)
1730	{
1731		// HACK: use gFloaterView directly in case we are using Ctrl-W to close snapshot window
1732		// which sits in gSnapshotFloaterView, and needs to pass focus on to normal floater view
1733		gFloaterView->focusFrontFloater();
1734	}
1735}
1736
1737
1738// static
1739void LLFloater::onClickClose( LLFloater* self )
1740{
1741	if (!self)
1742		return;
1743	self->onClickCloseBtn();
1744}
1745
1746void	LLFloater::onClickCloseBtn()
1747{
1748	closeFloater(false);
1749}
1750
1751
1752// virtual
1753void LLFloater::draw()
1754{
1755	const F32 alpha = getCurrentTransparency();
1756
1757	// draw background
1758	if( isBackgroundVisible() )
1759	{
1760		drawShadow(this);
1761
1762		S32 left = LLPANEL_BORDER_WIDTH;
1763		S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH;
1764		S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH;
1765		S32 bottom = LLPANEL_BORDER_WIDTH;
1766
1767		LLUIImage* image = NULL;
1768		LLColor4 color;
1769		LLColor4 overlay_color;
1770		if (isBackgroundOpaque())
1771		{
1772			// NOTE: image may not be set
1773			image = getBackgroundImage();
1774			color = getBackgroundColor();
1775			overlay_color = getBackgroundImageOverlay();
1776		}
1777		else
1778		{
1779			image = getTransparentImage();
1780			color = getTransparentColor();
1781			overlay_color = getTransparentImageOverlay();
1782		}
1783
1784		if (image)
1785		{
1786			// We're using images for this floater's backgrounds
1787			image->draw(getLocalRect(), overlay_color % alpha);
1788		}
1789		else
1790		{
1791			// We're not using images, use old-school flat colors
1792			gl_rect_2d( left, top, right, bottom, color % alpha );
1793
1794			// draw highlight on title bar to indicate focus.  RDW
1795			if(hasFocus() 
1796				&& !getIsChrome() 
1797				&& !getCurrentTitle().empty())
1798			{
1799				static LLUIColor titlebar_focus_color = LLUIColorTable::instance().getColor("TitleBarFocusColor");
1800				
1801				const LLFontGL* font = LLFontGL::getFontSansSerif();
1802				LLRect r = getRect();
1803				gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, 
1804					titlebar_focus_color % alpha, 0, TRUE);
1805			}
1806		}
1807	}
1808
1809	LLPanel::updateDefaultBtn();
1810
1811	if( getDefaultButton() )
1812	{
1813		if (hasFocus() && getDefaultButton()->getEnabled())
1814		{
1815			LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus();
1816			// is this button a direct descendent and not a nested widget (e.g. checkbox)?
1817			BOOL focus_is_child_button = dynamic_cast<LLButton*>(focus_ctrl) != NULL && dynamic_cast<LLButton*>(focus_ctrl)->getParent() == this;
1818			// only enable default button when current focus is not a button
1819			getDefaultButton()->setBorderEnabled(!focus_is_child_button);
1820		}
1821		else
1822		{
1823			getDefaultButton()->setBorderEnabled(FALSE);
1824		}
1825	}
1826	if (isMinimized())
1827	{
1828		for (S32 i = 0; i < BUTTON_COUNT; i++)
1829		{
1830			drawChild(mButtons[i]);
1831		}
1832		drawChild(mDragHandle, 0, 0, TRUE);
1833	}
1834	else
1835	{
1836		// don't call LLPanel::draw() since we've implemented custom background rendering
1837		LLView::draw();
1838	}
1839
1840	// update tearoff button for torn off floaters
1841	// when last host goes away
1842	if (mCanTearOff && !getHost())
1843	{
1844		LLFloater* old_host = mLastHostHandle.get();
1845		if (!old_host)
1846		{
1847			setCanTearOff(FALSE);
1848		}
1849	}
1850}
1851
1852void	LLFloater::drawShadow(LLPanel* panel)
1853{
1854	S32 left = LLPANEL_BORDER_WIDTH;
1855	S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH;
1856	S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH;
1857	S32 bottom = LLPANEL_BORDER_WIDTH;
1858
1859	static LLUICachedControl<S32> shadow_offset_S32 ("DropShadowFloater", 0);
1860	static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow");
1861	LLColor4 shadow_color = shadow_color_cached;
1862	F32 shadow_offset = (F32)shadow_offset_S32;
1863
1864	if (!panel->isBackgroundOpaque())
1865	{
1866		shadow_offset *= 0.2f;
1867		shadow_color.mV[VALPHA] *= 0.5f;
1868	}
1869	gl_drop_shadow(left, top, right, bottom, 
1870		shadow_color % getCurrentTransparency(),
1871		llround(shadow_offset));
1872}
1873
1874void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type)
1875{
1876	child_list_t children = *view->getChildList();
1877	child_list_t::iterator it = children.begin();
1878
1879	LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(view);
1880	if (ctrl)
1881	{
1882		ctrl->setTransparencyType(transparency_type);
1883	}
1884
1885	for(; it != children.end(); ++it)
1886	{
1887		updateTransparency(*it, transparency_type);
1888	}
1889}
1890
1891void LLFloater::updateTransparency(ETypeTransparency transparency_type)
1892{
1893	updateTransparency(this, transparency_type);
1894}
1895
1896void	LLFloater::setCanMinimize(BOOL can_minimize)
1897{
1898	// if removing minimize/restore button programmatically,
1899	// go ahead and unminimize floater
1900	mCanMinimize = can_minimize;
1901	if (!can_minimize)
1902	{
1903		setMinimized(FALSE);
1904	}
1905
1906	mButtonsEnabled[BUTTON_MINIMIZE] = can_minimize && !isMinimized();
1907	mButtonsEnabled[BUTTON_RESTORE]  = can_minimize &&  isMinimized();
1908
1909	updateTitleButtons();
1910}
1911
1912void	LLFloater::setCanClose(BOOL can_close)
1913{
1914	mCanClose = can_close;
1915	mButtonsEnabled[BUTTON_CLOSE] = can_close;
1916
1917	updateTitleButtons();
1918}
1919
1920void	LLFloater::setCanTearOff(BOOL can_tear_off)
1921{
1922	mCanTearOff = can_tear_off;
1923	mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead();
1924
1925	updateTitleButtons();
1926}
1927
1928
1929void LLFloater::setCanResize(BOOL can_resize)
1930{
1931	mResizable = can_resize;
1932	enableResizeCtrls(can_resize);
1933}
1934
1935void LLFloater::setCanDrag(BOOL can_drag)
1936{
1937	// if we delete drag handle, we no longer have access to the floater's title
1938	// so just enable/disable it
1939	if (!can_drag && mDragHandle->getEnabled())
1940	{
1941		mDragHandle->setEnabled(FALSE);
1942	}
1943	else if (can_drag && !mDragHandle->getEnabled())
1944	{
1945		mDragHandle->setEnabled(TRUE);
1946	}
1947}
1948
1949bool LLFloater::getCanDrag()
1950{
1951	return mDragHandle->getEnabled();
1952}
1953
1954
1955void LLFloater::updateTitleButtons()
1956{
1957	static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
1958	static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0);
1959	LLRect buttons_rect;
1960	S32 button_count = 0;
1961	for (S32 i = 0; i < BUTTON_COUNT; i++)
1962	{
1963		if (!mButtons[i])
1964		{
1965			continue;
1966		}
1967
1968		bool enabled = mButtonsEnabled[i];
1969		if (i == BUTTON_HELP)
1970		{
1971			// don't show the help button if the floater is minimized
1972			// or if it is a docked tear-off floater
1973			if (isMinimized() || (mButtonsEnabled[BUTTON_TEAR_OFF] && ! mTornOff))
1974			{
1975				enabled = false;
1976			}
1977		}
1978		if (i == BUTTON_CLOSE && mButtonScale != 1.f)
1979		{
1980			//*HACK: always render close button for hosted floaters so
1981			//that users don't accidentally hit the button when
1982			//closing multiple windows in the chatterbox
1983			enabled = true;
1984		}
1985
1986		mButtons[i]->setEnabled(enabled);
1987
1988		if (enabled)
1989		{
1990			button_count++;
1991
1992			LLRect btn_rect;
1993			if (mDragOnLeft)
1994			{
1995				btn_rect.setLeftTopAndSize(
1996					LLPANEL_BORDER_WIDTH,
1997					getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * button_count,
1998					llround((F32)floater_close_box_size * mButtonScale),
1999					llround((F32)floater_close_box_size * mButtonScale));
2000			}
2001			else
2002			{
2003				btn_rect.setLeftTopAndSize(
2004					getRect().getWidth() - LLPANEL_BORDER_WIDTH - (floater_close_box_size + 1) * button_count,
2005					getRect().getHeight() - close_box_from_top,
2006					llround((F32)floater_close_box_size * mButtonScale),
2007					llround((F32)floater_close_box_size * mButtonScale));
2008			}
2009
2010			// first time here, init 'buttons_rect'
2011			if(1 == button_count)
2012			{
2013				buttons_rect = btn_rect;
2014			}
2015			else
2016			{
2017				// if mDragOnLeft=true then buttons are on top-left side vertically aligned
2018				// title is not displayed in this case, calculating 'buttons_rect' for future use
2019				mDragOnLeft ? buttons_rect.mBottom -= btn_rect.mBottom : 
2020					buttons_rect.mLeft = btn_rect.mLeft;
2021			}
2022			mButtons[i]->setRect(btn_rect);
2023			mButtons[i]->setVisible(TRUE);
2024			// the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater
2025			mButtons[i]->setTabStop(i == BUTTON_RESTORE);
2026		}
2027		else
2028		{
2029			mButtons[i]->setVisible(FALSE);
2030		}
2031	}
2032	if (mDragHandle)
2033	{
2034		localRectToOtherView(buttons_rect, &buttons_rect, mDragHandle);
2035		mDragHandle->setButtonsRect(buttons_rect);
2036	}
2037}
2038
2039void LLFloater::buildButtons(const Params& floater_params)
2040{
2041	static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
2042	static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0);
2043	for (S32 i = 0; i < BUTTON_COUNT; i++)
2044	{
2045		if (mButtons[i])
2046		{
2047			removeChild(mButtons[i]);
2048			delete mButtons[i];
2049			mButtons[i] = NULL;
2050		}
2051		
2052		LLRect btn_rect;
2053		if (mDragOnLeft)
2054		{
2055			btn_rect.setLeftTopAndSize(
2056				LLPANEL_BORDER_WIDTH,
2057				getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * (i + 1),
2058				llround(floater_close_box_size * mButtonScale),
2059				llround(floater_close_box_size * mButtonScale));
2060		}
2061		else
2062		{
2063			btn_rect.setLeftTopAndSize(
2064				getRect().getWidth() - LLPANEL_BORDER_WIDTH - (floater_close_box_size + 1) * (i + 1),
2065				getRect().getHeight() - close_box_from_top,
2066				llround(floater_close_box_size * mButtonScale),
2067				llround(floater_close_box_size * mButtonScale));
2068		}
2069
2070		LLButton::Params p;
2071		p.name(sButtonNames[i]);
2072		p.rect(btn_rect);
2073		p.image_unselected = getButtonImage(floater_params, (EFloaterButton)i);
2074		// Selected, no matter if hovered or not, is "pressed"
2075		LLUIImage* pressed_image = getButtonPressedImage(floater_params, (EFloaterButton)i);
2076		p.image_selected = pressed_image;
2077		p.image_hover_selected = pressed_image;
2078		// Use a glow effect when the user hovers over the button
2079		// These icons are really small, need glow amount increased
2080		p.hover_glow_amount( 0.33f );
2081		p.click_callback.function(boost::bind(sButtonCallbacks[i], this));
2082		p.tab_stop(false);
2083		p.follows.flags(FOLLOWS_TOP|FOLLOWS_RIGHT);
2084		p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i, getIsChrome());
2085		p.scale_image(true);
2086		p.chrome(true);
2087
2088		LLButton* buttonp = LLUICtrlFactory::create<LLButton>(p);
2089		addChild(buttonp);
2090		mButtons[i] = buttonp;
2091	}
2092
2093	updateTitleButtons();
2094}
2095
2096// static
2097LLUIImage* LLFloater::getButtonImage(const Params& p, EFloaterButton e)
2098{
2099	switch(e)
2100	{
2101		default:
2102		case BUTTON_CLOSE:
2103			return p.close_image;
2104		case BUTTON_RESTORE:
2105			return p.restore_image;
2106		case BUTTON_MINIMIZE:
2107			return p.minimize_image;
2108		case BUTTON_TEAR_OFF:
2109			return p.tear_off_image;
2110		case BUTTON_DOCK:
2111			return p.dock_image;
2112		case BUTTON_HELP:
2113			return p.help_image;
2114	}
2115}
2116
2117// static
2118LLUIImage* LLFloater::getButtonPressedImage(const Params& p, EFloaterButton e)
2119{
2120	switch(e)
2121	{
2122		default:
2123		case BUTTON_CLOSE:
2124			return p.close_pressed_image;
2125		case BUTTON_RESTORE:
2126			return p.restore_pressed_image;
2127		case BUTTON_MINIMIZE:
2128			return p.minimize_pressed_image;
2129		case BUTTON_TEAR_OFF:
2130			return p.tear_off_pressed_image;
2131		case BUTTON_DOCK:
2132			return p.dock_pressed_image;
2133		case BUTTON_HELP:
2134			return p.help_pressed_image;
2135	}
2136}
2137
2138// static
2139std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome)
2140{
2141	// EXT-4081 (Lag Meter: Ctrl+W does not close floater)
2142	// If floater is chrome set 'Close' text for close button's tooltip
2143	if(is_chrome && BUTTON_CLOSE == e)
2144	{
2145		static std::string close_tooltip_chrome = LLTrans::getString("BUTTON_CLOSE_CHROME");
2146		return close_tooltip_chrome;
2147	}
2148	// TODO: per-floater localizable tooltips set in XML
2149	return sButtonToolTips[e];
2150}
2151
2152/////////////////////////////////////////////////////
2153// LLFloaterView
2154
2155static LLDefaultChildRegistry::Register<LLFloaterView> r("floater_view");
2156
2157LLFloaterView::LLFloaterView (const Params& p)
2158:	LLUICtrl (p),
2159	mFocusCycleMode(FALSE),
2160	mMinimizePositionVOffset(0),
2161	mSnapOffsetBottom(0),
2162	mSnapOffsetRight(0)
2163{
2164}
2165
2166// By default, adjust vertical.
2167void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent)
2168{
2169	S32 old_right = mLastSnapRect.mRight;
2170	S32 old_top = mLastSnapRect.mTop;
2171
2172	LLView::reshape(width, height, called_from_parent);
2173
2174	S32 new_right = getSnapRect().mRight;
2175	S32 new_top = getSnapRect().mTop;
2176
2177	mLastSnapRect = getSnapRect();
2178
2179	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
2180	{
2181		LLView* viewp = *child_it;
2182		LLFloater* floaterp = (LLFloater*)viewp;
2183		if (floaterp->isDependent())
2184		{
2185			// dependents are moved with their "dependee"
2186			continue;
2187		}
2188
2189		if (!floaterp->isMinimized())
2190		{
2191			LLRect r = floaterp->getRect();
2192
2193			// Compute absolute distance from each edge of screen
2194			S32 left_offset = llabs(r.mLeft - 0);
2195			S32 right_offset = llabs(old_right - r.mRight);
2196
2197			S32 top_offset = llabs(old_top - r.mTop);
2198			S32 bottom_offset = llabs(r.mBottom - 0);
2199
2200			S32 translate_x = 0;
2201			S32 translate_y = 0;
2202
2203			if (left_offset > right_offset)
2204			{
2205				translate_x = new_right - old_right;
2206			}
2207
2208			if (top_offset < bottom_offset)
2209			{
2210				translate_y = new_top - old_top;
2211			}
2212
2213			// don't reposition immovable floaters
2214			if (floaterp->getCanDrag())
2215			{
2216				floaterp->translate(translate_x, translate_y);
2217			}
2218			BOOST_FOREACH(LLHandle<LLFloater> dependent_floater, floaterp->mDependents)
2219			{
2220				if (dependent_floater.get())
2221				{
2222					dependent_floater.get()->translate(translate_x, translate_y);
2223				}
2224			}
2225		}
2226	}
2227}
2228
2229
2230void LLFloaterView::restoreAll()
2231{
2232	// make sure all subwindows aren't minimized
2233	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
2234	{
2235		LLFloater* floaterp = (LLFloater*)*child_it;
2236		floaterp->setMinimized(FALSE);
2237	}
2238
2239	// *FIX: make sure dependents are restored
2240
2241	// children then deleted by default view constructor
2242}
2243
2244
2245LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor )
2246{
2247	LLRect base_rect = reference_floater->getRect();
2248	LLRect::tCoordType width = neighbor->getRect().getWidth();
2249	LLRect::tCoordType height = neighbor->getRect().getHeight();
2250	LLRect new_rect = neighbor->getRect();
2251
2252	LLRect expanded_base_rect = base_rect;
2253	expanded_base_rect.stretch(10);
2254	for(LLFloater::handle_set_iter_t dependent_it = reference_floater->mDependents.begin();
2255		dependent_it != reference_floater->mDependents.end(); ++dependent_it)
2256	{
2257		LLFloater* sibling = dependent_it->get();
2258		// check for dependents within 10 pixels of base floater
2259		if (sibling && 
2260			sibling != neighbor && 
2261			sibling->getVisible() && 
2262			expanded_base_rect.overlaps(sibling->getRect()))
2263		{
2264			base_rect.unionWith(sibling->getRect());
2265		}
2266	}
2267
2268	LLRect::tCoordType left_margin = llmax(0, base_rect.mLeft);
2269	LLRect::tCoordType right_margin = llmax(0, getRect().getWidth() - base_rect.mRight);
2270	LLRect::tCoordType top_margin = llmax(0, getRect().getHeight() - base_rect.mTop);
2271	LLRect::tCoordType bottom_margin = llmax(0, base_rect.mBottom);
2272
2273	// find position for floater in following order
2274	// right->left->bottom->top
2275	for (S32 i = 0; i < 5; i++)
2276	{
2277		if (right_margin > width)
2278		{
2279			new_rect.translate(base_rect.mRight - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mTop);
2280			return new_rect;
2281		}
2282		else if (left_margin > width)
2283		{
2284			new_rect.translate(base_rect.mLeft - neighbor->getRect().mRight, base_rect.mTop - neighbor->getRect().mTop);
2285			return new_rect;
2286		}
2287		else if (bottom_margin > height)
2288		{
2289			new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mBottom - neighbor->getRect().mTop);
2290			return new_rect;
2291		}
2292		else if (top_margin > height)
2293		{
2294			new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mBottom);
2295			return new_rect;
2296		}
2297
2298		// keep growing margins to find "best" fit
2299		left_margin += 20;
2300		right_margin += 20;
2301		top_margin += 20;
2302		bottom_margin += 20;
2303	}
2304
2305	// didn't find anything, return initial rect
2306	return new_rect;
2307}
2308
2309
2310void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
2311{
2312	// *TODO: make this respect floater's mAutoFocus value, instead of
2313	// using parameter
2314	if (child->getHost())
2315 	{
2316		// this floater is hosted elsewhere and hence not one of our children, abort
2317		return;
2318	}
2319	std::vector<LLView*> floaters_to_move;
2320	// Look at all floaters...tab
2321	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
2322	{
2323		LLView* viewp = *child_it;
2324		LLFloater *floater = (LLFloater *)viewp;
2325
2326		// ...but if I'm a dependent floater...
2327		if (child->isDependent())
2328		{
2329			// ...look for floaters that have me as a dependent...
2330			LLFloater::handle_set_iter_t found_dependent = floater->mDependents.find(child->getHandle());
2331
2332			if (found_dependent != floater->mDependents.end())
2333			{
2334				// ...and make sure all children of that floater (including me) are brought to front...
2335				for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
2336					dependent_it != floater->mDependents.end(); )
2337				{
2338					LLFloater* sibling = dependent_it->get();
2339					if (sibling)
2340					{
2341						floaters_to_move.push_back(sibling);
2342					}
2343					++dependent_it;
2344				}
2345				//...before bringing my parent to the front...
2346				floaters_to_move.push_back(floater);
2347			}
2348		}
2349	}
2350
2351	std::vector<LLView*>::iterator view_it;
2352	for(view_it = floaters_to_move.begin(); view_it != floaters_to_move.end(); ++view_it)
2353	{
2354		LLFloater* floaterp = (LLFloater*)(*view_it);
2355		sendChildToFront(floaterp);
2356
2357		// always unminimize dependee, but allow dependents to stay minimized
2358		if (!floaterp->isDependent())
2359		{
2360			floaterp->setMinimized(FALSE);
2361		}
2362	}
2363	floaters_to_move.clear();
2364
2365	// ...then bringing my own dependents to the front...
2366	for(LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin();
2367		dependent_it != child->mDependents.end(); )
2368	{
2369		LLFloater* dependent = dependent_it->get();
2370		if (dependent)
2371		{
2372			sendChildToFront(dependent);
2373			//don't un-minimize dependent windows automatically
2374			// respect user's wishes
2375			//dependent->setMinimized(FALSE);
2376		}
2377		++dependent_it;
2378	}
2379
2380	// ...and finally bringing myself to front 
2381	// (do this last, so that I'm left in front at end of this call)
2382	if( *getChildList()->begin() != child ) 
2383	{
2384		sendChildToFront(child);
2385	}
2386	child->setMinimized(FALSE);
2387	if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
2388	{
2389		child->setFocus(TRUE);
2390		// floater did not take focus, so relinquish focus to world
2391		if (!child->hasFocus())
2392		{
2393			gFocusMgr.setKeyboardFocus(NULL);
2394		}
2395	}
2396}
2397
2398void LLFloaterView::highlightFocusedFloater()
2399{
2400	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
2401	{
2402		LLFloater *floater = (LLFloater *)(*child_it);
2403
2404		// skip dependent floaters, as we'll handle them in a batch along with their dependee(?)
2405		if (floater->isDependent())
2406		{
2407			continue;
2408		}
2409
2410		BOOL floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater);
2411		for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
2412			dependent_it != floater->mDependents.end(); 
2413			++dependent_it)
2414		{
2415			LLFloater* dependent_floaterp = dependent_it->get();
2416			if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp))
2417			{
2418				floater_or_dependent_has_focus = TRUE;
2419			}
2420		}
2421
2422		// now set this floater and all its dependents
2423		floater->setForeground(floater_or_dependent_has_focus);
2424
2425		for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
2426			dependent_it != floater->mDependents.end(); )
2427		{
2428			LLFloater* dependent_floaterp = dependent_it->get();
2429			if (dependent_floaterp)
2430			{
2431				dependent_floaterp->setForeground(floater_or_dependent_has_focus);
2432			}
2433			++dependent_it;
2434		}
2435			
2436		floater->cleanupHandles();
2437	}
2438}
2439
2440void LLFloaterView::unhighlightFocusedFloater()
2441{
2442	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
2443	{
2444		LLFloater *floater = (LLFloater *)(*child_it);
2445
2446		floater->setForeground(FALSE);
2447	}
2448}
2449
2450void LLFloaterView::focusFrontFloater()
2451{
2452	LLFloater* floaterp = getFrontmost();
2453	if (floaterp)
2454	{
2455		floaterp->setFocus(TRUE);
2456	}
2457}
2458
2459void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom)
2460{
2461	const LLFloater::Params& default_params = LLFloater::getDefaultParams();
2462	S32 floater_header_size = default_params.header_height;
2463	static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0);
2464	LLRect snap_rect_local = getLocalSnapRect();
2465	snap_rect_local.mTop += mMinimizePositionVOffset;
2466	for(S32 col = snap_rect_local.mLeft;
2467		col < snap_rect_local.getWidth() - minimized_width;
2468		col += minimized_width)
2469	{	
2470		for(S32 row = snap_rect_local.mTop - floater_header_size;
2471		row > floater_header_size;
2472		row -= floater_header_size ) //loop rows
2473		{
2474
2475			bool foundGap = TRUE;
2476			for(child_list_const_iter_t child_it = getChildList()->begin();
2477				child_it != getChildList()->end();
2478				++child_it) //loop floaters
2479			{
2480				// Examine minimized children.
2481				LLFloater* floater = (LLFloater*)((LLView*)*child_it);
2482				if(floater->isMinimized()) 
2483				{
2484					LLRect r = floater->getRect();
2485					if((r.mBottom < (row + floater_header_size))
2486					   && (r.mBottom > (row - floater_header_size))
2487					   && (r.mLeft < (col + minimized_width))
2488					   && (r.mLeft > (col - minimized_width)))
2489					{
2490						// needs the check for off grid. can't drag,
2491						// but window resize makes them off
2492						foundGap = FALSE;
2493						break;
2494					}
2495				}
2496			} //done floaters
2497			if(foundGap)
2498			{
2499				*left = col;
2500				*bottom = row;
2501				return; //done
2502			}
2503		} //done this col
2504	}
2505
2506	// crude - stack'em all at 0,0 when screen is full of minimized
2507	// floaters.
2508	*left = snap_rect_local.mLeft;
2509	*bottom = snap_rect_local.mBottom;
2510}
2511
2512
2513void LLFloaterView::destroyAllChildren()
2514{
2515	LLView::deleteAllChildren();
2516}
2517
2518void LLFloaterView::closeAllChildren(bool app_quitting)
2519{
2520	// iterate over a copy of the list, because closing windows will destroy
2521	// some windows on the list.
2522	child_list_t child_list = *(getChildList());
2523
2524	for (child_list_const_iter_t it = child_list.begin(); it != child_list.end(); ++it)
2525	{
2526		LLView* viewp = *it;
2527		child_list_const_iter_t exists = std::find(getChildList()->begin(), getChildList()->end(), viewp);
2528		if (exists == getChildList()->end())
2529		{
2530			// this floater has already been removed
2531			continue;
2532		}
2533
2534		LLFloater* floaterp = (LLFloater*)viewp;
2535
2536		// Attempt to close floater.  This will cause the "do you want to save"
2537		// dialogs to appear.
2538		// Skip invisible floaters if we're not quitting (STORM-192).
2539		if (floaterp->canClose() && !floaterp->isDead() &&
2540			(app_quitting || floaterp->getVisible()))
2541		{
2542			floaterp->closeFloater(app_quitting);
2543		}
2544	}
2545}
2546
2547void LLFloaterView::hiddenFloaterClosed(LLFloater* floater)
2548{
2549	for (hidden_floaters_t::iterator it = mHiddenFloaters.begin(), end_it = mHiddenFloaters.end();
2550		it != end_it;
2551		++it)
2552	{
2553		if (it->first.get() == floater)
2554		{
2555			it->second.disconnect();
2556			mHiddenFloaters.erase(it);
2557			break;
2558		}
2559	}
2560}
2561
2562void LLFloaterView::hideAllFloaters()
2563{
2564	child_list_t child_list = *(getChildList());
2565
2566	for (child_list_iter_t it = child_list.begin(); it != child_list.end(); ++it)
2567	{
2568		LLFloater* floaterp = dynamic_cast<LLFloater*>(*it);
2569		if (floaterp && floaterp->getVisible())
2570		{
2571			floaterp->setVisible(false);
2572			boost::signals2::connection connection = floaterp->mCloseSignal.connect(boost::bind(&LLFloaterView::hiddenFloaterClosed, this, floaterp));
2573			mHiddenFloaters.push_back(std::make_pair(floaterp->getHandle(), connection));
2574		}
2575	}
2576}
2577
2578void LLFloaterView::showHiddenFloaters()
2579{
2580	for (hidden_floaters_t::iterator it = mHiddenFloaters.begin(), end_it = mHiddenFloaters.end();
2581		it != end_it;
2582		++it)
2583	{
2584		LLFloater* floaterp = it->first.get();
2585		if (floaterp)
2586		{
2587			floaterp->setVisible(true);
2588		}
2589		it->second.disconnect();
2590	}
2591	mHiddenFloaters.clear();
2592}
2593
2594BOOL LLFloaterView::allChildrenClosed()
2595{
2596	// see if there are any visible floaters (some floaters "close"
2597	// by setting themselves invisible)
2598	for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
2599	{
2600		LLView* viewp = *it;
2601		LLFloater* floaterp = (LLFloater*)viewp;
2602
2603		if (floaterp->getVisible() && !floaterp->isDead() && floaterp->isCloseable())
2604		{
2605			return false;
2606		}
2607	}
2608	return true;
2609}
2610
2611void LLFloaterView::shiftFloaters(S32 x_offset, S32 y_of