diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llui/lltabcontainer.cpp | |
parent | README.txt (diff) | |
download | meta-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.cpp | 1487 |
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 | |||
52 | const F32 SCROLL_STEP_TIME = 0.4f; | ||
53 | const S32 TAB_PADDING = 15; | ||
54 | const S32 TABCNTR_TAB_MIN_WIDTH = 60; | ||
55 | const S32 TABCNTR_TAB_MAX_WIDTH = 150; | ||
56 | const S32 TABCNTR_TAB_PARTIAL_WIDTH = 12; // When tabs are parially obscured, how much can you still see. | ||
57 | const S32 TABCNTR_TAB_HEIGHT = 16; | ||
58 | const S32 TABCNTR_ARROW_BTN_SIZE = 16; | ||
59 | const S32 TABCNTR_BUTTON_PANEL_OVERLAP = 1; // how many pixels the tab buttons and tab panels overlap. | ||
60 | const S32 TABCNTR_TAB_H_PAD = 4; | ||
61 | |||
62 | |||
63 | LLTabContainerCommon::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 | |||
85 | LLTabContainerCommon::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 | |||
108 | LLTabContainerCommon::~LLTabContainerCommon() | ||
109 | { | ||
110 | std::for_each(mTabList.begin(), mTabList.end(), DeletePointer()); | ||
111 | } | ||
112 | |||
113 | LLView* 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 | |||
139 | void LLTabContainerCommon::addPlaceholder(LLPanel* child, const LLString& label) | ||
140 | { | ||
141 | addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE); | ||
142 | } | ||
143 | |||
144 | void 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 | |||
183 | void 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 | |||
206 | LLPanel* LLTabContainerCommon::getCurrentPanel() | ||
207 | { | ||
208 | if (mCurrentTabIdx < 0 || mCurrentTabIdx >= (S32) mTabList.size()) return NULL; | ||
209 | |||
210 | return mTabList[mCurrentTabIdx]->mTabPanel; | ||
211 | } | ||
212 | |||
213 | LLTabContainerCommon::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 | |||
226 | void 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 | |||
235 | void LLTabContainerCommon::setTabUserData(LLPanel* tab, void* userdata) | ||
236 | { | ||
237 | LLTabTuple* tuplep = getTabByPanel(tab); | ||
238 | if (tuplep) | ||
239 | { | ||
240 | tuplep->mUserData = userdata; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | S32 LLTabContainerCommon::getCurrentPanelIndex() | ||
245 | { | ||
246 | return mCurrentTabIdx; | ||
247 | } | ||
248 | |||
249 | S32 LLTabContainerCommon::getTabCount() | ||
250 | { | ||
251 | return mTabList.size(); | ||
252 | } | ||
253 | |||
254 | LLPanel* 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 | |||
263 | S32 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 | |||
275 | S32 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 | |||
287 | LLPanel *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 | |||
301 | void LLTabContainerCommon::scrollNext() | ||
302 | { | ||
303 | // No wrap | ||
304 | if( mScrollPos < mMaxScrollPos ) | ||
305 | { | ||
306 | mScrollPos++; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | void LLTabContainerCommon::scrollPrev() | ||
311 | { | ||
312 | // No wrap | ||
313 | if( mScrollPos > 0 ) | ||
314 | { | ||
315 | mScrollPos--; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | void 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 | |||
327 | BOOL 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 | |||
342 | BOOL 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 | |||
357 | void LLTabContainerCommon::selectFirstTab() | ||
358 | { | ||
359 | selectTab( 0 ); | ||
360 | } | ||
361 | |||
362 | |||
363 | void LLTabContainerCommon::selectLastTab() | ||
364 | { | ||
365 | selectTab( mTabList.size()-1 ); | ||
366 | } | ||
367 | |||
368 | |||
369 | void 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 | |||
390 | void 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 | |||
413 | void 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 | ||
420 | void 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 | ||
435 | void 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 | ||
445 | void 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 | ||
457 | void 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 | ||
469 | void 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 | ||
480 | void 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 | |||
491 | BOOL 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 | |||
501 | void LLTabContainerCommon::setTabPanelFlashing(LLPanel* child, BOOL state ) | ||
502 | { | ||
503 | LLTabTuple* tuple = getTabByPanel(child); | ||
504 | if( tuple ) | ||
505 | { | ||
506 | tuple->mButton->setFlashing( state ); | ||
507 | } | ||
508 | } | ||
509 | |||
510 | void LLTabContainerCommon::setTitle(const LLString& title) | ||
511 | { | ||
512 | if (mTitleBox) | ||
513 | { | ||
514 | mTitleBox->setText( title ); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | const 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 | |||
528 | void LLTabContainerCommon::setTopBorderHeight(S32 height) | ||
529 | { | ||
530 | mTopBorderHeight = height; | ||
531 | } | ||
532 | |||
533 | // Change the name of the button for the current tab. | ||
534 | void 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 | |||
543 | LLView* 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 | |||
665 | void 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 | |||
687 | LLTabContainer::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 | |||
703 | LLTabContainer::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 | |||
719 | void 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 | |||
790 | LLTabContainer::~LLTabContainer() | ||
791 | { | ||
792 | } | ||
793 | |||
794 | void 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 | |||
933 | void 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 | |||
949 | void 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 | |||
965 | void 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 | |||
1003 | void 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 | |||
1021 | void LLTabContainer::setMinTabWidth(S32 width) | ||
1022 | { | ||
1023 | mMinTabWidth = width; | ||
1024 | } | ||
1025 | |||
1026 | void LLTabContainer::setMaxTabWidth(S32 width) | ||
1027 | { | ||
1028 | mMaxTabWidth = width; | ||
1029 | } | ||
1030 | |||
1031 | S32 LLTabContainer::getMinTabWidth() const | ||
1032 | { | ||
1033 | return mMinTabWidth; | ||
1034 | } | ||
1035 | |||
1036 | S32 LLTabContainer::getMaxTabWidth() const | ||
1037 | { | ||
1038 | return mMaxTabWidth; | ||
1039 | } | ||
1040 | |||
1041 | BOOL 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 | |||
1117 | void 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 | |||
1224 | void LLTabContainer::setRightTabBtnOffset(S32 offset) | ||
1225 | { | ||
1226 | mRightArrowBtn->translate( -offset - mRightTabBtnOffset, 0 ); | ||
1227 | mRightTabBtnOffset = offset; | ||
1228 | updateMaxScrollPos(); | ||
1229 | } | ||
1230 | |||
1231 | BOOL 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 | |||
1273 | BOOL 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 | |||
1302 | BOOL 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 | |||
1345 | BOOL 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 | |||
1383 | BOOL 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 | ||
1445 | LLXMLNodePtr 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 | |||
1454 | BOOL 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 | } | ||