PageRenderTime 110ms CodeModel.GetById 10ms app.highlight 93ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llui/llaccordionctrl.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 914 lines | 702 code | 147 blank | 65 comment | 136 complexity | 1fb24b5e1f0ffd8dc94a233dfe305310 MD5 | raw file
  1/** 
  2 * @file llaccordionctrl.cpp
  3 * @brief Accordion panel  implementation
  4 *
  5 * $LicenseInfo:firstyear=2009&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#include "linden_common.h"
 27
 28#include "llaccordionctrl.h"
 29#include "llaccordionctrltab.h"
 30
 31#include "lluictrlfactory.h" // builds floaters from XML
 32
 33#include "llwindow.h"
 34#include "llfocusmgr.h"
 35#include "lllocalcliprect.h"
 36
 37#include "boost/bind.hpp"
 38
 39static const S32 DRAGGER_BAR_MARGIN = 4;
 40static const S32 DRAGGER_BAR_HEIGHT = 5;
 41static const S32 BORDER_MARGIN = 2;
 42static const S32 PARENT_BORDER_MARGIN = 5;
 43
 44static const S32 panel_delta = DRAGGER_BAR_MARGIN;  // Distanse between two panels 
 45
 46static const S32 HORIZONTAL_MULTIPLE = 8;
 47static const S32 VERTICAL_MULTIPLE = 16;
 48static const F32 MIN_AUTO_SCROLL_RATE = 120.f;
 49static const F32 MAX_AUTO_SCROLL_RATE = 500.f;
 50static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
 51
 52
 53// LLAccordionCtrl =================================================================|
 54
 55static LLDefaultChildRegistry::Register<LLAccordionCtrl>	t2("accordion");
 56
 57
 58LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
 59 , mFitParent(params.fit_parent)
 60 , mAutoScrolling( false )
 61 , mAutoScrollRate( 0.f )
 62 , mSelectedTab( NULL )
 63 , mTabComparator( NULL )
 64 , mNoVisibleTabsHelpText(NULL)
 65 , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString())
 66{
 67	initNoTabsWidget(params.no_matched_tabs_text);
 68
 69	mSingleExpansion = params.single_expansion;
 70	if(mFitParent && !mSingleExpansion)
 71	{
 72		llinfos << "fit_parent works best when combined with single_expansion" << llendl;
 73	}
 74}
 75
 76LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
 77 , mAutoScrolling( false )
 78 , mAutoScrollRate( 0.f )
 79 , mSelectedTab( NULL )
 80 , mNoVisibleTabsHelpText(NULL)
 81{
 82	initNoTabsWidget(LLTextBox::Params());
 83
 84	mSingleExpansion = false;
 85	mFitParent = false;
 86	buildFromFile( "accordion_parent.xml");	
 87}
 88
 89//---------------------------------------------------------------------------------
 90void LLAccordionCtrl::draw()
 91{
 92	if (mAutoScrolling)
 93	{
 94		// add acceleration to autoscroll
 95		mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
 96	}
 97	else
 98	{
 99		// reset to minimum for next time
100		mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
101	}
102	// clear this flag to be set on next call to autoScroll
103	mAutoScrolling = false;
104
105	LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
106	
107	LLLocalClipRect clip(local_rect);
108	
109	LLPanel::draw();
110}
111
112
113//---------------------------------------------------------------------------------
114BOOL LLAccordionCtrl::postBuild()
115{
116	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
117
118	LLRect scroll_rect;
119	scroll_rect.setOriginAndSize( 
120		getRect().getWidth() - scrollbar_size,
121		1,
122		scrollbar_size,
123		getRect().getHeight() - 1);
124	
125	LLScrollbar::Params sbparams;
126	sbparams.name("scrollable vertical");
127	sbparams.rect(scroll_rect);
128	sbparams.orientation(LLScrollbar::VERTICAL);
129	sbparams.doc_size(mInnerRect.getHeight());
130	sbparams.doc_pos(0);
131	sbparams.page_size(mInnerRect.getHeight());
132	sbparams.step_size(VERTICAL_MULTIPLE);
133	sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
134	sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2));
135	
136	mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
137	LLView::addChild( mScrollbar );
138	mScrollbar->setVisible( false );
139	mScrollbar->setFollowsRight();
140	mScrollbar->setFollowsTop();
141	mScrollbar->setFollowsBottom();
142
143	//if it was created from xml...
144	std::vector<LLUICtrl*> accordion_tabs;
145	for(child_list_const_iter_t it = getChildList()->begin(); 
146		getChildList()->end() != it; ++it)
147	{
148		LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*it);
149		if(accordion_tab == NULL)
150			continue;
151		if(std::find(mAccordionTabs.begin(),mAccordionTabs.end(),accordion_tab) == mAccordionTabs.end())
152		{
153			accordion_tabs.push_back(accordion_tab);
154		}
155	}
156
157	for(std::vector<LLUICtrl*>::reverse_iterator it = accordion_tabs.rbegin();it!=accordion_tabs.rend();++it)
158		addCollapsibleCtrl(*it);
159
160	arrange	();
161
162	if(mSingleExpansion)
163	{
164		if(!mAccordionTabs[0]->getDisplayChildren())
165			mAccordionTabs[0]->setDisplayChildren(true);
166		for(size_t i=1;i<mAccordionTabs.size();++i)
167		{
168			if(mAccordionTabs[i]->getDisplayChildren())
169				mAccordionTabs[i]->setDisplayChildren(false);
170		}
171	}
172
173	updateNoTabsHelpTextVisibility();
174
175	return TRUE;
176}
177
178
179//---------------------------------------------------------------------------------
180LLAccordionCtrl::~LLAccordionCtrl()
181{
182  mAccordionTabs.clear();
183}
184
185//---------------------------------------------------------------------------------
186
187void LLAccordionCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
188{
189	// adjust our rectangle
190	LLRect rcLocal = getRect();
191	rcLocal.mRight = rcLocal.mLeft + width;
192	rcLocal.mTop = rcLocal.mBottom + height;
193
194	// get textbox a chance to reshape its content
195	mNoVisibleTabsHelpText->reshape(width, height, called_from_parent);
196
197	setRect(rcLocal);
198
199	// assume that help text is always fit accordion.
200	// necessary text paddings can be set via h_pad and v_pad
201	mNoVisibleTabsHelpText->setRect(getLocalRect());
202
203	arrange();
204}
205
206//---------------------------------------------------------------------------------
207BOOL LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
208{
209	return LLPanel::handleRightMouseDown(x, y, mask);
210}
211
212//---------------------------------------------------------------------------------
213void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta)
214{
215	for(size_t i = panel_num; i < mAccordionTabs.size(); i++ )
216	{
217		ctrlShiftVertical(mAccordionTabs[i],delta);
218	}	
219}
220
221
222//---------------------------------------------------------------------------------
223void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num) 
224{
225	if(mSingleExpansion)
226	{
227		for(size_t i=0;i<mAccordionTabs.size();++i)
228		{
229			if(i==panel_num)
230				continue;
231			if(mAccordionTabs[i]->getDisplayChildren())
232				mAccordionTabs[i]->setDisplayChildren(false);
233		}
234
235	}
236	arrange();
237}
238
239void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height)
240{
241	calcRecuiredHeight();
242	if(getRecuiredHeight() > height )
243		showScrollbar(width,height);
244	else
245		hideScrollbar(width,height);
246}
247
248void	LLAccordionCtrl::showScrollbar(S32 width, S32 height)
249{
250	bool was_visible = mScrollbar->getVisible();
251
252	mScrollbar->setVisible(true);
253	
254	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
255
256	ctrlSetLeftTopAndSize(mScrollbar
257		,width-scrollbar_size - PARENT_BORDER_MARGIN/2
258		,height-PARENT_BORDER_MARGIN
259		,scrollbar_size
260		,height-2*PARENT_BORDER_MARGIN);
261	
262	mScrollbar->setPageSize(height);
263	mScrollbar->setDocParams(mInnerRect.getHeight(),mScrollbar->getDocPos());
264
265	if(was_visible)
266	{
267		S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1);
268		mScrollbar->setDocPos(scroll_pos);
269	}
270}
271
272void	LLAccordionCtrl::hideScrollbar( S32 width, S32 height )
273{
274	if(mScrollbar->getVisible() == false)
275		return;
276	mScrollbar->setVisible(false);
277
278	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
279
280	S32 panel_width = width - 2*BORDER_MARGIN;
281
282	//reshape all accordeons and shift all draggers
283	for(size_t i=0;i<mAccordionTabs.size();++i)
284	{
285		LLRect panel_rect = mAccordionTabs[i]->getRect();
286		ctrlSetLeftTopAndSize(mAccordionTabs[i],panel_rect.mLeft,panel_rect.mTop,panel_width,panel_rect.getHeight());
287	}
288
289	mScrollbar->setDocPos(0);
290
291	if(mAccordionTabs.size()>0)
292	{
293		S32 panel_top = height - BORDER_MARGIN;		  // Top coordinate of the first panel
294		S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop;
295		shiftAccordionTabs(0,diff);
296	}
297}
298
299
300//---------------------------------------------------------------------------------
301S32 LLAccordionCtrl::calcRecuiredHeight()
302{
303	S32 rec_height = 0;
304	
305	std::vector<LLAccordionCtrlTab*>::iterator panel;
306	for(panel=mAccordionTabs.begin(); panel!=mAccordionTabs.end(); ++panel)
307	{
308		LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*panel);
309		if(accordion_tab && accordion_tab->getVisible())
310		{
311			rec_height += accordion_tab->getRect().getHeight();
312		}
313	}
314
315	mInnerRect.setLeftTopAndSize(0,rec_height + BORDER_MARGIN*2,getRect().getWidth(),rec_height + BORDER_MARGIN);
316
317	return mInnerRect.getHeight();
318}
319
320//---------------------------------------------------------------------------------
321void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
322{
323	if(!panel)
324		return;
325	LLRect panel_rect = panel->getRect();
326	panel_rect.setLeftTopAndSize( left, top, width, height);
327	panel->reshape( width, height, 1);
328	panel->setRect(panel_rect);
329}
330
331void LLAccordionCtrl::ctrlShiftVertical(LLView* panel,S32 delta)
332{
333	if(!panel)
334		return;
335	panel->translate(0,delta);
336}
337
338//---------------------------------------------------------------------------------
339
340void LLAccordionCtrl::addCollapsibleCtrl(LLView* view)
341{
342	LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
343	if(!accordion_tab)
344		return;
345	if(std::find(beginChild(), endChild(), accordion_tab) == endChild())
346		addChild(accordion_tab);
347	mAccordionTabs.push_back(accordion_tab);
348
349	accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, mAccordionTabs.size() - 1) );
350	arrange();	
351}
352
353void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view)
354{
355	LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
356	if(!accordion_tab)
357		return;
358
359	if(std::find(beginChild(), endChild(), accordion_tab) != endChild())
360		removeChild(accordion_tab);
361
362	for (std::vector<LLAccordionCtrlTab*>::iterator iter = mAccordionTabs.begin();
363			iter != mAccordionTabs.end(); ++iter)
364	{
365		if (accordion_tab == (*iter))
366		{
367			mAccordionTabs.erase(iter);
368			break;
369		}
370	}
371
372	// if removed is selected - reset selection
373	if (mSelectedTab == view)
374	{
375		mSelectedTab = NULL;
376	}
377}
378
379void	LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params)
380{
381	LLTextBox::Params tp = tb_params;
382	tp.rect(getLocalRect());
383	mNoMatchedTabsOrigString = tp.initial_value().asString();
384	mNoVisibleTabsHelpText = LLUICtrlFactory::create<LLTextBox>(tp, this);
385}
386
387void	LLAccordionCtrl::updateNoTabsHelpTextVisibility()
388{
389	bool visible_exists = false;
390	std::vector<LLAccordionCtrlTab*>::const_iterator it = mAccordionTabs.begin();
391	const std::vector<LLAccordionCtrlTab*>::const_iterator it_end = mAccordionTabs.end();
392	for (; it != it_end; ++it)
393	{
394		if ((*it)->getVisible())
395		{
396			visible_exists = true;
397			break;
398		}
399	}
400
401	mNoVisibleTabsHelpText->setVisible(!visible_exists);
402}
403
404void	LLAccordionCtrl::arrangeSinge()
405{
406	S32 panel_left = BORDER_MARGIN;	  // Margin from left side of Splitter
407	S32 panel_top = getRect().getHeight() - BORDER_MARGIN;		  // Top coordinate of the first panel
408	S32 panel_width = getRect().getWidth() - 4;		  // Top coordinate of the first panel
409	S32 panel_height;
410
411	S32 collapsed_height = 0;
412
413	for(size_t i=0;i<mAccordionTabs.size();++i)
414	{
415		LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
416		
417		if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
418			continue;
419		if(!accordion_tab->isExpanded() )
420		{
421			collapsed_height+=mAccordionTabs[i]->getRect().getHeight();
422		}
423	}
424
425	S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height;
426	
427	for(size_t i=0;i<mAccordionTabs.size();++i)
428	{
429		LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
430		
431		if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
432			continue;
433		if(!accordion_tab->isExpanded() )
434		{
435			panel_height = accordion_tab->getRect().getHeight();
436		}
437		else
438		{
439			if(mFitParent)
440			{
441				panel_height = expanded_height;
442			}
443			else
444			{
445				if(accordion_tab->getAccordionView())
446				{
447					panel_height = accordion_tab->getAccordionView()->getRect().getHeight() + 
448						accordion_tab->getHeaderHeight() + 2*BORDER_MARGIN;
449				}
450				else
451				{
452					panel_height = accordion_tab->getRect().getHeight();
453				}
454			}
455		}
456
457		// make sure at least header is shown
458		panel_height = llmax(panel_height, accordion_tab->getHeaderHeight());
459
460		ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
461		panel_top-=mAccordionTabs[i]->getRect().getHeight();
462	}
463
464	show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
465	updateLayout(getRect().getWidth(), getRect().getHeight());
466}
467
468void	LLAccordionCtrl::arrangeMultiple()
469{
470	S32 panel_left = BORDER_MARGIN;	  // Margin from left side of Splitter
471	S32 panel_top = getRect().getHeight() - BORDER_MARGIN;		  // Top coordinate of the first panel
472	S32 panel_width = getRect().getWidth() - 4;		  // Top coordinate of the first panel
473
474	//Calculate params	
475	for(size_t i = 0; i < mAccordionTabs.size(); i++ )
476	{
477		LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
478		
479		if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
480			continue;
481		
482		if(!accordion_tab->isExpanded() )
483		{
484			ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight());
485			panel_top-=mAccordionTabs[i]->getRect().getHeight();
486		}
487		else
488		{
489			S32 panel_height = accordion_tab->getRect().getHeight();
490			
491			if(mFitParent)
492			{
493				// all expanded tabs will have equal height
494				panel_height = calcExpandedTabHeight(i, panel_top);
495				ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height);
496
497				// try to make accordion tab fit accordion view height.
498				// Accordion View should implement getRequiredRect() and provide valid height
499				S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight();
500				optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN;
501				if(optimal_height < panel_height)
502				{
503					panel_height = optimal_height;
504				}
505
506				// minimum tab height is equal to header height
507				if(mAccordionTabs[i]->getHeaderHeight() > panel_height)
508				{
509					panel_height = mAccordionTabs[i]->getHeaderHeight();
510				}
511			}
512			
513			ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
514			panel_top-=panel_height;
515			
516		}
517	}	
518
519	show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
520
521	updateLayout(getRect().getWidth(),getRect().getHeight());
522}
523
524
525void LLAccordionCtrl::arrange()
526{
527	updateNoTabsHelpTextVisibility();
528
529	if( mAccordionTabs.size() == 0)
530	{
531		//We do not arrange if we do not have what should be arranged
532		return;
533	}
534
535
536	if(mAccordionTabs.size() == 1)
537	{
538		S32 panel_top = getRect().getHeight() - BORDER_MARGIN;		  // Top coordinate of the first panel
539		S32 panel_width = getRect().getWidth() - 4;		  // Top coordinate of the first panel
540		
541		LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
542		
543		LLRect panel_rect = accordion_tab->getRect();
544		
545		S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN;
546
547		if (accordion_tab->getFitParent())
548			panel_height = accordion_tab->getRect().getHeight();
549		ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
550		
551		show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
552		return;
553
554	}
555
556	if(mSingleExpansion)
557		arrangeSinge ();
558	else
559		arrangeMultiple ();
560}
561
562//---------------------------------------------------------------------------------
563
564BOOL LLAccordionCtrl::handleScrollWheel		( S32 x, S32 y, S32 clicks )
565{
566	if(LLPanel::handleScrollWheel(x,y,clicks))
567		return TRUE;
568	if( mScrollbar->getVisible() && mScrollbar->handleScrollWheel( 0, 0, clicks ) )
569		return TRUE;
570	return false;
571
572}
573
574BOOL LLAccordionCtrl::handleKeyHere			(KEY key, MASK mask)
575{
576	if( mScrollbar->getVisible() && mScrollbar->handleKeyHere( key,mask ) )
577		return TRUE;
578	return LLPanel::handleKeyHere(key,mask);
579}
580
581BOOL LLAccordionCtrl::handleDragAndDrop		(S32 x, S32 y, MASK mask,
582											 BOOL drop,
583											 EDragAndDropType cargo_type,
584											 void* cargo_data,
585											 EAcceptance* accept,
586											 std::string& tooltip_msg)
587{
588	// Scroll folder view if needed.  Never accepts a drag or drop.
589	*accept = ACCEPT_NO;
590	BOOL handled = autoScroll(x, y);
591
592	if( !handled )
593	{
594		handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
595											cargo_data, accept, tooltip_msg) != NULL;
596	}
597	return TRUE;
598}
599
600BOOL LLAccordionCtrl::autoScroll		(S32 x, S32 y)
601{
602	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
603
604	bool scrolling = false;
605	if( mScrollbar->getVisible() )
606	{
607		LLRect rect_local( 0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0 );
608		LLRect screen_local_extents;
609
610		// clip rect against root view
611		screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
612		rect_local.intersectWith(screen_local_extents);
613
614		// autoscroll region should take up no more than one third of visible scroller area
615		S32 auto_scroll_region_height = llmin(rect_local.getHeight() / 3, 10);
616		S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
617
618		LLRect bottom_scroll_rect = screen_local_extents;
619		bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height;
620		if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax()) )
621		{
622			mScrollbar->setDocPos( mScrollbar->getDocPos() + auto_scroll_speed );
623			mAutoScrolling = true;
624			scrolling = true;
625		}
626
627		LLRect top_scroll_rect = screen_local_extents;
628		top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height;
629		if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() > 0) )
630		{
631			mScrollbar->setDocPos( mScrollbar->getDocPos() - auto_scroll_speed );
632			mAutoScrolling = true;
633			scrolling = true;
634		}
635	}
636	return scrolling;
637}
638
639void	LLAccordionCtrl::updateLayout	(S32 width, S32 height)
640{
641	S32 panel_top = height - BORDER_MARGIN ;
642	if(mScrollbar->getVisible())
643		panel_top+=mScrollbar->getDocPos();
644
645	S32 panel_width = width - 2*BORDER_MARGIN;
646
647	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
648	if(mScrollbar->getVisible())
649		panel_width-=scrollbar_size;
650
651	//set sizes for first panels and dragbars
652	for(size_t i=0;i<mAccordionTabs.size();++i)
653	{
654		if(!mAccordionTabs[i]->getVisible())
655			continue;
656		LLRect panel_rect = mAccordionTabs[i]->getRect();
657		ctrlSetLeftTopAndSize(mAccordionTabs[i],panel_rect.mLeft,panel_top,panel_width,panel_rect.getHeight());
658		panel_top-=panel_rect.getHeight();
659	}
660}
661
662void	LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*)
663{
664	updateLayout(getRect().getWidth(),getRect().getHeight());
665}
666void	LLAccordionCtrl::onOpen		(const LLSD& key)
667{
668	for(size_t i=0;i<mAccordionTabs.size();++i)
669	{
670		LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
671		LLPanel* panel = dynamic_cast<LLPanel*>(accordion_tab->getAccordionView());
672		if(panel!=NULL)
673		{
674			panel->onOpen(key);
675		}
676	}
677}
678S32	LLAccordionCtrl::notifyParent(const LLSD& info)
679{
680	if(info.has("action"))
681	{
682		std::string str_action = info["action"];
683		if(str_action == "size_changes")
684		{
685			//
686			arrange();
687			return 1;
688		}
689		else if(str_action == "select_next")
690		{
691			for(size_t i=0;i<mAccordionTabs.size();++i)
692			{
693				LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
694				if(accordion_tab->hasFocus())
695				{
696					while(++i<mAccordionTabs.size())
697					{
698						if(mAccordionTabs[i]->getVisible())
699							break;
700					}
701					if(i<mAccordionTabs.size())
702					{
703						accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
704						accordion_tab->notify(LLSD().with("action","select_first"));
705						return 1;
706					}
707					break;
708				}
709			}
710			return 0;
711		}
712		else if(str_action == "select_prev")
713		{
714			for(size_t i=0;i<mAccordionTabs.size();++i)
715			{
716				LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
717				if(accordion_tab->hasFocus() && i>0)
718				{
719					bool prev_visible_tab_found = false;
720					while(i>0)
721					{
722						if(mAccordionTabs[--i]->getVisible())
723						{
724							prev_visible_tab_found = true;
725							break;
726						}
727					}
728
729					if (prev_visible_tab_found)
730					{
731						accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
732						accordion_tab->notify(LLSD().with("action","select_last"));
733						return 1;
734					}
735					break;
736				}
737			}
738			return 0;
739		}
740		else if(str_action == "select_current")
741		{
742			for(size_t i=0;i<mAccordionTabs.size();++i)
743			{
744				// Set selection to the currently focused tab.
745				if(mAccordionTabs[i]->hasFocus())
746				{
747					if (mAccordionTabs[i] != mSelectedTab)
748					{
749						if (mSelectedTab)
750						{
751							mSelectedTab->setSelected(false);
752						}
753						mSelectedTab = mAccordionTabs[i];
754						mSelectedTab->setSelected(true);
755					}
756
757					return 1;
758				}
759			}
760			return 0;
761		}
762		else if(str_action == "deselect_current")
763		{
764			// Reset selection to the currently selected tab.
765			if (mSelectedTab)
766			{
767				mSelectedTab->setSelected(false);
768				mSelectedTab = NULL;
769				return 1;
770			}
771			return 0;
772		}
773	}
774	else if (info.has("scrollToShowRect"))
775	{
776		LLRect screen_rc, local_rc;
777		screen_rc.setValue(info["scrollToShowRect"]);
778		screenRectToLocal(screen_rc, &local_rc);
779
780		// Translate to parent coordinatess to check if we are in visible rectangle
781		local_rc.translate( getRect().mLeft, getRect().mBottom );
782
783		if ( !getRect().contains (local_rc) )
784		{
785			// Back to local coords and calculate position for scroller
786			S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom;
787			S32 top = mScrollbar->getDocPos() - local_rc.mTop + getRect().mTop;
788
789			S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
790									 bottom, // min vertical scroll
791									 top); // max vertical scroll 
792
793			mScrollbar->setDocPos( scroll_pos );
794		}
795		return 1;
796	}
797	else if (info.has("child_visibility_change"))
798	{
799		BOOL new_visibility = info["child_visibility_change"];
800		if (new_visibility)
801		{
802			// there is at least one visible tab
803			mNoVisibleTabsHelpText->setVisible(FALSE);
804		}
805		else
806		{
807			// it could be the latest visible tab, check all of them
808			updateNoTabsHelpTextVisibility();
809		}
810	}
811	return LLPanel::notifyParent(info);
812}
813void	LLAccordionCtrl::reset		()
814{
815	if(mScrollbar)
816		mScrollbar->setDocPos(0);
817}
818
819void LLAccordionCtrl::expandDefaultTab()
820{
821	if (mAccordionTabs.size() > 0)
822	{
823		LLAccordionCtrlTab* tab = mAccordionTabs.front();
824
825		if (!tab->getDisplayChildren())
826		{
827			tab->setDisplayChildren(true);
828		}
829
830		for (size_t i = 1; i < mAccordionTabs.size(); ++i)
831		{
832			tab = mAccordionTabs[i];
833
834			if (tab->getDisplayChildren())
835			{
836				tab->setDisplayChildren(false);
837			}
838		}
839
840		arrange();
841	}
842}
843
844void LLAccordionCtrl::sort()
845{
846	if (!mTabComparator)
847	{
848		llwarns << "No comparator specified for sorting accordion tabs." << llendl;
849		return;
850	}
851
852	std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator));
853	arrange();
854}
855
856void	LLAccordionCtrl::setFilterSubString(const std::string& filter_string)
857{
858	LLStringUtil::format_map_t args;
859	args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
860	std::string text = filter_string.empty() ? mNoVisibleTabsOrigString : mNoMatchedTabsOrigString;
861	LLStringUtil::format(text, args);
862
863	mNoVisibleTabsHelpText->setValue(text);
864}
865
866const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const
867{
868	typedef std::vector<LLAccordionCtrlTab*>::const_iterator tabs_const_iterator;
869
870	const LLAccordionCtrlTab* result = 0;
871
872	for (tabs_const_iterator i = mAccordionTabs.begin(); i != mAccordionTabs.end(); ++i)
873	{
874		if ((*i)->isExpanded())
875		{
876			result = *i;
877			break;
878		}
879	}
880
881	return result;
882}
883
884S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */)
885{
886	if(tab_index < 0)
887	{
888		return available_height;
889	}
890
891	S32 collapsed_tabs_height = 0;
892	S32 num_expanded = 0;
893
894	for(size_t n = tab_index; n < mAccordionTabs.size(); ++n)
895	{
896		if(!mAccordionTabs[n]->isExpanded())
897		{
898			collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight();
899		}
900		else
901		{
902			++num_expanded;
903		}
904	}
905
906	if(0 == num_expanded)
907	{
908		return available_height;
909	}
910
911	S32 expanded_tab_height = available_height - collapsed_tabs_height - BORDER_MARGIN; // top BORDER_MARGIN is added in arrange(), here we add bottom BORDER_MARGIN
912	expanded_tab_height /= num_expanded;
913	return expanded_tab_height;
914}