PageRenderTime 49ms CodeModel.GetById 19ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llui/llmultifloater.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 512 lines | 337 code | 63 blank | 112 comment | 62 complexity | b69ceaa90608a5aff52269c410ed225c MD5 | raw file
  1/** 
  2 * @file llmultifloater.cpp
  3 * @brief LLFloater that hosts other floaters
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27// Floating "windows" within the GL display, like the inventory floater,
 28// mini-map floater, etc.
 29
 30#include "linden_common.h"
 31
 32#include "llmultifloater.h"
 33#include "llresizehandle.h"
 34
 35//
 36// LLMultiFloater
 37//
 38
 39LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params)
 40	: LLFloater(key),
 41	  mTabContainer(NULL),
 42	  mTabPos(LLTabContainer::TOP),
 43	  mAutoResize(TRUE),
 44	  mOrigMinWidth(0),
 45	  mOrigMinHeight(0)
 46{
 47}
 48
 49void LLMultiFloater::buildTabContainer()
 50{
 51	const LLFloater::Params& default_params = LLFloater::getDefaultParams();
 52	S32 floater_header_size = default_params.header_height;
 53	
 54	LLTabContainer::Params p;
 55	p.name(std::string("Preview Tabs"));
 56	p.rect(LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - floater_header_size, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0));
 57	p.tab_position(mTabPos);
 58	p.follows.flags(FOLLOWS_ALL);
 59	p.commit_callback.function(boost::bind(&LLMultiFloater::onTabSelected, this));
 60
 61	mTabContainer = LLUICtrlFactory::create<LLTabContainer>(p);
 62	addChild(mTabContainer);
 63	
 64	if (isResizable())
 65	{
 66		mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
 67	}
 68}
 69
 70void LLMultiFloater::onOpen(const LLSD& key)
 71{
 72// 	if (mTabContainer->getTabCount() <= 0)
 73// 	{
 74// 		// for now, don't allow multifloaters
 75// 		// without any child floaters
 76// 		closeFloater();
 77// 	}
 78}
 79
 80void LLMultiFloater::draw()
 81{
 82	if (mTabContainer->getTabCount() == 0)
 83	{
 84		//RN: could this potentially crash in draw hierarchy?
 85		closeFloater();
 86	}
 87	else
 88	{
 89		LLFloater::draw();
 90	}
 91}
 92
 93BOOL LLMultiFloater::closeAllFloaters()
 94{
 95	S32	tabToClose = 0;
 96	S32	lastTabCount = mTabContainer->getTabCount();
 97	while (tabToClose < mTabContainer->getTabCount())
 98	{
 99		LLFloater* first_floater = (LLFloater*)mTabContainer->getPanelByIndex(tabToClose);
100		first_floater->closeFloater();
101		if(lastTabCount == mTabContainer->getTabCount())
102		{
103			//Tab did not actually close, possibly due to a pending Save Confirmation dialog..
104			//so try and close the next one in the list...
105			tabToClose++;
106		}
107		else
108		{
109			//Tab closed ok.
110			lastTabCount = mTabContainer->getTabCount();
111		}
112	}
113	if( mTabContainer->getTabCount() != 0 )
114		return FALSE; // Couldn't close all the tabs (pending save dialog?) so return FALSE.
115	return TRUE; //else all tabs were successfully closed...
116}
117
118void LLMultiFloater::growToFit(S32 content_width, S32 content_height)
119{
120	static LLUICachedControl<S32> tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0);
121	const LLFloater::Params& default_params = LLFloater::getDefaultParams();
122	S32 floater_header_size = default_params.header_height;
123	S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size;
124	S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2);
125	S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height);
126
127    if (isMinimized())
128    {
129        LLRect newrect;
130        newrect.setLeftTopAndSize(getExpandedRect().mLeft, getExpandedRect().mTop, new_width, new_height);
131        setExpandedRect(newrect);
132    }
133	else
134	{
135		S32 old_height = getRect().getHeight();
136		reshape(new_width, new_height);
137		// keep top left corner in same position
138		translate(0, old_height - new_height);
139	}
140}
141
142/**
143  void addFloater(LLFloater* floaterp, BOOL select_added_floater)
144
145  Adds the LLFloater pointed to by floaterp to this.
146  If floaterp is already hosted by this, then it is re-added to get
147  new titles, etc.
148  If select_added_floater is true, the LLFloater pointed to by floaterp will
149  become the selected tab in this
150
151  Affects: mTabContainer, floaterp
152**/
153void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
154{
155	if (!floaterp)
156	{
157		return;
158	}
159
160	if (!mTabContainer)
161	{
162		llerrs << "Tab Container used without having been initialized." << llendl;
163		return;
164	}
165
166	if (floaterp->getHost() == this)
167	{
168		// already hosted by me, remove
169		// do this so we get updated title, etc.
170		mFloaterDataMap.erase(floaterp->getHandle());
171		mTabContainer->removeTabPanel(floaterp);
172	}
173	else if (floaterp->getHost())
174	{
175		// floaterp is hosted by somebody else and
176		// this is adding it, so remove it from it's old host
177		floaterp->getHost()->removeFloater(floaterp);
178	}
179	else if (floaterp->getParent() == gFloaterView)
180	{
181		// rehost preview floater as child panel
182		gFloaterView->removeChild(floaterp);
183	}
184
185	// store original configuration
186	LLFloaterData floater_data;
187	floater_data.mWidth = floaterp->getRect().getWidth();
188	floater_data.mHeight = floaterp->getRect().getHeight();
189	floater_data.mCanMinimize = floaterp->isMinimizeable();
190	floater_data.mCanResize = floaterp->isResizable();
191
192	// remove minimize and close buttons
193	floaterp->setCanMinimize(FALSE);
194	floaterp->setCanResize(FALSE);
195	floaterp->setCanDrag(FALSE);
196	floaterp->storeRectControl();
197	// avoid double rendering of floater background (makes it more opaque)
198	floaterp->setBackgroundVisible(FALSE);
199
200	if (mAutoResize)
201	{
202		growToFit(floater_data.mWidth, floater_data.mHeight);
203	}
204
205	//add the panel, add it to proper maps
206	mTabContainer->addTabPanel(
207		LLTabContainer::TabPanelParams()
208			.panel(floaterp)
209			.label(floaterp->getShortTitle())
210			.insert_at(insertion_point));
211	mFloaterDataMap[floaterp->getHandle()] = floater_data;
212
213	updateResizeLimits();
214
215	if ( select_added_floater )
216	{
217		mTabContainer->selectTabPanel(floaterp);
218	}
219	else
220	{
221		// reassert visible tab (hiding new floater if necessary)
222		mTabContainer->selectTab(mTabContainer->getCurrentPanelIndex());
223	}
224
225	floaterp->setHost(this);
226	if (isMinimized())
227	{
228		floaterp->setVisible(FALSE);
229	}
230	
231	// Tabs sometimes overlap resize handle
232	moveResizeHandlesToFront();
233}
234
235void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp)
236{
237	S32 index = mTabContainer->getIndexForPanel(floaterp);
238	if (index != -1)
239	{
240		mTabContainer->setPanelTitle(index, floaterp->getShortTitle());
241	}
242}
243
244
245/**
246	BOOL selectFloater(LLFloater* floaterp)
247
248	If the LLFloater pointed to by floaterp is hosted by this,
249	then its tab is selected and returns true.  Otherwise returns false.
250
251	Affects: mTabContainer
252**/
253BOOL LLMultiFloater::selectFloater(LLFloater* floaterp)
254{
255	return mTabContainer->selectTabPanel(floaterp);
256}
257
258// virtual
259void LLMultiFloater::selectNextFloater()
260{
261	mTabContainer->selectNextTab();
262}
263
264// virtual
265void LLMultiFloater::selectPrevFloater()
266{
267	mTabContainer->selectPrevTab();
268}
269
270void LLMultiFloater::showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point)
271{
272	if(!floaterp) return;
273	// we won't select a panel that already is selected
274	// it is hard to do this internally to tab container
275	// as tab selection is handled via index and the tab at a given
276	// index might have changed
277	if (floaterp != mTabContainer->getCurrentPanel() &&
278		!mTabContainer->selectTabPanel(floaterp))
279	{
280		addFloater(floaterp, TRUE, insertion_point);
281	}
282}
283
284void LLMultiFloater::removeFloater(LLFloater* floaterp)
285{
286	if (!floaterp || floaterp->getHost() != this )
287		return;
288
289	floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle());
290	if (found_data_it != mFloaterDataMap.end())
291	{
292		LLFloaterData& floater_data = found_data_it->second;
293		floaterp->setCanMinimize(floater_data.mCanMinimize);
294		if (!floater_data.mCanResize)
295		{
296			// restore original size
297			floaterp->reshape(floater_data.mWidth, floater_data.mHeight);
298		}
299		floaterp->setCanResize(floater_data.mCanResize);
300		mFloaterDataMap.erase(found_data_it);
301	}
302	mTabContainer->removeTabPanel(floaterp);
303	floaterp->setBackgroundVisible(TRUE);
304	floaterp->setCanDrag(TRUE);
305	floaterp->setHost(NULL);
306	floaterp->applyRectControl();
307
308	updateResizeLimits();
309
310	tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false);
311}
312
313void LLMultiFloater::tabOpen(LLFloater* opened_floater, bool from_click)
314{
315	// default implementation does nothing
316}
317
318void LLMultiFloater::tabClose()
319{
320	if (mTabContainer->getTabCount() == 0)
321	{
322		// no more children, close myself
323		closeFloater();
324	}
325}
326
327void LLMultiFloater::setVisible(BOOL visible)
328{
329	// *FIX: shouldn't have to do this, fix adding to minimized multifloater
330	LLFloater::setVisible(visible);
331	
332	if (mTabContainer)
333	{
334		LLPanel* cur_floaterp = mTabContainer->getCurrentPanel();
335
336		if (cur_floaterp)
337		{
338			cur_floaterp->setVisible(visible);
339		}
340
341		// if no tab selected, and we're being shown,
342		// select last tab to be added
343		if (visible && !cur_floaterp)
344		{
345			mTabContainer->selectLastTab();
346		}
347	}
348}
349
350BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask)
351{
352	if (key == 'W' && mask == (MASK_CONTROL|MASK_SHIFT))
353	{
354		LLFloater* floater = getActiveFloater();
355		// is user closeable and is system closeable
356		if (floater && floater->canClose() && floater->isCloseable())
357		{
358			floater->closeFloater();
359
360			// EXT-5695 (Tabbed IM window loses focus if close any tabs by Ctrl+W)
361			// bring back focus on tab container if there are any tab left
362			if(mTabContainer->getTabCount() > 0)
363			{
364				mTabContainer->setFocus(TRUE);
365			}
366		}
367		return TRUE;
368	}
369
370	return LLFloater::handleKeyHere(key, mask);
371}
372
373bool LLMultiFloater::addChild(LLView* child, S32 tab_group)
374{
375	LLTabContainer* tab_container = dynamic_cast<LLTabContainer*>(child);
376	if (tab_container)
377	{
378		// store pointer to tab container
379		setTabContainer(tab_container);
380	}
381
382	// then go ahead and add child as usual
383	return LLFloater::addChild(child, tab_group);
384}
385
386LLFloater* LLMultiFloater::getActiveFloater()
387{
388	return (LLFloater*)mTabContainer->getCurrentPanel();
389}
390
391S32	LLMultiFloater::getFloaterCount()
392{
393	return mTabContainer->getTabCount();
394}
395
396/**
397	BOOL isFloaterFlashing(LLFloater* floaterp)
398
399	Returns true if the LLFloater pointed to by floaterp
400	is currently in a flashing state and is hosted by this.
401	False otherwise.
402
403	Requires: floaterp != NULL
404**/
405BOOL LLMultiFloater::isFloaterFlashing(LLFloater* floaterp)
406{
407	if ( floaterp && floaterp->getHost() == this )
408		return mTabContainer->getTabPanelFlashing(floaterp);
409
410	return FALSE;
411}
412
413/**
414	BOOL setFloaterFlashing(LLFloater* floaterp, BOOL flashing)
415
416	Sets the current flashing state of the LLFloater pointed
417	to by floaterp to be the BOOL flashing if the LLFloater pointed
418	to by floaterp is hosted by this.
419
420	Requires: floaterp != NULL
421**/
422void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, BOOL flashing)
423{
424	if ( floaterp && floaterp->getHost() == this )
425		mTabContainer->setTabPanelFlashing(floaterp, flashing);
426}
427
428void LLMultiFloater::onTabSelected()
429{
430	LLFloater* floaterp = dynamic_cast<LLFloater*>(mTabContainer->getCurrentPanel());
431	if (floaterp)
432	{
433		tabOpen(floaterp, true);
434	}
435}
436
437void LLMultiFloater::setCanResize(BOOL can_resize)
438{
439	LLFloater::setCanResize(can_resize);
440	if (!mTabContainer) return;
441	if (isResizable() && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM)
442	{
443		mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
444	}
445	else
446	{
447		mTabContainer->setRightTabBtnOffset(0);
448	}
449}
450
451BOOL LLMultiFloater::postBuild()
452{
453	mCloseSignal.connect(boost::bind(&LLMultiFloater::closeAllFloaters, this));
454		
455	// remember any original xml minimum size
456	getResizeLimits(&mOrigMinWidth, &mOrigMinHeight);
457
458	if (mTabContainer)
459	{
460		return TRUE;
461	}
462
463	mTabContainer = getChild<LLTabContainer>("Preview Tabs");
464	
465	setCanResize(mResizable);
466	return TRUE;
467}
468
469void LLMultiFloater::updateResizeLimits()
470{
471	static LLUICachedControl<S32> tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0);
472	const LLFloater::Params& default_params = LLFloater::getDefaultParams();
473	S32 floater_header_size = default_params.header_height;
474	S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size;
475	// initialize minimum size constraint to the original xml values.
476	S32 new_min_width = mOrigMinWidth;
477	S32 new_min_height = mOrigMinHeight;
478	// possibly increase minimum size constraint due to children's minimums.
479	for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx)
480	{
481		LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx);
482		if (floaterp)
483		{
484			new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2);
485			new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height);
486		}
487	}
488	setResizeLimits(new_min_width, new_min_height);
489
490	S32 cur_height = getRect().getHeight();
491	S32 new_width = llmax(getRect().getWidth(), new_min_width);
492	S32 new_height = llmax(getRect().getHeight(), new_min_height);
493
494	if (isMinimized())
495	{
496		const LLRect& expanded = getExpandedRect();
497		LLRect newrect;
498		newrect.setLeftTopAndSize(expanded.mLeft, expanded.mTop, llmax(expanded.getWidth(), new_width), llmax(expanded.getHeight(), new_height));
499		setExpandedRect(newrect);
500	}
501	else
502	{
503		reshape(new_width, new_height);
504
505		// make sure upper left corner doesn't move
506		translate(0, cur_height - getRect().getHeight());
507
508		// make sure this window is visible on screen when it has been modified
509		// (tab added, etc)
510		gFloaterView->adjustToFitScreen(this, TRUE);
511	}
512}