aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llui/lltabcontainer.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llui/lltabcontainer.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to '')
-rw-r--r--linden/indra/llui/lltabcontainer.cpp1487
1 files changed, 1487 insertions, 0 deletions
diff --git a/linden/indra/llui/lltabcontainer.cpp b/linden/indra/llui/lltabcontainer.cpp
new file mode 100644
index 0000000..4687414
--- /dev/null
+++ b/linden/indra/llui/lltabcontainer.cpp
@@ -0,0 +1,1487 @@
1/**
2 * @file lltabcontainer.cpp
3 * @brief LLTabContainerCommon base class
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include "lltabcontainer.h"
31
32#include "llfocusmgr.h"
33#include "llfontgl.h"
34#include "llgl.h"
35
36#include "llbutton.h"
37#include "llrect.h"
38#include "llpanel.h"
39#include "llresmgr.h"
40#include "llkeyboard.h"
41#include "llresizehandle.h"
42#include "llui.h"
43#include "lltextbox.h"
44#include "llcontrol.h"
45#include "llcriticaldamp.h"
46#include "lluictrlfactory.h"
47
48#include "lltabcontainervertical.h"
49
50#include "llglheaders.h"
51
52const F32 SCROLL_STEP_TIME = 0.4f;
53const S32 TAB_PADDING = 15;
54const S32 TABCNTR_TAB_MIN_WIDTH = 60;
55const S32 TABCNTR_TAB_MAX_WIDTH = 150;
56const S32 TABCNTR_TAB_PARTIAL_WIDTH = 12; // When tabs are parially obscured, how much can you still see.
57const S32 TABCNTR_TAB_HEIGHT = 16;
58const S32 TABCNTR_ARROW_BTN_SIZE = 16;
59const S32 TABCNTR_BUTTON_PANEL_OVERLAP = 1; // how many pixels the tab buttons and tab panels overlap.
60const S32 TABCNTR_TAB_H_PAD = 4;
61
62
63LLTabContainerCommon::LLTabContainerCommon(
64 const LLString& name, const LLRect& rect,
65 TabPosition pos,
66 void(*close_callback)(void*), void* callback_userdata,
67 BOOL bordered )
68 :
69 LLPanel(name, rect, bordered),
70 mCurrentTabIdx(-1),
71 mScrolled(FALSE),
72 mScrollPos(0),
73 mScrollPosPixels(0),
74 mMaxScrollPos(0),
75 mCloseCallback( close_callback ),
76 mCallbackUserdata( callback_userdata ),
77 mTitleBox(NULL),
78 mTopBorderHeight(LLPANEL_BORDER_WIDTH),
79 mTabPosition(pos)
80{
81 setMouseOpaque(FALSE);
82}
83
84
85LLTabContainerCommon::LLTabContainerCommon(
86 const LLString& name,
87 const LLString& rect_control,
88 TabPosition pos,
89 void(*close_callback)(void*), void* callback_userdata,
90 BOOL bordered )
91 :
92 LLPanel(name, rect_control, bordered),
93 mCurrentTabIdx(-1),
94 mScrolled(FALSE),
95 mScrollPos(0),
96 mScrollPosPixels(0),
97 mMaxScrollPos(0),
98 mCloseCallback( close_callback ),
99 mCallbackUserdata( callback_userdata ),
100 mTitleBox(NULL),
101 mTopBorderHeight(LLPANEL_BORDER_WIDTH),
102 mTabPosition(pos)
103{
104 setMouseOpaque(FALSE);
105}
106
107
108LLTabContainerCommon::~LLTabContainerCommon()
109{
110 std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
111}
112
113LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse) const
114{
115 tuple_list_t::const_iterator itor;
116 for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
117 {
118 LLPanel *panel = (*itor)->mTabPanel;
119 if (panel->getName() == name)
120 {
121 return panel;
122 }
123 }
124 if (recurse)
125 {
126 for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
127 {
128 LLPanel *panel = (*itor)->mTabPanel;
129 LLView *child = panel->getChildByName(name, recurse);
130 if (child)
131 {
132 return child;
133 }
134 }
135 }
136 return LLView::getChildByName(name, recurse);
137}
138
139void LLTabContainerCommon::addPlaceholder(LLPanel* child, const LLString& label)
140{
141 addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE);
142}
143
144void LLTabContainerCommon::removeTabPanel(LLPanel* child)
145{
146 BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this);
147
148 // If the tab being deleted is the selected one, select a different tab.
149 for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
150 {
151 LLTabTuple* tuple = *iter;
152 if( tuple->mTabPanel == child )
153 {
154 removeChild( tuple->mButton );
155 delete tuple->mButton;
156
157 removeChild( tuple->mTabPanel );
158// delete tuple->mTabPanel;
159
160 mTabList.erase( iter );
161 delete tuple;
162
163 break;
164 }
165 }
166 if (mCurrentTabIdx >= (S32)mTabList.size())
167 {
168 mCurrentTabIdx = mTabList.size()-1;
169 }
170 selectTab(mCurrentTabIdx);
171 if (has_focus)
172 {
173 LLPanel* panelp = getPanelByIndex(mCurrentTabIdx);
174 if (panelp)
175 {
176 panelp->setFocus(TRUE);
177 }
178 }
179
180 updateMaxScrollPos();
181}
182
183void LLTabContainerCommon::deleteAllTabs()
184{
185 // Remove all the tab buttons and delete them. Also, unlink all the child panels.
186 for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
187 {
188 LLTabTuple* tuple = *iter;
189
190 removeChild( tuple->mButton );
191 delete tuple->mButton;
192
193 removeChild( tuple->mTabPanel );
194// delete tuple->mTabPanel;
195 }
196
197 // Actually delete the tuples themselves
198 std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
199 mTabList.clear();
200
201 // And there isn't a current tab any more
202 mCurrentTabIdx = -1;
203}
204
205
206LLPanel* LLTabContainerCommon::getCurrentPanel()
207{
208 if (mCurrentTabIdx < 0 || mCurrentTabIdx >= (S32) mTabList.size()) return NULL;
209
210 return mTabList[mCurrentTabIdx]->mTabPanel;
211}
212
213LLTabContainerCommon::LLTabTuple* LLTabContainerCommon::getTabByPanel(LLPanel* child)
214{
215 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
216 {
217 LLTabTuple* tuple = *iter;
218 if( tuple->mTabPanel == child )
219 {
220 return tuple;
221 }
222 }
223 return NULL;
224}
225
226void LLTabContainerCommon::setTabChangeCallback(LLPanel* tab, void (*on_tab_clicked)(void*, bool))
227{
228 LLTabTuple* tuplep = getTabByPanel(tab);
229 if (tuplep)
230 {
231 tuplep->mOnChangeCallback = on_tab_clicked;
232 }
233}
234
235void LLTabContainerCommon::setTabUserData(LLPanel* tab, void* userdata)
236{
237 LLTabTuple* tuplep = getTabByPanel(tab);
238 if (tuplep)
239 {
240 tuplep->mUserData = userdata;
241 }
242}
243
244S32 LLTabContainerCommon::getCurrentPanelIndex()
245{
246 return mCurrentTabIdx;
247}
248
249S32 LLTabContainerCommon::getTabCount()
250{
251 return mTabList.size();
252}
253
254LLPanel* LLTabContainerCommon::getPanelByIndex(S32 index)
255{
256 if (index >= 0 && index < (S32)mTabList.size())
257 {
258 return mTabList[index]->mTabPanel;
259 }
260 return NULL;
261}
262
263S32 LLTabContainerCommon::getIndexForPanel(LLPanel* panel)
264{
265 for (S32 index = 0; index < (S32)mTabList.size(); index++)
266 {
267 if (mTabList[index]->mTabPanel == panel)
268 {
269 return index;
270 }
271 }
272 return -1;
273}
274
275S32 LLTabContainerCommon::getPanelIndexByTitle(const LLString& title)
276{
277 for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
278 {
279 if (title == mTabList[index]->mButton->getLabelSelected())
280 {
281 return index;
282 }
283 }
284 return -1;
285}
286
287LLPanel *LLTabContainerCommon::getPanelByName(const LLString& name)
288{
289 for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
290 {
291 LLPanel *panel = mTabList[index]->mTabPanel;
292 if (name == panel->getName())
293 {
294 return panel;
295 }
296 }
297 return NULL;
298}
299
300
301void LLTabContainerCommon::scrollNext()
302{
303 // No wrap
304 if( mScrollPos < mMaxScrollPos )
305 {
306 mScrollPos++;
307 }
308}
309
310void LLTabContainerCommon::scrollPrev()
311{
312 // No wrap
313 if( mScrollPos > 0 )
314 {
315 mScrollPos--;
316 }
317}
318
319void LLTabContainerCommon::enableTabButton(S32 which, BOOL enable)
320{
321 if (which >= 0 && which < (S32)mTabList.size())
322 {
323 mTabList[which]->mButton->setEnabled(enable);
324 }
325}
326
327BOOL LLTabContainerCommon::selectTabPanel(LLPanel* child)
328{
329 S32 idx = 0;
330 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
331 {
332 LLTabTuple* tuple = *iter;
333 if( tuple->mTabPanel == child )
334 {
335 return selectTab( idx );
336 }
337 idx++;
338 }
339 return FALSE;
340}
341
342BOOL LLTabContainerCommon::selectTabByName(const LLString& name)
343{
344 LLPanel* panel = getPanelByName(name);
345 if (!panel)
346 {
347 llwarns << "LLTabContainerCommon::selectTabByName("
348 << name << ") failed" << llendl;
349 return FALSE;
350 }
351
352 BOOL result = selectTabPanel(panel);
353 return result;
354}
355
356
357void LLTabContainerCommon::selectFirstTab()
358{
359 selectTab( 0 );
360}
361
362
363void LLTabContainerCommon::selectLastTab()
364{
365 selectTab( mTabList.size()-1 );
366}
367
368
369void LLTabContainerCommon::selectNextTab()
370{
371 BOOL tab_has_focus = FALSE;
372 if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
373 {
374 tab_has_focus = TRUE;
375 }
376 S32 idx = mCurrentTabIdx+1;
377 if (idx >= (S32)mTabList.size())
378 idx = 0;
379 while (!selectTab(idx) && idx != mCurrentTabIdx)
380 {
381 idx = (idx + 1 ) % (S32)mTabList.size();
382 }
383
384 if (tab_has_focus)
385 {
386 mTabList[idx]->mButton->setFocus(TRUE);
387 }
388}
389
390void LLTabContainerCommon::selectPrevTab()
391{
392 BOOL tab_has_focus = FALSE;
393 if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
394 {
395 tab_has_focus = TRUE;
396 }
397 S32 idx = mCurrentTabIdx-1;
398 if (idx < 0)
399 idx = mTabList.size()-1;
400 while (!selectTab(idx) && idx != mCurrentTabIdx)
401 {
402 idx = idx - 1;
403 if (idx < 0)
404 idx = mTabList.size()-1;
405 }
406 if (tab_has_focus)
407 {
408 mTabList[idx]->mButton->setFocus(TRUE);
409 }
410}
411
412
413void LLTabContainerCommon::reshape(S32 width, S32 height, BOOL called_from_parent)
414{
415 LLPanel::reshape( width, height, called_from_parent );
416 updateMaxScrollPos();
417}
418
419// static
420void LLTabContainerCommon::onTabBtn( void* userdata )
421{
422 LLTabTuple* tuple = (LLTabTuple*) userdata;
423 LLTabContainerCommon* self = tuple->mTabContainer;
424 self->selectTabPanel( tuple->mTabPanel );
425
426 if( tuple->mOnChangeCallback )
427 {
428 tuple->mOnChangeCallback( tuple->mUserData, true );
429 }
430
431 tuple->mTabPanel->setFocus(TRUE);
432}
433
434// static
435void LLTabContainerCommon::onCloseBtn( void* userdata )
436{
437 LLTabContainer* self = (LLTabContainer*) userdata;
438 if( self->mCloseCallback )
439 {
440 self->mCloseCallback( self->mCallbackUserdata );
441 }
442}
443
444// static
445void LLTabContainerCommon::onNextBtn( void* userdata )
446{
447 // Scroll tabs to the left
448 LLTabContainer* self = (LLTabContainer*) userdata;
449 if (!self->mScrolled)
450 {
451 self->scrollNext();
452 }
453 self->mScrolled = FALSE;
454}
455
456// static
457void LLTabContainerCommon::onNextBtnHeld( void* userdata )
458{
459 LLTabContainer* self = (LLTabContainer*) userdata;
460 if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
461 {
462 self->mScrollTimer.reset();
463 self->scrollNext();
464 self->mScrolled = TRUE;
465 }
466}
467
468// static
469void LLTabContainerCommon::onPrevBtn( void* userdata )
470{
471 LLTabContainer* self = (LLTabContainer*) userdata;
472 if (!self->mScrolled)
473 {
474 self->scrollPrev();
475 }
476 self->mScrolled = FALSE;
477}
478
479// static
480void LLTabContainerCommon::onPrevBtnHeld( void* userdata )
481{
482 LLTabContainer* self = (LLTabContainer*) userdata;
483 if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
484 {
485 self->mScrollTimer.reset();
486 self->scrollPrev();
487 self->mScrolled = TRUE;
488 }
489}
490
491BOOL LLTabContainerCommon::getTabPanelFlashing(LLPanel *child)
492{
493 LLTabTuple* tuple = getTabByPanel(child);
494 if( tuple )
495 {
496 return tuple->mButton->getFlashing();
497 }
498 return FALSE;
499}
500
501void LLTabContainerCommon::setTabPanelFlashing(LLPanel* child, BOOL state )
502{
503 LLTabTuple* tuple = getTabByPanel(child);
504 if( tuple )
505 {
506 tuple->mButton->setFlashing( state );
507 }
508}
509
510void LLTabContainerCommon::setTitle(const LLString& title)
511{
512 if (mTitleBox)
513 {
514 mTitleBox->setText( title );
515 }
516}
517
518const LLString LLTabContainerCommon::getPanelTitle(S32 index)
519{
520 if (index >= 0 && index < (S32)mTabList.size())
521 {
522 LLButton* tab_button = mTabList[index]->mButton;
523 return tab_button->getLabelSelected();
524 }
525 return LLString::null;
526}
527
528void LLTabContainerCommon::setTopBorderHeight(S32 height)
529{
530 mTopBorderHeight = height;
531}
532
533// Change the name of the button for the current tab.
534void LLTabContainerCommon::setCurrentTabName(const LLString& name)
535{
536 // Might not have a tab selected
537 if (mCurrentTabIdx < 0) return;
538
539 mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
540 mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
541}
542
543LLView* LLTabContainerCommon::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
544{
545 LLString name("tab_container");
546 node->getAttributeString("name", name);
547
548 // Figure out if we are creating a vertical or horizontal tab container.
549 bool is_vertical = false;
550 LLTabContainer::TabPosition tab_position = LLTabContainer::TOP;
551 if (node->hasAttribute("tab_position"))
552 {
553 LLString tab_position_string;
554 node->getAttributeString("tab_position", tab_position_string);
555 LLString::toLower(tab_position_string);
556
557 if ("top" == tab_position_string)
558 {
559 tab_position = LLTabContainer::TOP;
560 is_vertical = false;
561 }
562 else if ("bottom" == tab_position_string)
563 {
564 tab_position = LLTabContainer::BOTTOM;
565 is_vertical = false;
566 }
567 else if ("left" == tab_position_string)
568 {
569 is_vertical = true;
570 }
571 }
572 BOOL border = FALSE;
573 node->getAttributeBOOL("border", border);
574
575 // Create the correct container type.
576 LLTabContainerCommon* tab_container = NULL;
577
578 if (is_vertical)
579 {
580 // Vertical tabs can specify tab width
581 U32 tab_width = TABCNTRV_TAB_WIDTH;
582 if (node->hasAttribute("tab_width"))
583 {
584 node->getAttributeU32("tab_width", tab_width);
585 }
586
587 tab_container = new LLTabContainerVertical(name,
588 LLRect::null,
589 NULL,
590 NULL,
591 tab_width,
592 border);
593
594 }
595 else // horizontal tab container
596 {
597 // Horizontal tabs can have a title (?)
598 LLString title(LLString::null);
599 if (node->hasAttribute("title"))
600 {
601 node->getAttributeString("title", title);
602 }
603
604 tab_container = new LLTabContainer(name,
605 LLRect::null,
606 tab_position,
607 NULL,
608 NULL,
609 title,
610 border);
611
612 if(node->hasAttribute("tab_min_width"))
613 {
614 S32 minTabWidth=0;
615 node->getAttributeS32("tab_min_width",minTabWidth);
616 ((LLTabContainer*)tab_container)->setMinTabWidth(minTabWidth);
617 }
618 if(node->hasAttribute("tab_max_width"))
619 {
620 S32 maxTabWidth=0;
621 node->getAttributeS32("tab_max_width",maxTabWidth);
622 ((LLTabContainer*)tab_container)->setMaxTabWidth(maxTabWidth);
623 }
624 }
625
626 tab_container->setPanelParameters(node, parent);
627
628 if (LLFloater::getFloaterHost())
629 {
630 LLFloater::getFloaterHost()->setTabContainer(tab_container);
631 }
632
633 //parent->addChild(tab_container);
634
635 // Add all tab panels.
636 LLXMLNodePtr child;
637 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
638 {
639 LLView *control = factory->createCtrlWidget(tab_container, child);
640 if (control && control->isPanel())
641 {
642 LLPanel* panelp = (LLPanel*)control;
643 LLString label;
644 child->getAttributeString("label", label);
645 if (label.empty())
646 {
647 label = panelp->getLabel();
648 }
649 BOOL placeholder = FALSE;
650 child->getAttributeBOOL("placeholder", placeholder);
651 tab_container->addTabPanel(panelp, label.c_str(), false,
652 NULL, NULL, 0, placeholder);
653 }
654 }
655
656 tab_container->selectFirstTab();
657
658 tab_container->postBuild();
659
660 tab_container->initButtons(); // now that we have the correct rect
661
662 return tab_container;
663}
664
665void LLTabContainerCommon::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point)
666{
667 switch(insertion_point)
668 {
669 case START:
670 // insert the new tab in the front of the list
671 mTabList.insert(mTabList.begin(), tuple);
672 break;
673 case RIGHT_OF_CURRENT:
674 // insert the new tab after the current tab
675 {
676 tuple_list_t::iterator current_iter = mTabList.begin() + mCurrentTabIdx + 1;
677 mTabList.insert(current_iter, tuple);
678 }
679 break;
680 case END:
681 default:
682 mTabList.push_back( tuple );
683 }
684}
685
686
687LLTabContainer::LLTabContainer(
688 const LLString& name, const LLRect& rect, TabPosition pos,
689 void(*close_callback)(void*), void* callback_userdata,
690 const LLString& title, BOOL bordered )
691 :
692 LLTabContainerCommon(name, rect, pos, close_callback, callback_userdata, bordered),
693 mLeftArrowBtn(NULL),
694 mRightArrowBtn(NULL),
695 mRightTabBtnOffset(0),
696 mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
697 mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
698 mTotalTabWidth(0)
699{
700 initButtons( );
701}
702
703LLTabContainer::LLTabContainer(
704 const LLString& name, const LLString& rect_control, TabPosition pos,
705 void(*close_callback)(void*), void* callback_userdata,
706 const LLString& title, BOOL bordered )
707 :
708 LLTabContainerCommon(name, rect_control, pos, close_callback, callback_userdata, bordered),
709 mLeftArrowBtn(NULL),
710 mRightArrowBtn(NULL),
711 mRightTabBtnOffset(0),
712 mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
713 mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
714 mTotalTabWidth(0)
715{
716 initButtons( );
717}
718
719void LLTabContainer::initButtons()
720{
721 // Hack:
722 if (mRect.getHeight() == 0 || mLeftArrowBtn)
723 {
724 return; // Don't have a rect yet or already got called
725 }
726
727 LLString out_id;
728 LLString in_id;
729
730 S32 arrow_fudge = 1; // match new art better
731
732 // tabs on bottom reserve room for resize handle (just in case)
733 if (mTabPosition == BOTTOM)
734 {
735 mRightTabBtnOffset = RESIZE_HANDLE_WIDTH;
736 }
737
738 // Left and right scroll arrows (for when there are too many tabs to show all at once).
739 S32 btn_top = (mTabPosition == TOP ) ? mRect.getHeight() - mTopBorderHeight : TABCNTR_ARROW_BTN_SIZE + 1;
740
741 LLRect left_arrow_btn_rect;
742 left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
743
744 S32 right_pad = TABCNTR_ARROW_BTN_SIZE + LLPANEL_BORDER_WIDTH + 1;
745 LLRect right_arrow_btn_rect;
746 right_arrow_btn_rect.setLeftTopAndSize( mRect.getWidth() - mRightTabBtnOffset - right_pad,
747 btn_top + arrow_fudge,
748 TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
749
750 out_id = "UIImgBtnScrollLeftOutUUID";
751 in_id = "UIImgBtnScrollLeftInUUID";
752 mLeftArrowBtn = new LLButton(
753 "Left Arrow", left_arrow_btn_rect,
754 out_id, in_id, "",
755 &LLTabContainer::onPrevBtn, this, LLFontGL::sSansSerif );
756 mLeftArrowBtn->setHeldDownCallback(onPrevBtnHeld);
757 mLeftArrowBtn->setFollowsLeft();
758 mLeftArrowBtn->setSaveToXML(false);
759 mLeftArrowBtn->setTabStop(FALSE);
760 addChild(mLeftArrowBtn);
761
762 out_id = "UIImgBtnScrollRightOutUUID";
763 in_id = "UIImgBtnScrollRightInUUID";
764 mRightArrowBtn = new LLButton(
765 "Right Arrow", right_arrow_btn_rect,
766 out_id, in_id, "",
767 &LLTabContainer::onNextBtn, this,
768 LLFontGL::sSansSerif);
769 mRightArrowBtn->setFollowsRight();
770 mRightArrowBtn->setHeldDownCallback(onNextBtnHeld);
771 mRightArrowBtn->setSaveToXML(false);
772 mRightArrowBtn->setTabStop(FALSE);
773 addChild(mRightArrowBtn);
774
775 if( mTabPosition == TOP )
776 {
777 mRightArrowBtn->setFollowsTop();
778 mLeftArrowBtn->setFollowsTop();
779 }
780 else
781 {
782 mRightArrowBtn->setFollowsBottom();
783 mLeftArrowBtn->setFollowsBottom();
784 }
785
786 // set default tab group to be panel contents
787 mDefaultTabGroup = 1;
788}
789
790LLTabContainer::~LLTabContainer()
791{
792}
793
794void LLTabContainer::addTabPanel(LLPanel* child,
795 const LLString& label,
796 BOOL select,
797 void (*on_tab_clicked)(void*, bool),
798 void* userdata,
799 S32 indent,
800 BOOL placeholder,
801 eInsertionPoint insertion_point)
802{
803 if (child->getParent() == this)
804 {
805 // already a child of mine
806 return;
807 }
808 const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
809
810 // Store the original label for possible xml export.
811 child->setLabel(label);
812 LLString trimmed_label = label;
813 LLString::trim(trimmed_label);
814
815 S32 button_width = llclamp(font->getWidth(trimmed_label) + TAB_PADDING, mMinTabWidth, mMaxTabWidth);
816
817 // Tab panel
818 S32 tab_panel_top;
819 S32 tab_panel_bottom;
820 if( LLTabContainer::TOP == mTabPosition )
821 {
822 tab_panel_top = mRect.getHeight() - mTopBorderHeight - (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP);
823 tab_panel_bottom = LLPANEL_BORDER_WIDTH;
824 }
825 else
826 {
827 tab_panel_top = mRect.getHeight() - mTopBorderHeight;
828 tab_panel_bottom = (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP); // Run to the edge, covering up the border
829 }
830
831 LLRect tab_panel_rect(
832 LLPANEL_BORDER_WIDTH,
833 tab_panel_top,
834 mRect.getWidth()-LLPANEL_BORDER_WIDTH,
835 tab_panel_bottom );
836
837 child->setFollowsAll();
838 child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
839 child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
840 child->setBackgroundVisible( FALSE ); // No need to overdraw
841 // add this child later
842
843 child->setVisible( FALSE ); // Will be made visible when selected
844
845 mTotalTabWidth += button_width;
846
847 // Tab button
848 LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw().
849 LLString tab_img;
850 LLString tab_selected_img;
851 S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel
852
853 if( LLTabContainer::TOP == mTabPosition )
854 {
855 btn_rect.setLeftTopAndSize( 0, mRect.getHeight() - mTopBorderHeight + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
856 tab_img = "UIImgBtnTabTopOutUUID";
857 tab_selected_img = "UIImgBtnTabTopInUUID";
858 }
859 else
860 {
861 btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
862 tab_img = "UIImgBtnTabBottomOutUUID";
863 tab_selected_img = "UIImgBtnTabBottomInUUID";
864 }
865
866 if (placeholder)
867 {
868 // *FIX: wont work for horizontal tabs
869 btn_rect.translate(0, -LLBUTTON_V_PAD-2);
870 LLString box_label = trimmed_label;
871 LLTextBox* text = new LLTextBox(box_label, btn_rect, box_label, font);
872 addChild( text, 0 );
873
874 LLButton* btn = new LLButton("", LLRect(0,0,0,0));
875 LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata, text );
876 addChild( btn, 0 );
877 addChild( child, 1 );
878 insertTuple(tuple, insertion_point);
879 }
880 else
881 {
882 LLString tooltip = trimmed_label;
883 tooltip += "\nCtrl-[ for previous tab";
884 tooltip += "\nCtrl-] for next tab";
885
886 LLButton* btn = new LLButton(
887 LLString(child->getName()) + " tab",
888 btn_rect,
889 tab_img, tab_selected_img, "",
890 &LLTabContainer::onTabBtn, NULL, // set userdata below
891 font,
892 trimmed_label, trimmed_label );
893 btn->setSaveToXML(false);
894 btn->setVisible( FALSE );
895 btn->setToolTip( tooltip );
896 btn->setScaleImage(TRUE);
897 btn->setFixedBorder(14, 14);
898
899 // Try to squeeze in a bit more text
900 btn->setLeftHPad( 4 );
901 btn->setRightHPad( 2 );
902 btn->setHAlign(LLFontGL::LEFT);
903 btn->setTabStop(FALSE);
904 if (indent)
905 {
906 btn->setLeftHPad(indent);
907 }
908
909 if( mTabPosition == TOP )
910 {
911 btn->setFollowsTop();
912 }
913 else
914 {
915 btn->setFollowsBottom();
916 }
917
918 LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata );
919 btn->setCallbackUserData( tuple );
920 addChild( btn, 0 );
921 addChild( child, 1 );
922 insertTuple(tuple, insertion_point);
923 }
924
925 updateMaxScrollPos();
926
927 if( select )
928 {
929 selectLastTab();
930 }
931}
932
933void LLTabContainer::removeTabPanel(LLPanel* child)
934{
935 // Adjust the total tab width.
936 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
937 {
938 LLTabTuple* tuple = *iter;
939 if( tuple->mTabPanel == child )
940 {
941 mTotalTabWidth -= tuple->mButton->getRect().getWidth();
942 break;
943 }
944 }
945
946 LLTabContainerCommon::removeTabPanel(child);
947}
948
949void LLTabContainer::setPanelTitle(S32 index, const LLString& title)
950{
951 if (index >= 0 && index < (S32)mTabList.size())
952 {
953 LLButton* tab_button = mTabList[index]->mButton;
954 const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
955 mTotalTabWidth -= tab_button->getRect().getWidth();
956 tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
957 mTotalTabWidth += tab_button->getRect().getWidth();
958 tab_button->setLabelSelected(title);
959 tab_button->setLabelUnselected(title);
960 }
961 updateMaxScrollPos();
962}
963
964
965void LLTabContainer::updateMaxScrollPos()
966{
967 S32 tab_space = 0;
968 S32 available_space = 0;
969 tab_space = mTotalTabWidth;
970 available_space = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD);
971
972 if( tab_space > available_space )
973 {
974 S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
975 // subtract off reserved portion on left
976 available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH;
977
978 S32 running_tab_width = 0;
979 mMaxScrollPos = mTabList.size();
980 for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it)
981 {
982 running_tab_width += (*tab_it)->mButton->getRect().getWidth();
983 if (running_tab_width > available_width_with_arrows)
984 {
985 break;
986 }
987 mMaxScrollPos--;
988 }
989 // in case last tab doesn't actually fit on screen, make it the last scrolling position
990 mMaxScrollPos = llmin(mMaxScrollPos, (S32)mTabList.size() - 1);
991 }
992 else
993 {
994 mMaxScrollPos = 0;
995 mScrollPos = 0;
996 }
997 if (mScrollPos > mMaxScrollPos)
998 {
999 mScrollPos = mMaxScrollPos;
1000 }
1001}
1002
1003void LLTabContainer::commitHoveredButton(S32 x, S32 y)
1004{
1005 if (gFocusMgr.getMouseCapture() == this)
1006 {
1007 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1008 {
1009 LLTabTuple* tuple = *iter;
1010 tuple->mButton->setVisible( TRUE );
1011 S32 local_x = x - tuple->mButton->getRect().mLeft;
1012 S32 local_y = y - tuple->mButton->getRect().mBottom;
1013 if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
1014 {
1015 tuple->mButton->onCommit();
1016 }
1017 }
1018 }
1019}
1020
1021void LLTabContainer::setMinTabWidth(S32 width)
1022{
1023 mMinTabWidth = width;
1024}
1025
1026void LLTabContainer::setMaxTabWidth(S32 width)
1027{
1028 mMaxTabWidth = width;
1029}
1030
1031S32 LLTabContainer::getMinTabWidth() const
1032{
1033 return mMinTabWidth;
1034}
1035
1036S32 LLTabContainer::getMaxTabWidth() const
1037{
1038 return mMaxTabWidth;
1039}
1040
1041BOOL LLTabContainer::selectTab(S32 which)
1042{
1043 if (which >= (S32)mTabList.size()) return FALSE;
1044 if (which < 0) return FALSE;
1045
1046 //if( gFocusMgr.childHasKeyboardFocus( this ) )
1047 //{
1048 // gFocusMgr.setKeyboardFocus( NULL, NULL );
1049 //}
1050
1051 LLTabTuple* selected_tuple = mTabList[which];
1052 if (!selected_tuple)
1053 {
1054 return FALSE;
1055 }
1056
1057 if (mTabList[which]->mButton->getEnabled())
1058 {
1059 mCurrentTabIdx = which;
1060
1061 S32 i = 0;
1062 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1063 {
1064 LLTabTuple* tuple = *iter;
1065 BOOL is_selected = ( tuple == selected_tuple );
1066 tuple->mTabPanel->setVisible( is_selected );
1067// tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
1068 tuple->mButton->setToggleState( is_selected );
1069 // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
1070 tuple->mButton->setTabStop( is_selected );
1071
1072 if( is_selected && mMaxScrollPos > 0)
1073 {
1074 // Make sure selected tab is within scroll region
1075 if( i < mScrollPos )
1076 {
1077 mScrollPos = i;
1078 }
1079 else
1080 {
1081 S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
1082 S32 running_tab_width = tuple->mButton->getRect().getWidth();
1083 S32 j = i - 1;
1084 S32 min_scroll_pos = i;
1085 if (running_tab_width < available_width_with_arrows)
1086 {
1087 while (j >= 0)
1088 {
1089 LLTabTuple* other_tuple = mTabList[j];
1090 running_tab_width += other_tuple->mButton->getRect().getWidth();
1091 if (running_tab_width > available_width_with_arrows)
1092 {
1093 break;
1094 }
1095 j--;
1096 }
1097 min_scroll_pos = j + 1;
1098 }
1099 mScrollPos = llclamp(mScrollPos, min_scroll_pos, i);
1100 mScrollPos = llmin(mScrollPos, mMaxScrollPos);
1101 }
1102 }
1103 i++;
1104 }
1105 if( selected_tuple->mOnChangeCallback )
1106 {
1107 selected_tuple->mOnChangeCallback( selected_tuple->mUserData, false );
1108 }
1109 return TRUE;
1110 }
1111 else
1112 {
1113 return FALSE;
1114 }
1115}
1116
1117void LLTabContainer::draw()
1118{
1119 S32 target_pixel_scroll = 0;
1120 S32 cur_scroll_pos = mScrollPos;
1121 if (cur_scroll_pos > 0)
1122 {
1123 S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
1124 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1125 {
1126 if (cur_scroll_pos == 0)
1127 {
1128 break;
1129 }
1130 target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
1131 cur_scroll_pos--;
1132 }
1133
1134 // Show part of the tab to the left of what is fully visible
1135 target_pixel_scroll -= TABCNTR_TAB_PARTIAL_WIDTH;
1136 // clamp so that rightmost tab never leaves right side of screen
1137 target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
1138 }
1139
1140 mScrollPosPixels = (S32)lerp((F32)mScrollPosPixels, (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f));
1141 if( getVisible() )
1142 {
1143 BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0);
1144 mLeftArrowBtn->setVisible( has_scroll_arrows );
1145 mRightArrowBtn->setVisible( has_scroll_arrows );
1146
1147 // Set the leftmost position of the tab buttons.
1148 S32 left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? TABCNTR_ARROW_BTN_SIZE : TABCNTR_TAB_H_PAD);
1149 left -= mScrollPosPixels;
1150
1151 // Hide all the buttons
1152 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1153 {
1154 LLTabTuple* tuple = *iter;
1155 tuple->mButton->setVisible( FALSE );
1156 }
1157
1158 LLPanel::draw();
1159
1160 // Show all the buttons
1161 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1162 {
1163 LLTabTuple* tuple = *iter;
1164 tuple->mButton->setVisible( TRUE );
1165 }
1166
1167 // Draw some of the buttons...
1168
1169 LLGLEnable scissor_test(has_scroll_arrows ? GL_SCISSOR_TEST : GL_FALSE);
1170 if( has_scroll_arrows )
1171 {
1172 // ...but clip them.
1173 S32 x1 = mLeftArrowBtn->getRect().mRight;
1174 S32 y1 = 0;
1175 S32 x2 = mRightArrowBtn->getRect().mLeft;
1176 S32 y2 = 1;
1177 if (mTabList.size() > 0)
1178 {
1179 y2 = mTabList[0]->mButton->getRect().mTop;
1180 }
1181 LLUI::setScissorRegionLocal(LLRect(x1, y2, x2, y1));
1182 }
1183
1184 S32 max_scroll_visible = mTabList.size() - mMaxScrollPos + mScrollPos;
1185 S32 idx = 0;
1186 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1187 {
1188 LLTabTuple* tuple = *iter;
1189 tuple->mButton->translate( left - tuple->mButton->getRect().mLeft, 0 );
1190 left += tuple->mButton->getRect().getWidth();
1191
1192 if( idx < mScrollPos )
1193 {
1194 if( tuple->mButton->getFlashing() )
1195 {
1196 mLeftArrowBtn->setFlashing( TRUE );
1197 }
1198 }
1199 else
1200 if( max_scroll_visible < idx )
1201 {
1202 if( tuple->mButton->getFlashing() )
1203 {
1204 mRightArrowBtn->setFlashing( TRUE );
1205 }
1206 }
1207
1208 LLUI::pushMatrix();
1209 {
1210 LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
1211 tuple->mButton->draw();
1212 }
1213 LLUI::popMatrix();
1214
1215 idx++;
1216 }
1217
1218 mLeftArrowBtn->setFlashing(FALSE);
1219 mRightArrowBtn->setFlashing(FALSE);
1220 }
1221}
1222
1223
1224void LLTabContainer::setRightTabBtnOffset(S32 offset)
1225{
1226 mRightArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
1227 mRightTabBtnOffset = offset;
1228 updateMaxScrollPos();
1229}
1230
1231BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
1232{
1233 BOOL handled = FALSE;
1234 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1235
1236 if (has_scroll_arrows)
1237 {
1238 if (mLeftArrowBtn->getRect().pointInRect(x, y))
1239 {
1240 S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
1241 S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
1242 handled = mLeftArrowBtn->handleMouseDown(local_x, local_y, mask);
1243 }
1244 else if (mRightArrowBtn->getRect().pointInRect(x, y))
1245 {
1246 S32 local_x = x - mRightArrowBtn->getRect().mLeft;
1247 S32 local_y = y - mRightArrowBtn->getRect().mBottom;
1248 handled = mRightArrowBtn->handleMouseDown(local_x, local_y, mask);
1249 }
1250 }
1251 if (!handled)
1252 {
1253 handled = LLPanel::handleMouseDown( x, y, mask );
1254 }
1255
1256 if (mTabList.size() > 0)
1257 {
1258 LLTabTuple* firsttuple = mTabList[0];
1259 LLRect tab_rect(has_scroll_arrows ? mLeftArrowBtn->getRect().mRight : mLeftArrowBtn->getRect().mLeft,
1260 firsttuple->mButton->getRect().mTop,
1261 has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mRightArrowBtn->getRect().mRight,
1262 firsttuple->mButton->getRect().mBottom );
1263 if( tab_rect.pointInRect( x, y ) )
1264 {
1265 LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton;
1266 gFocusMgr.setMouseCapture(this, NULL);
1267 gFocusMgr.setKeyboardFocus(tab_button, NULL);
1268 }
1269 }
1270 return handled;
1271}
1272
1273BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
1274{
1275 BOOL handled = FALSE;
1276 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1277
1278 if (has_scroll_arrows)
1279 {
1280 if (mLeftArrowBtn->getRect().pointInRect(x, y))
1281 {
1282 S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
1283 S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
1284 handled = mLeftArrowBtn->handleHover(local_x, local_y, mask);
1285 }
1286 else if (mRightArrowBtn->getRect().pointInRect(x, y))
1287 {
1288 S32 local_x = x - mRightArrowBtn->getRect().mLeft;
1289 S32 local_y = y - mRightArrowBtn->getRect().mBottom;
1290 handled = mRightArrowBtn->handleHover(local_x, local_y, mask);
1291 }
1292 }
1293 if (!handled)
1294 {
1295 handled = LLPanel::handleHover(x, y, mask);
1296 }
1297
1298 commitHoveredButton(x, y);
1299 return handled;
1300}
1301
1302BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
1303{
1304 BOOL handled = FALSE;
1305 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1306
1307 if (has_scroll_arrows)
1308 {
1309 if (mLeftArrowBtn->getRect().pointInRect(x, y))
1310 {
1311 S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
1312 S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
1313 handled = mLeftArrowBtn->handleMouseUp(local_x, local_y, mask);
1314 }
1315 else if (mRightArrowBtn->getRect().pointInRect(x, y))
1316 {
1317 S32 local_x = x - mRightArrowBtn->getRect().mLeft;
1318 S32 local_y = y - mRightArrowBtn->getRect().mBottom;
1319 handled = mRightArrowBtn->handleMouseUp(local_x, local_y, mask);
1320 }
1321 }
1322 if (!handled)
1323 {
1324 handled = LLPanel::handleMouseUp( x, y, mask );
1325 }
1326
1327 commitHoveredButton(x, y);
1328 LLPanel* cur_panel = getCurrentPanel();
1329 if (gFocusMgr.getMouseCapture() == this)
1330 {
1331 if (cur_panel)
1332 {
1333 if (!cur_panel->focusFirstItem(FALSE))
1334 {
1335 // if nothing in the panel gets focus, make sure the new tab does
1336 // otherwise the last tab might keep focus
1337 mTabList[getCurrentPanelIndex()]->mButton->setFocus(TRUE);
1338 }
1339 }
1340 gFocusMgr.setMouseCapture(NULL, NULL);
1341 }
1342 return handled;
1343}
1344
1345BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect )
1346{
1347 BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect );
1348 if (!handled && mTabList.size() > 0 && getVisible() && pointInView( x, y ) )
1349 {
1350 LLTabTuple* firsttuple = mTabList[0];
1351
1352 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1353 LLRect clip(
1354 has_scroll_arrows ? mLeftArrowBtn->getRect().mRight : mLeftArrowBtn->getRect().mLeft,
1355 firsttuple->mButton->getRect().mTop,
1356 has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mRightArrowBtn->getRect().mRight,
1357 0 );
1358 if( clip.pointInRect( x, y ) )
1359 {
1360 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1361 {
1362 LLTabTuple* tuple = *iter;
1363 tuple->mButton->setVisible( TRUE );
1364 S32 local_x = x - tuple->mButton->getRect().mLeft;
1365 S32 local_y = y - tuple->mButton->getRect().mBottom;
1366 handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect );
1367 if( handled )
1368 {
1369 break;
1370 }
1371 }
1372 }
1373
1374 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1375 {
1376 LLTabTuple* tuple = *iter;
1377 tuple->mButton->setVisible( FALSE );
1378 }
1379 }
1380 return handled;
1381}
1382
1383BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
1384{
1385 if (!getEnabled()) return FALSE;
1386
1387 if (!gFocusMgr.childHasKeyboardFocus(this)) return FALSE;
1388
1389 BOOL handled = FALSE;
1390 if (key == '[' && mask == MASK_CONTROL)
1391 {
1392 selectPrevTab();
1393 handled = TRUE;
1394 }
1395 else if (key == ']' && mask == MASK_CONTROL)
1396 {
1397 selectNextTab();
1398 handled = TRUE;
1399 }
1400
1401 if (handled)
1402 {
1403 if (getCurrentPanel())
1404 {
1405 getCurrentPanel()->setFocus(TRUE);
1406 }
1407 }
1408
1409 if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
1410 {
1411 // if child has focus, but not the current panel, focus
1412 // is on a button
1413 switch(key)
1414 {
1415 case KEY_UP:
1416 if (getTabPosition() == BOTTOM && getCurrentPanel())
1417 {
1418 getCurrentPanel()->setFocus(TRUE);
1419 }
1420 handled = TRUE;
1421 break;
1422 case KEY_DOWN:
1423 if (getTabPosition() == TOP && getCurrentPanel())
1424 {
1425 getCurrentPanel()->setFocus(TRUE);
1426 }
1427 handled = TRUE;
1428 break;
1429 case KEY_LEFT:
1430 selectPrevTab();
1431 handled = TRUE;
1432 break;
1433 case KEY_RIGHT:
1434 selectNextTab();
1435 handled = TRUE;
1436 break;
1437 default:
1438 break;
1439 }
1440 }
1441 return handled;
1442}
1443
1444// virtual
1445LLXMLNodePtr LLTabContainer::getXML(bool save_children) const
1446{
1447 LLXMLNodePtr node = LLTabContainerCommon::getXML();
1448
1449 node->createChild("tab_position", TRUE)->setStringValue((mTabPosition == TOP ? "top" : "bottom"));
1450
1451 return node;
1452}
1453
1454BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, LLString &tooltip)
1455{
1456 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1457
1458 if (has_scroll_arrows)
1459 {
1460 if (mLeftArrowBtn->getRect().pointInRect(x, y))
1461 {
1462 S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
1463 S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
1464 mLeftArrowBtn->handleHover(local_x, local_y, mask);
1465 }
1466 else if (mRightArrowBtn->getRect().pointInRect(x, y))
1467 {
1468 S32 local_x = x - mRightArrowBtn->getRect().mLeft;
1469 S32 local_y = y - mRightArrowBtn->getRect().mBottom;
1470 mRightArrowBtn->handleHover(local_x, local_y, mask);
1471 }
1472 }
1473
1474 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1475 {
1476 LLTabTuple* tuple = *iter;
1477 tuple->mButton->setVisible( TRUE );
1478 S32 local_x = x - tuple->mButton->getRect().mLeft;
1479 S32 local_y = y - tuple->mButton->getRect().mBottom;
1480 if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
1481 {
1482 tuple->mButton->onCommit();
1483 }
1484 }
1485
1486 return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip);
1487}