PageRenderTime 154ms CodeModel.GetById 10ms app.highlight 130ms RepoModel.GetById 1ms app.codeStats 1ms

/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

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

   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.setL

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