aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llui/llscrolllistctrl.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/llscrolllistctrl.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 'linden/indra/llui/llscrolllistctrl.cpp')
-rw-r--r--linden/indra/llui/llscrolllistctrl.cpp2741
1 files changed, 2741 insertions, 0 deletions
diff --git a/linden/indra/llui/llscrolllistctrl.cpp b/linden/indra/llui/llscrolllistctrl.cpp
new file mode 100644
index 0000000..df4ca64
--- /dev/null
+++ b/linden/indra/llui/llscrolllistctrl.cpp
@@ -0,0 +1,2741 @@
1/**
2 * @file llscrolllistctrl.cpp
3 * @brief LLScrollListCtrl 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 <algorithm>
29
30#include "linden_common.h"
31#include "llstl.h"
32#include "llboost.h"
33
34#include "llscrolllistctrl.h"
35
36#include "indra_constants.h"
37
38#include "llcheckboxctrl.h"
39#include "llclipboard.h"
40#include "llfocusmgr.h"
41#include "llgl.h"
42#include "llglheaders.h"
43#include "llresmgr.h"
44#include "llscrollbar.h"
45#include "llstring.h"
46#include "llui.h"
47#include "lluictrlfactory.h"
48#include "llwindow.h"
49#include "llcontrol.h"
50#include "llkeyboard.h"
51
52const S32 LIST_BORDER_PAD = 2; // white space inside the border and to the left of the scrollbar
53
54// local structures & classes.
55struct SortScrollListItem
56{
57 SortScrollListItem(const S32 sort_col, BOOL sort_ascending)
58 {
59 mSortCol = sort_col;
60 mSortAscending = sort_ascending;
61 }
62
63 bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2)
64 {
65 const LLScrollListCell *cell1;
66 const LLScrollListCell *cell2;
67
68 cell1 = i1->getColumn(mSortCol);
69 cell2 = i2->getColumn(mSortCol);
70
71 S32 order = 1;
72 if (!mSortAscending)
73 {
74 order = -1;
75 }
76
77 BOOL retval = FALSE;
78
79 if (cell1 && cell2)
80 {
81 retval = ((order * LLString::compareDict(cell1->getText(), cell2->getText())) < 0);
82 }
83
84 return (retval ? TRUE : FALSE);
85 }
86
87protected:
88 S32 mSortCol;
89 S32 mSortAscending;
90};
91
92
93
94//
95// LLScrollListIcon
96//
97LLScrollListIcon::LLScrollListIcon(LLImageGL* icon, S32 width, LLUUID image_id) :
98mIcon(icon), mImageUUID(image_id.getString())
99{
100 if (width)
101 {
102 mWidth = width;
103 }
104 else
105 {
106 mWidth = icon->getWidth();
107 }
108}
109
110LLScrollListIcon::~LLScrollListIcon()
111{
112}
113
114//
115// LLScrollListCheck
116//
117LLScrollListCheck::LLScrollListCheck(LLCheckBoxCtrl* check_box, S32 width)
118{
119 mCheckBox = check_box;
120 LLRect rect(mCheckBox->getRect());
121 if (width)
122 {
123
124 rect.mRight = rect.mLeft + width;
125 mCheckBox->setRect(rect);
126 mWidth = width;
127 }
128 else
129 {
130 mWidth = rect.getWidth(); //check_box->getWidth();
131 }
132}
133
134LLScrollListCheck::~LLScrollListCheck()
135{
136 delete mCheckBox;
137}
138
139void LLScrollListCheck::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const
140{
141 mCheckBox->draw();
142
143}
144
145BOOL LLScrollListCheck::handleClick()
146{
147 if ( mCheckBox->getEnabled() )
148 {
149 LLCheckBoxCtrl::onButtonPress(mCheckBox);
150 }
151 return TRUE;
152}
153
154//
155// LLScrollListText
156//
157U32 LLScrollListText::sCount = 0;
158
159LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, S32 width, U8 font_style, LLColor4& color, BOOL use_color, BOOL visible)
160: mText( text ),
161 mFont( font ),
162 mFontStyle( font_style ),
163 mWidth( width ),
164 mVisible( visible ),
165 mHighlightChars( 0 )
166{
167 if (use_color)
168 {
169 mColor = new LLColor4();
170 mColor->setVec(color);
171 }
172 else
173 {
174 mColor = NULL;
175 }
176
177 sCount++;
178
179 // initialize rounded rect image
180 if (!mRoundedRectImage)
181 {
182 mRoundedRectImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("rounded_square.tga")));
183 }
184
185 // Yes, that's four dots, because we want it to have a little
186 // padding, in proportion to the font size.
187 mEllipsisWidth = (S32)mFont->getWidth("....");
188}
189
190LLScrollListText::~LLScrollListText()
191{
192 sCount--;
193 delete mColor;
194}
195
196void LLScrollListText::setText(const LLString& text)
197{
198 mText = text;
199}
200
201void LLScrollListText::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const
202{
203 // If the user has specified a small minimum width, use that.
204 if (mWidth > 0 && mWidth < width)
205 {
206 width = mWidth;
207 }
208
209 const LLColor4* display_color;
210 if (mColor)
211 {
212 display_color = mColor;
213 }
214 else
215 {
216 display_color = &color;
217 }
218
219 if (mHighlightChars > 0)
220 {
221 mRoundedRectImage->bind();
222 glColor4fv(highlight_color.mV);
223 gl_segmented_rect_2d_tex(-2,
224 llround(mFont->getLineHeight()) + 1,
225 mFont->getWidth(mText.getString(), 0, mHighlightChars) + 1,
226 1,
227 mRoundedRectImage->getWidth(),
228 mRoundedRectImage->getHeight(),
229 16);
230 }
231
232 // Try to draw the entire string
233 F32 right_x;
234 U32 string_chars = mText.length();
235 U32 drawn_chars = mFont->render(mText.getWString(), 0, 0, 2,
236 *display_color,
237 LLFontGL::LEFT,
238 LLFontGL::BOTTOM,
239 mFontStyle,
240 string_chars,
241 width - mEllipsisWidth,
242 &right_x, FALSE);
243
244 // If we didn't get the whole string, abbreviate
245 if (drawn_chars < string_chars && drawn_chars)
246 {
247 mFont->renderUTF8("...", 0, right_x, 0.f, color, LLFontGL::LEFT, LLFontGL::BOTTOM, mFontStyle,
248 S32_MAX, S32_MAX, NULL, FALSE);
249 }
250}
251
252
253LLScrollListItem::~LLScrollListItem()
254{
255 std::for_each(mColumns.begin(), mColumns.end(), DeletePointer());
256}
257
258BOOL LLScrollListItem::handleMouseDown(S32 x, S32 y, MASK mask)
259{
260 BOOL handled = FALSE;
261
262 S32 left = 0;
263 S32 right = 0;
264 S32 width = 0;
265
266 std::vector<LLScrollListCell *>::iterator iter = mColumns.begin();
267 std::vector<LLScrollListCell *>::iterator end = mColumns.end();
268 for ( ; iter != end; ++iter)
269 {
270 width = (*iter)->getWidth();
271 right += width;
272 if (left <= x && x < right )
273 {
274 handled = (*iter)->handleClick();
275 break;
276 }
277
278 left += width;
279 }
280 return handled;
281}
282
283void LLScrollListItem::setNumColumns(S32 columns)
284{
285 S32 prev_columns = mColumns.size();
286 if (columns < prev_columns)
287 {
288 std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer());
289 }
290
291 mColumns.resize(columns);
292
293 for (S32 col = prev_columns; col < columns; ++col)
294 {
295 mColumns[col] = NULL;
296 }
297}
298
299void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell )
300{
301 if (column < (S32)mColumns.size())
302 {
303 delete mColumns[column];
304 mColumns[column] = cell;
305 }
306 else
307 {
308 llerrs << "LLScrollListItem::setColumn: bad column: " << column << llendl;
309 }
310}
311
312LLString LLScrollListItem::getContentsCSV()
313{
314 LLString ret;
315
316 S32 count = getNumColumns();
317 for (S32 i=0; i<count; ++i)
318 {
319 ret += getColumn(i)->getText();
320 if (i < count-1)
321 {
322 ret += ", ";
323 }
324 }
325
326 return ret;
327}
328
329void LLScrollListItem::setEnabled(BOOL b)
330{
331 if (b != mEnabled)
332 {
333 std::vector<LLScrollListCell *>::iterator iter = mColumns.begin();
334 std::vector<LLScrollListCell *>::iterator end = mColumns.end();
335 for ( ; iter != end; ++iter)
336 {
337 (*iter)->setEnabled(b);
338 }
339 mEnabled = b;
340 }
341}
342
343//---------------------------------------------------------------------------
344// LLScrollListCtrl
345//---------------------------------------------------------------------------
346
347LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect,
348 void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
349 void* callback_user_data,
350 BOOL allow_multiple_selection,
351 BOOL show_border
352 )
353 : LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data),
354 mLineHeight(0),
355 mScrollLines(0),
356 mPageLines(0),
357 mHeadingHeight(20),
358 mMaxSelectable(0),
359 mHeadingFont(NULL),
360 mAllowMultipleSelection( allow_multiple_selection ),
361 mAllowKeyboardMovement(TRUE),
362 mCommitOnKeyboardMovement(TRUE),
363 mCommitOnSelectionChange(FALSE),
364 mSelectionChanged(FALSE),
365 mCanSelect(TRUE),
366 mDisplayColumnButtons(FALSE),
367 mCollapseEmptyColumns(FALSE),
368 mIsPopup(FALSE),
369 mMaxItemCount(INT_MAX),
370 //mItemCount(0),
371 mBackgroundVisible( TRUE ),
372 mDrawStripes(TRUE),
373 mBgWriteableColor( LLUI::sColorsGroup->getColor( "ScrollBgWriteableColor" ) ),
374 mBgReadOnlyColor( LLUI::sColorsGroup->getColor( "ScrollBgReadOnlyColor" ) ),
375 mBgSelectedColor( LLUI::sColorsGroup->getColor("ScrollSelectedBGColor") ),
376 mBgStripeColor( LLUI::sColorsGroup->getColor("ScrollBGStripeColor") ),
377 mFgSelectedColor( LLUI::sColorsGroup->getColor("ScrollSelectedFGColor") ),
378 mFgUnselectedColor( LLUI::sColorsGroup->getColor("ScrollUnselectedColor") ),
379 mFgDisabledColor( LLUI::sColorsGroup->getColor("ScrollDisabledColor") ),
380 mHighlightedColor( LLUI::sColorsGroup->getColor("ScrollHighlightedColor") ),
381 mHighlightedItem(-1),
382 mBorderThickness( 2 ),
383 mOnDoubleClickCallback( NULL ),
384 mOnMaximumSelectCallback( NULL ),
385 mOnSortChangedCallback( NULL ),
386 mDrewSelected(FALSE),
387 mBorder(NULL),
388 mSearchColumn(0),
389 mDefaultColumn("SIMPLE"),
390
391 mNumDynamicWidthColumns(0),
392 mTotalStaticColumnWidth(0),
393 mSortColumn(0),
394 mSortAscending(TRUE)
395{
396 mItemListRect.setOriginAndSize(
397 mBorderThickness + LIST_BORDER_PAD,
398 mBorderThickness + LIST_BORDER_PAD,
399 mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE,
400 mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) );
401
402 updateLineHeight();
403
404 mPageLines = mLineHeight? (mItemListRect.getHeight()) / mLineHeight : 0;
405
406 // Init the scrollbar
407 LLRect scroll_rect;
408 scroll_rect.setOriginAndSize(
409 mRect.getWidth() - mBorderThickness - SCROLLBAR_SIZE,
410 mItemListRect.mBottom,
411 SCROLLBAR_SIZE,
412 mItemListRect.getHeight());
413 mScrollbar = new LLScrollbar( "Scrollbar", scroll_rect,
414 LLScrollbar::VERTICAL,
415 getItemCount(),
416 mScrollLines,
417 mPageLines,
418 &LLScrollListCtrl::onScrollChange, this );
419 mScrollbar->setFollowsRight();
420 mScrollbar->setFollowsTop();
421 mScrollbar->setFollowsBottom();
422 mScrollbar->setEnabled( TRUE );
423 mScrollbar->setVisible( TRUE );
424 addChild(mScrollbar);
425
426 // Border
427 if (show_border)
428 {
429 LLRect border_rect( 0, mRect.getHeight(), mRect.getWidth(), 0 );
430 mBorder = new LLViewBorder( "dlg border", border_rect, LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, 1 );
431 addChild(mBorder);
432 }
433
434 mColumnPadding = 5;
435
436 mLastSelected = NULL;
437}
438
439LLScrollListCtrl::~LLScrollListCtrl()
440{
441 std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
442
443 if( gEditMenuHandler == this )
444 {
445 gEditMenuHandler = NULL;
446 }
447}
448
449
450BOOL LLScrollListCtrl::setMaxItemCount(S32 max_count)
451{
452 if (max_count >= getItemCount())
453 {
454 mMaxItemCount = max_count;
455 }
456 return (max_count == mMaxItemCount);
457}
458
459S32 LLScrollListCtrl::isEmpty() const
460{
461 return mItemList.empty();
462}
463
464S32 LLScrollListCtrl::getItemCount() const
465{
466 return mItemList.size();
467}
468
469// virtual LLScrolListInterface function (was deleteAllItems)
470void LLScrollListCtrl::clearRows()
471{
472 std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
473 mItemList.clear();
474 //mItemCount = 0;
475
476 // Scroll the bar back up to the top.
477 mScrollbar->setDocParams(0, 0);
478
479 mScrollLines = 0;
480 mLastSelected = NULL;
481}
482
483
484LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
485{
486 item_list::const_iterator iter;
487 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
488 {
489 LLScrollListItem* item = *iter;
490 if (item->getSelected())
491 {
492 return item;
493 }
494 }
495 return NULL;
496}
497
498std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
499{
500 std::vector<LLScrollListItem*> ret;
501 item_list::const_iterator iter;
502 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
503 {
504 LLScrollListItem* item = *iter;
505 if (item->getSelected())
506 {
507 ret.push_back(item);
508 }
509 }
510 return ret;
511}
512
513S32 LLScrollListCtrl::getFirstSelectedIndex()
514{
515 S32 CurSelectedIndex = 0;
516 item_list::iterator iter;
517 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
518 {
519 LLScrollListItem* item = *iter;
520 if (item->getSelected())
521 {
522 return CurSelectedIndex;
523 }
524 CurSelectedIndex++;
525 }
526
527 return -1;
528}
529
530
531LLScrollListItem* LLScrollListCtrl::getFirstData() const
532{
533 if (mItemList.size() == 0)
534 {
535 return NULL;
536 }
537 return mItemList[0];
538}
539
540std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const
541{
542 std::vector<LLScrollListItem*> ret;
543 item_list::const_iterator iter;
544 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
545 {
546 LLScrollListItem* item = *iter;
547 ret.push_back(item);
548 }
549 return ret;
550}
551
552
553void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent )
554{
555 LLUICtrl::reshape( width, height, called_from_parent );
556
557 S32 heading_size = (mDisplayColumnButtons ? mHeadingHeight : 0);
558
559 mItemListRect.setOriginAndSize(
560 mBorderThickness + LIST_BORDER_PAD,
561 mBorderThickness + LIST_BORDER_PAD,
562 mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE,
563 mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) - heading_size );
564
565 mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0;
566 mScrollbar->setVisible(mPageLines < getItemCount());
567 mScrollbar->setPageSize( mPageLines );
568
569 updateColumns();
570 updateColumnButtons();
571}
572
573
574// Attempt to size the control to show all items.
575// Do not make larger than width or height.
576void LLScrollListCtrl::arrange(S32 max_width, S32 max_height)
577{
578 S32 height = mLineHeight * (getItemCount() + 1);
579 height = llmin( height, max_height );
580
581 S32 width = mRect.getWidth();
582
583 reshape( width, height );
584}
585
586
587LLRect LLScrollListCtrl::getRequiredRect()
588{
589 S32 height = mLineHeight * (getItemCount() + 1);
590 S32 width = mRect.getWidth();
591
592 return LLRect(0, height, width, 0);
593}
594
595
596BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos )
597{
598 BOOL not_too_big = getItemCount() < mMaxItemCount;
599 if (not_too_big)
600 {
601 switch( pos )
602 {
603 case ADD_TOP:
604 mItemList.push_front(item);
605 break;
606
607 case ADD_SORTED:
608 mSortColumn = 0;
609 mSortAscending = TRUE;
610 mItemList.push_back(item);
611 std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending));
612 break;
613
614 case ADD_BOTTOM:
615 mItemList.push_back(item);
616 break;
617
618 default:
619 llassert(0);
620 mItemList.push_back(item);
621 break;
622 }
623
624 updateLineHeight();
625 mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0;
626 mScrollbar->setVisible(mPageLines < getItemCount());
627 mScrollbar->setPageSize( mPageLines );
628
629 mScrollbar->setDocSize( getItemCount() );
630 }
631 return not_too_big;
632}
633
634
635// Line height is the max height of all the cells in all the items.
636void LLScrollListCtrl::updateLineHeight()
637{
638 const S32 ROW_PAD = 2;
639
640 mLineHeight = 0;
641 item_list::iterator iter;
642 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
643 {
644 LLScrollListItem *itemp = *iter;
645 S32 num_cols = itemp->getNumColumns();
646 S32 i = 0;
647 for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
648 {
649 mLineHeight = llmax( mLineHeight, cell->getHeight() + ROW_PAD );
650 }
651 }
652}
653
654void LLScrollListCtrl::updateColumns()
655{
656 mColumnsIndexed.resize(mColumns.size());
657
658 std::map<LLString, LLScrollListColumn>::iterator column_itor;
659 for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)
660 {
661 LLScrollListColumn *column = &column_itor->second;
662 if (column->mRelWidth >= 0)
663 {
664 column->mWidth = (S32)llround(column->mRelWidth*mItemListRect.getWidth());
665 }
666 else if (column->mDynamicWidth)
667 {
668 column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns;
669
670 }
671 mColumnsIndexed[column_itor->second.mIndex] = column;
672 }
673}
674
675void LLScrollListCtrl::updateColumnButtons()
676{
677 std::map<LLString, LLScrollListColumn>::iterator column_itor;
678 for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)
679 {
680 LLScrollListColumn* column = &column_itor->second;
681 LLButton *button = column->mButton;
682
683 if (button)
684 {
685 mColumnsIndexed[column->mIndex] = column;
686
687 S32 top = mItemListRect.mTop;
688 S32 left = mItemListRect.mLeft;
689 {
690 std::map<LLString, LLScrollListColumn>::iterator itor;
691 for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
692 {
693 if (itor->second.mIndex < column->mIndex &&
694 itor->second.mWidth > 0)
695 {
696 left += itor->second.mWidth + mColumnPadding;
697 }
698 }
699 }
700 S32 right = left+column->mWidth;
701 if (column->mIndex != (S32)mColumns.size()-1)
702 {
703 right += mColumnPadding;
704 }
705 LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top);
706 button->setRect(temp_rect);
707 button->setFont(mHeadingFont);
708 button->setVisible(mDisplayColumnButtons);
709 }
710 }
711}
712
713void LLScrollListCtrl::setDisplayHeading(BOOL display)
714{
715 mDisplayColumnButtons = display;
716
717 updateColumns();
718
719 setHeadingHeight(mHeadingHeight);
720}
721
722void LLScrollListCtrl::setHeadingHeight(S32 heading_height)
723{
724 mHeadingHeight = heading_height;
725
726 reshape(mRect.getWidth(), mRect.getHeight());
727
728 // Resize
729 mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight());
730
731 updateColumnButtons();
732}
733
734void LLScrollListCtrl::setHeadingFont(const LLFontGL* heading_font)
735{
736 mHeadingFont = heading_font;
737 updateColumnButtons();
738}
739
740void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse)
741{
742 mCollapseEmptyColumns = collapse;
743}
744
745BOOL LLScrollListCtrl::selectFirstItem()
746{
747 BOOL success = FALSE;
748
749 // our $%&@#$()^%#$()*^ iterators don't let us check against the first item inside out iteration
750 BOOL first_item = TRUE;
751
752 item_list::iterator iter;
753 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
754 {
755 LLScrollListItem *itemp = *iter;
756 if( first_item && itemp->getEnabled() )
757 {
758 if (!itemp->getSelected())
759 {
760 selectItem(itemp);
761 }
762 success = TRUE;
763 }
764 else
765 {
766 deselectItem(itemp);
767 }
768 first_item = FALSE;
769 }
770 if (mCommitOnSelectionChange)
771 {
772 commitIfChanged();
773 }
774 return success;
775}
776
777
778BOOL LLScrollListCtrl::selectNthItem( S32 target_index )
779{
780 // Deselects all other items
781 BOOL success = FALSE;
782 S32 index = 0;
783
784 target_index = llclamp(target_index, 0, (S32)mItemList.size() - 1);
785
786 item_list::iterator iter;
787 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
788 {
789 LLScrollListItem *itemp = *iter;
790 if( target_index == index )
791 {
792 if( itemp->getEnabled() )
793 {
794 selectItem(itemp);
795 success = TRUE;
796 }
797 }
798 else
799 {
800 deselectItem(itemp);
801 }
802 index++;
803 }
804
805 if (mCommitOnSelectionChange)
806 {
807 commitIfChanged();
808 }
809
810 mSearchString.clear();
811
812 return success;
813}
814
815
816void LLScrollListCtrl::swapWithNext(S32 index)
817{
818 if (index >= ((S32)mItemList.size() - 1))
819 {
820 // At end of list, doesn't do anything
821 return;
822 }
823 LLScrollListItem *cur_itemp = mItemList[index];
824 mItemList[index] = mItemList[index + 1];
825 mItemList[index + 1] = cur_itemp;
826}
827
828
829void LLScrollListCtrl::swapWithPrevious(S32 index)
830{
831 if (index <= 0)
832 {
833 // At beginning of list, don't do anything
834 }
835
836 LLScrollListItem *cur_itemp = mItemList[index];
837 mItemList[index] = mItemList[index - 1];
838 mItemList[index - 1] = cur_itemp;
839}
840
841
842void LLScrollListCtrl::deleteSingleItem(S32 target_index)
843{
844 if (target_index >= (S32)mItemList.size())
845 {
846 return;
847 }
848
849 LLScrollListItem *itemp;
850 itemp = mItemList[target_index];
851 if (itemp == mLastSelected)
852 {
853 mLastSelected = NULL;
854 }
855 delete itemp;
856 mItemList.erase(mItemList.begin() + target_index);
857}
858
859void LLScrollListCtrl::deleteSelectedItems()
860{
861 item_list::iterator iter;
862 for (iter = mItemList.begin(); iter < mItemList.end(); )
863 {
864 LLScrollListItem* itemp = *iter;
865 if (itemp->getSelected())
866 {
867 delete itemp;
868 iter = mItemList.erase(iter);
869 }
870 else
871 {
872 iter++;
873 }
874 }
875 mLastSelected = NULL;
876}
877
878void LLScrollListCtrl::highlightNthItem(S32 target_index)
879{
880 if (mHighlightedItem != target_index)
881 {
882 mHighlightedItem = target_index;
883 }
884}
885
886S32 LLScrollListCtrl::selectMultiple( LLDynamicArray<LLUUID> ids )
887{
888 item_list::iterator iter;
889 S32 count = 0;
890 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
891 {
892 LLScrollListItem* item = *iter;
893 LLDynamicArray<LLUUID>::iterator iditr;
894 for(iditr = ids.begin(); iditr != ids.end(); ++iditr)
895 {
896 if (item->getEnabled() && (item->getUUID() == (*iditr)))
897 {
898 selectItem(item,FALSE);
899 ++count;
900 break;
901 }
902 }
903 if(ids.end() != iditr) ids.erase(iditr);
904 }
905
906 if (mCommitOnSelectionChange)
907 {
908 commitIfChanged();
909 }
910 return count;
911}
912
913S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item )
914{
915 S32 index = 0;
916 item_list::iterator iter;
917 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
918 {
919 LLScrollListItem *itemp = *iter;
920 if (target_item == itemp)
921 {
922 return index;
923 }
924 index++;
925 }
926 return -1;
927}
928
929S32 LLScrollListCtrl::getItemIndex( LLUUID& target_id )
930{
931 S32 index = 0;
932 item_list::iterator iter;
933 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
934 {
935 LLScrollListItem *itemp = *iter;
936 if (target_id == itemp->getUUID())
937 {
938 return index;
939 }
940 index++;
941 }
942 return -1;
943}
944
945void LLScrollListCtrl::selectPrevItem( BOOL extend_selection)
946{
947 LLScrollListItem* prev_item = NULL;
948
949 if (!getFirstSelected())
950 {
951 selectFirstItem();
952 }
953 else
954 {
955 item_list::iterator iter;
956 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
957 {
958 LLScrollListItem* cur_item = *iter;
959
960 if (cur_item->getSelected())
961 {
962 if (prev_item)
963 {
964 selectItem(prev_item, !extend_selection);
965 }
966 else
967 {
968 reportInvalidInput();
969 }
970 break;
971 }
972
973 prev_item = cur_item;
974 }
975 }
976
977 if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement))
978 {
979 commitIfChanged();
980 }
981
982 mSearchString.clear();
983}
984
985
986void LLScrollListCtrl::selectNextItem( BOOL extend_selection)
987{
988 if (!getFirstSelected())
989 {
990 selectFirstItem();
991 }
992 else
993 {
994 item_list::iterator iter;
995 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
996 {
997 LLScrollListItem* item = *iter;
998 if (item->getSelected())
999 {
1000 if (++iter != mItemList.end())
1001 {
1002 LLScrollListItem *next_item = *iter;
1003 if (next_item)
1004 {
1005 selectItem(next_item, !extend_selection);
1006 }
1007 else
1008 {
1009 reportInvalidInput();
1010 }
1011 }
1012 break;
1013 }
1014 }
1015 }
1016
1017 if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement))
1018 {
1019 onCommit();
1020 }
1021
1022 mSearchString.clear();
1023}
1024
1025
1026
1027void LLScrollListCtrl::deselectAllItems(BOOL no_commit_on_change)
1028{
1029 item_list::iterator iter;
1030 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
1031 {
1032 LLScrollListItem* item = *iter;
1033 deselectItem(item);
1034 }
1035
1036 if (mCommitOnSelectionChange && !no_commit_on_change)
1037 {
1038 commitIfChanged();
1039 }
1040}
1041
1042///////////////////////////////////////////////////////////////////////////////////////////////////
1043// "Simple" interface: use this when you're creating a list that contains only unique strings, only
1044// one of which can be selected at a time.
1045
1046LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, EAddPosition pos, BOOL enabled)
1047{
1048 LLScrollListItem* item = NULL;
1049 if (getItemCount() < mMaxItemCount)
1050 {
1051 // simple items have their LLSD data set to their label
1052 item = new LLScrollListItem( LLSD(item_text) );
1053 item->setEnabled(enabled);
1054 item->addColumn( item_text, gResMgr->getRes( LLFONT_SANSSERIF_SMALL ) );
1055 addItem( item, pos );
1056 }
1057 return item;
1058}
1059
1060
1061// Selects first enabled item of the given name.
1062// Returns false if item not found.
1063BOOL LLScrollListCtrl::selectSimpleItem(const LLString& label, BOOL case_sensitive)
1064{
1065 //RN: assume no empty items
1066 if (label.empty())
1067 {
1068 return FALSE;
1069 }
1070
1071 LLString target_text = label;
1072 if (!case_sensitive)
1073 {
1074 LLString::toLower(target_text);
1075 }
1076
1077 BOOL found = FALSE;
1078
1079 item_list::iterator iter;
1080 S32 index = 0;
1081 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
1082 {
1083 LLScrollListItem* item = *iter;
1084 // Only select enabled items with matching names
1085 LLString item_text = item->getColumn(0)->getText();
1086 if (!case_sensitive)
1087 {
1088 LLString::toLower(item_text);
1089 }
1090 BOOL select = !found && item->getEnabled() && item_text == target_text;
1091 if (select)
1092 {
1093 selectItem(item);
1094 }
1095 found = found || select;
1096 index++;
1097 }
1098
1099 if (mCommitOnSelectionChange)
1100 {
1101 commitIfChanged();
1102 }
1103
1104 return found;
1105}
1106
1107
1108BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLString& target, BOOL case_sensitive)
1109{
1110 return selectSimpleItemByPrefix(utf8str_to_wstring(target), case_sensitive);
1111}
1112
1113// Selects first enabled item that has a name where the name's first part matched the target string.
1114// Returns false if item not found.
1115BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL case_sensitive)
1116{
1117 BOOL found = FALSE;
1118
1119 LLWString target_trimmed( target );
1120 S32 target_len = target_trimmed.size();
1121
1122 if( 0 == target_len )
1123 {
1124 // Is "" a valid choice?
1125 item_list::iterator iter;
1126 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
1127 {
1128 LLScrollListItem* item = *iter;
1129 // Only select enabled items with matching names
1130 LLScrollListCell* cellp = item->getColumn(mSearchColumn);
1131 BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getText()[0]) : FALSE;
1132 if (select)
1133 {
1134 selectItem(item);
1135 found = TRUE;
1136 break;
1137 }
1138 }
1139 }
1140 else
1141 {
1142 if (!case_sensitive)
1143 {
1144 // do comparisons in lower case
1145 LLWString::toLower(target_trimmed);
1146 }
1147
1148 for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
1149 {
1150 LLScrollListItem* item = *iter;
1151
1152 // Only select enabled items with matching names
1153 LLScrollListCell* cellp = item->getColumn(mSearchColumn);
1154 if (!cellp)
1155 {
1156 continue;
1157 }
1158 LLWString item_label = utf8str_to_wstring(cellp->getText());
1159 if (!case_sensitive)
1160 {
1161 LLWString::toLower(item_label);
1162 }
1163
1164 BOOL select = item->getEnabled() && !item_label.compare(0, target_len, target_trimmed);
1165
1166 if (select)
1167 {
1168 selectItem(item);
1169 found = TRUE;
1170 break;
1171 }
1172 }
1173 }
1174
1175 if (mCommitOnSelectionChange)
1176 {
1177 commitIfChanged();
1178 }
1179
1180 return found;
1181}
1182
1183const LLString& LLScrollListCtrl::getSimpleSelectedItem(S32 column) const
1184{
1185 LLScrollListItem* item;
1186
1187 item = getFirstSelected();
1188 if (item)
1189 {
1190 return item->getColumn(column)->getText();
1191 }
1192
1193 return LLString::null;
1194}
1195
1196///////////////////////////////////////////////////////////////////////////////////////////////////
1197// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
1198// has an associated, unique UUID, and only one of which can be selected at a time.
1199
1200LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const LLString& item_text, const LLUUID& id, EAddPosition pos, BOOL enabled, S32 column_width)
1201{
1202 LLScrollListItem* item = NULL;
1203 if (getItemCount() < mMaxItemCount)
1204 {
1205 item = new LLScrollListItem( enabled, NULL, id );
1206 item->addColumn(item_text, gResMgr->getRes(LLFONT_SANSSERIF_SMALL), column_width);
1207 addItem( item, pos );
1208 }
1209 return item;
1210}
1211
1212LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, LLSD sd, EAddPosition pos, BOOL enabled, S32 column_width)
1213{
1214 LLScrollListItem* item = NULL;
1215 if (getItemCount() < mMaxItemCount)
1216 {
1217 item = new LLScrollListItem( sd );
1218 item->setEnabled(enabled);
1219 item->addColumn(item_text, gResMgr->getRes(LLFONT_SANSSERIF_SMALL), column_width);
1220 addItem( item, pos );
1221 }
1222 return item;
1223}
1224
1225
1226// Select the line or lines that match this UUID
1227BOOL LLScrollListCtrl::selectByID( const LLUUID& id )
1228{
1229 return selectByValue( LLSD(id) );
1230}
1231
1232BOOL LLScrollListCtrl::setSelectedByValue(LLSD value, BOOL selected)
1233{
1234 BOOL found = FALSE;
1235
1236 if (selected && !mAllowMultipleSelection) deselectAllItems(TRUE);
1237
1238 item_list::iterator iter;
1239 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
1240 {
1241 LLScrollListItem* item = *iter;
1242 if (item->getEnabled() && (item->getValue().asString() == value.asString()))
1243 {
1244 if (selected)
1245 {
1246 selectItem(item);
1247 }
1248 else
1249 {
1250 deselectItem(item);
1251 }
1252 found = TRUE;
1253 break;
1254 }
1255 }
1256
1257 if (mCommitOnSelectionChange)
1258 {
1259 commitIfChanged();
1260 }
1261
1262 return found;
1263}
1264
1265BOOL LLScrollListCtrl::isSelected(LLSD value)
1266{
1267 item_list::iterator iter;
1268 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
1269 {
1270 LLScrollListItem* item = *iter;
1271 if (item->getValue().asString() == value.asString())
1272 {
1273 return item->getSelected();
1274 }
1275 }
1276 return FALSE;
1277}
1278
1279LLUUID LLScrollListCtrl::getStringUUIDSelectedItem()
1280{
1281 LLScrollListItem* item = getFirstSelected();
1282
1283 if (item)
1284 {
1285 return item->getUUID();
1286 }
1287
1288 return LLUUID::null;
1289}
1290
1291LLSD LLScrollListCtrl::getSimpleSelectedValue()
1292{
1293 LLScrollListItem* item = getFirstSelected();
1294
1295 if (item)
1296 {
1297 return item->getValue();
1298 }
1299 else
1300 {
1301 return LLSD();
1302 }
1303}
1304
1305void LLScrollListCtrl::drawItems()
1306{
1307 S32 x = mItemListRect.mLeft;
1308 S32 y = mItemListRect.mTop - mLineHeight;
1309
1310 S32 num_page_lines = mPageLines;
1311
1312 LLRect item_rect;
1313
1314 LLGLSUIDefault gls_ui;
1315
1316 {
1317
1318 S32 cur_x = x;
1319 S32 cur_y = y;
1320
1321 mDrewSelected = FALSE;
1322
1323 S32 line = 0;
1324 LLColor4 color;
1325 S32 max_columns = 0;
1326
1327 item_list::iterator iter;
1328 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
1329 {
1330 LLScrollListItem* item = *iter;
1331
1332 item_rect.setOriginAndSize(
1333 cur_x,
1334 cur_y,
1335 mScrollbar->getVisible() ? mItemListRect.getWidth() : mItemListRect.getWidth() + mScrollbar->getRect().getWidth(),
1336 mLineHeight );
1337
1338 lldebugs << mItemListRect.getWidth() << llendl;
1339
1340 if (item->getSelected())
1341 {
1342 mDrewSelected = TRUE;
1343 }
1344
1345 max_columns = llmax(max_columns, item->getNumColumns());
1346
1347 LLRect bg_rect = item_rect;
1348 // pad background rectangle to separate it from contents
1349 bg_rect.stretch(LIST_BORDER_PAD, 0);
1350
1351 if( mScrollLines <= line && line < mScrollLines + num_page_lines )
1352 {
1353 if( item->getSelected() && mCanSelect)
1354 {
1355 // Draw background of selected item
1356 LLGLSNoTexture no_texture;
1357 glColor4fv(mBgSelectedColor.mV);
1358 gl_rect_2d( bg_rect );
1359
1360 color = mFgSelectedColor;
1361 }
1362 else if (mHighlightedItem == line && mCanSelect)
1363 {
1364 LLGLSNoTexture no_texture;
1365 glColor4fv(mHighlightedColor.mV);
1366 gl_rect_2d( bg_rect );
1367 color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor);
1368 }
1369 else
1370 {
1371 color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor);
1372 if (mDrawStripes && (line%2 == 0) && (max_columns > 1))
1373 {
1374 LLGLSNoTexture no_texture;
1375 glColor4fv(mBgStripeColor.mV);
1376 gl_rect_2d( bg_rect );
1377 }
1378 }
1379
1380 S32 line_x = cur_x;
1381 {
1382 S32 num_cols = item->getNumColumns();
1383 S32 cur_col = 0;
1384 S32 dynamic_width = 0;
1385 S32 dynamic_remainder = 0;
1386 if(mNumDynamicWidthColumns > 0)
1387 {
1388 dynamic_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns;
1389 dynamic_remainder = (mItemListRect.getWidth() - mTotalStaticColumnWidth) % mNumDynamicWidthColumns;
1390 }
1391 for (LLScrollListCell* cell = item->getColumn(0); cur_col < num_cols; cell = item->getColumn(++cur_col))
1392 {
1393 S32 cell_width = cell->getWidth();
1394 if(mColumnsIndexed.size() > (U32)cur_col && mColumnsIndexed[cur_col] && mColumnsIndexed[cur_col]->mDynamicWidth)
1395 {
1396 cell_width = dynamic_width + (--dynamic_remainder ? 1 : 0);
1397 cell->setWidth(cell_width);
1398 }
1399 // Two ways a cell could be hidden
1400 if (cell_width < 0
1401 || !cell->getVisible()) continue;
1402 LLUI::pushMatrix();
1403 LLUI::translate((F32) cur_x, (F32) cur_y, 0.0f);
1404 S32 space_left = mItemListRect.mRight - cur_x;
1405 LLColor4 highlight_color = LLColor4::white;
1406 F32 type_ahead_timeout = LLUI::sConfigGroup->getF32("TypeAheadTimeout");
1407
1408 highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f);
1409 cell->drawToWidth( space_left, color, highlight_color );
1410 LLUI::popMatrix();
1411
1412 cur_x += cell_width + mColumnPadding;
1413
1414 }
1415 }
1416 cur_x = line_x;
1417 cur_y -= mLineHeight;
1418 }
1419 line++;
1420 }
1421 }
1422}
1423
1424
1425void LLScrollListCtrl::draw()
1426{
1427 if( getVisible() )
1428 {
1429 LLRect background(0, mRect.getHeight(), mRect.getWidth(), 0);
1430 // Draw background
1431 if (mBackgroundVisible)
1432 {
1433 LLGLSNoTexture no_texture;
1434 glColor4fv( getEnabled() ? mBgWriteableColor.mV : mBgReadOnlyColor.mV );
1435 gl_rect_2d(background);
1436 }
1437
1438 drawItems();
1439
1440 if (mBorder)
1441 {
1442 mBorder->setKeyboardFocusHighlight(gFocusMgr.getKeyboardFocus() == this);
1443 }
1444
1445 LLUICtrl::draw();
1446 }
1447}
1448
1449void LLScrollListCtrl::setEnabled(BOOL enabled)
1450{
1451 mCanSelect = enabled;
1452 setTabStop(enabled);
1453 mScrollbar->setTabStop(!enabled && mScrollbar->getPageSize() < mScrollbar->getDocSize());
1454}
1455
1456BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
1457{
1458 BOOL handled = FALSE;
1459 // Pretend the mouse is over the scrollbar
1460 handled = mScrollbar->handleScrollWheel( 0, 0, clicks );
1461 return handled;
1462}
1463
1464
1465BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
1466{
1467 BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
1468
1469 if( !handled && mCanSelect)
1470 {
1471 LLScrollListItem* hit_item = hitItem(x, y);
1472 if( hit_item )
1473 {
1474 if( mAllowMultipleSelection )
1475 {
1476 if (mask & MASK_SHIFT)
1477 {
1478 if (mLastSelected == NULL)
1479 {
1480 selectItem(hit_item);
1481 }
1482 else
1483 {
1484 // Select everthing between mLastSelected and hit_item
1485 bool selecting = false;
1486 item_list::iterator itor;
1487 // If we multiselect backwards, we'll stomp on mLastSelected,
1488 // meaning that we never stop selecting until hitting max or
1489 // the end of the list.
1490 LLScrollListItem* lastSelected = mLastSelected;
1491 for (itor = mItemList.begin(); itor != mItemList.end(); ++itor)
1492 {
1493 if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)
1494 {
1495 if(mOnMaximumSelectCallback)
1496 {
1497 mOnMaximumSelectCallback(mCallbackUserData);
1498 }
1499 break;
1500 }
1501 LLScrollListItem *item = *itor;
1502 if (item == hit_item || item == lastSelected)
1503 {
1504 selectItem(item, FALSE);
1505 selecting = !selecting;
1506 }
1507 if (selecting)
1508 {
1509 selectItem(item, FALSE);
1510 }
1511 }
1512 }
1513 }
1514 else if (mask & MASK_CONTROL)
1515 {
1516 if (hit_item->getSelected())
1517 {
1518 deselectItem(hit_item);
1519 }
1520 else
1521 {
1522 if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable))
1523 {
1524 selectItem(hit_item, FALSE);
1525 }
1526 else
1527 {
1528 if(mOnMaximumSelectCallback)
1529 {
1530 mOnMaximumSelectCallback(mCallbackUserData);
1531 }
1532 }
1533 }
1534 }
1535 else
1536 {
1537 deselectAllItems(TRUE);
1538 selectItem(hit_item);
1539 }
1540 }
1541 else
1542 {
1543 selectItem(hit_item);
1544 }
1545
1546 hit_item->handleMouseDown(x - mBorderThickness - LIST_BORDER_PAD,
1547 1, mask);
1548 // always commit on mousedown
1549 onCommit();
1550 mSelectionChanged = FALSE;
1551
1552 // clear search string on mouse operations
1553 mSearchString.clear();
1554 }
1555 else
1556 {
1557 mLastSelected = NULL;
1558 }
1559 }
1560
1561 gFocusMgr.setKeyboardFocus(this, NULL);
1562
1563 return TRUE;
1564}
1565
1566BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
1567{
1568 //BOOL handled = FALSE;
1569 if(getVisible())
1570 {
1571 // Offer the click to the children, even if we aren't enabled
1572 // so the scroll bars will work.
1573 if (NULL == LLView::childrenHandleDoubleClick(x, y, mask))
1574 {
1575 if( mCanSelect && mOnDoubleClickCallback )
1576 {
1577 mOnDoubleClickCallback( mCallbackUserData );
1578 }
1579 }
1580 }
1581 return TRUE;
1582}
1583
1584LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
1585{
1586 // Excludes disabled items.
1587 LLScrollListItem* hit_item = NULL;
1588
1589 LLRect item_rect;
1590 item_rect.setLeftTopAndSize(
1591 mItemListRect.mLeft,
1592 mItemListRect.mTop,
1593 mItemListRect.getWidth(),
1594 mLineHeight );
1595
1596 int num_page_lines = mPageLines;
1597
1598 S32 line = 0;
1599 item_list::iterator iter;
1600 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
1601 {
1602 LLScrollListItem* item = *iter;
1603 if( mScrollLines <= line && line < mScrollLines + num_page_lines )
1604 {
1605 if( item->getEnabled() && item_rect.pointInRect( x, y ) )
1606 {
1607 hit_item = item;
1608 break;
1609 }
1610
1611 item_rect.translate(0, -mLineHeight);
1612 }
1613 line++;
1614 }
1615
1616 return hit_item;
1617}
1618
1619
1620BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask)
1621{
1622 BOOL handled = FALSE;
1623
1624 if(getVisible())
1625 {
1626 if (mCanSelect)
1627 {
1628 LLScrollListItem* item = hitItem(x, y);
1629 if (item)
1630 {
1631 highlightNthItem(getItemIndex(item));
1632 }
1633 else
1634 {
1635 highlightNthItem(-1);
1636 }
1637 }
1638
1639 handled = LLView::handleHover( x, y, mask );
1640
1641 if( !handled )
1642 {
1643 // Opaque
1644 getWindow()->setCursor(UI_CURSOR_ARROW);
1645 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
1646 handled = TRUE;
1647 }
1648 }
1649 return handled;
1650}
1651
1652
1653BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent )
1654{
1655 BOOL handled = FALSE;
1656
1657 // not called from parent means we have keyboard focus or a child does
1658 if (mCanSelect && !called_from_parent)
1659 {
1660 // Ignore capslock
1661 mask = mask;
1662
1663 if (mask == MASK_NONE)
1664 {
1665 switch(key)
1666 {
1667 case KEY_UP:
1668 if (mAllowKeyboardMovement || hasFocus())
1669 {
1670 // commit implicit in call
1671 selectPrevItem(FALSE);
1672 scrollToShowSelected();
1673 handled = TRUE;
1674 }
1675 break;
1676 case KEY_DOWN:
1677 if (mAllowKeyboardMovement || hasFocus())
1678 {
1679 // commit implicit in call
1680 selectNextItem(FALSE);
1681 scrollToShowSelected();
1682 handled = TRUE;
1683 }
1684 break;
1685 case KEY_PAGE_UP:
1686 if (mAllowKeyboardMovement || hasFocus())
1687 {
1688 selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1));
1689 scrollToShowSelected();
1690 if (mCommitOnKeyboardMovement
1691 && !mCommitOnSelectionChange)
1692 {
1693 onCommit();
1694 }
1695 handled = TRUE;
1696 }
1697 break;
1698 case KEY_PAGE_DOWN:
1699 if (mAllowKeyboardMovement || hasFocus())
1700 {
1701 selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1));
1702 scrollToShowSelected();
1703 if (mCommitOnKeyboardMovement
1704 && !mCommitOnSelectionChange)
1705 {
1706 onCommit();
1707 }
1708 handled = TRUE;
1709 }
1710 break;
1711 case KEY_HOME:
1712 if (mAllowKeyboardMovement || hasFocus())
1713 {
1714 selectFirstItem();
1715 scrollToShowSelected();
1716 if (mCommitOnKeyboardMovement
1717 && !mCommitOnSelectionChange)
1718 {
1719 onCommit();
1720 }
1721 handled = TRUE;
1722 }
1723 break;
1724 case KEY_END:
1725 if (mAllowKeyboardMovement || hasFocus())
1726 {
1727 selectNthItem(getItemCount() - 1);
1728 scrollToShowSelected();
1729 if (mCommitOnKeyboardMovement
1730 && !mCommitOnSelectionChange)
1731 {
1732 onCommit();
1733 }
1734 handled = TRUE;
1735 }
1736 break;
1737 case KEY_RETURN:
1738 // JC - Special case: Only claim to have handled it
1739 // if we're the special non-commit-on-move
1740 // type. AND we are visible
1741 if (!mCommitOnKeyboardMovement && mask == MASK_NONE && getVisible())
1742 {
1743 onCommit();
1744 mSearchString.clear();
1745 handled = TRUE;
1746 }
1747 break;
1748 case KEY_BACKSPACE:
1749 mSearchTimer.reset();
1750 if (mSearchString.size())
1751 {
1752 mSearchString.erase(mSearchString.size() - 1, 1);
1753 }
1754 if (mSearchString.empty())
1755 {
1756 if (getFirstSelected())
1757 {
1758 LLScrollListCell* cellp = getFirstSelected()->getColumn(mSearchColumn);
1759 if (cellp)
1760 {
1761 cellp->highlightText(0);
1762 }
1763 }
1764 }
1765 else if (selectSimpleItemByPrefix(wstring_to_utf8str(mSearchString), FALSE))
1766 {
1767 // update search string only on successful match
1768 mSearchTimer.reset();
1769
1770 // highlight current search on matching item
1771 LLScrollListCell* cellp = getFirstSelected()->getColumn(mSearchColumn);
1772 if (cellp)
1773 {
1774 cellp->highlightText(mSearchString.size());
1775 }
1776
1777 if (mCommitOnKeyboardMovement
1778 && !mCommitOnSelectionChange)
1779 {
1780 onCommit();
1781 }
1782 }
1783 break;
1784 default:
1785 break;
1786 }
1787 }
1788 // TODO: multiple: shift-up, shift-down, shift-home, shift-end, select all
1789 }
1790
1791 return handled;
1792}
1793
1794BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
1795{
1796 if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
1797 {
1798 return FALSE;
1799 }
1800
1801 // perform incremental search based on keyboard input
1802 if (mSearchTimer.getElapsedTimeF32() > LLUI::sConfigGroup->getF32("TypeAheadTimeout"))
1803 {
1804 mSearchString.clear();
1805 }
1806
1807 // type ahead search is case insensitive
1808 uni_char = LLStringOps::toLower((llwchar)uni_char);
1809
1810 if (selectSimpleItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE))
1811 {
1812 // update search string only on successful match
1813 mSearchString += uni_char;
1814 mSearchTimer.reset();
1815
1816 // highlight current search on matching item
1817 LLScrollListCell* cellp = getFirstSelected()->getColumn(mSearchColumn);
1818 if (cellp)
1819 {
1820 cellp->highlightText(mSearchString.size());
1821 }
1822
1823 if (mCommitOnKeyboardMovement
1824 && !mCommitOnSelectionChange)
1825 {
1826 onCommit();
1827 }
1828 }
1829 // handle iterating over same starting character
1830 else if (isRepeatedChars(mSearchString + (llwchar)uni_char) && !mItemList.empty())
1831 {
1832 // start from last selected item, in case we previously had a successful match against
1833 // duplicated characters ('AA' matches 'Aaron')
1834 item_list::iterator start_iter = mItemList.begin();
1835 S32 first_selected = getFirstSelectedIndex();
1836
1837 // if we have a selection (> -1) then point iterator at the selected item
1838 if (first_selected > 0)
1839 {
1840 // point iterator to first selected item
1841 start_iter += first_selected;
1842 }
1843
1844 // start search at first item after current selection
1845 item_list::iterator iter = start_iter;
1846 ++iter;
1847 if (iter == mItemList.end())
1848 {
1849 iter = mItemList.begin();
1850 }
1851
1852 // loop around once, back to previous selection
1853 while(iter != start_iter)
1854 {
1855 LLScrollListItem* item = *iter;
1856
1857 LLScrollListCell* cellp = item->getColumn(mSearchColumn);
1858 if (cellp)
1859 {
1860 // Only select enabled items with matching first characters
1861 LLWString item_label = utf8str_to_wstring(cellp->getText());
1862 if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char)
1863 {
1864 selectItem(item);
1865 cellp->highlightText(1);
1866 mSearchTimer.reset();
1867
1868 if (mCommitOnKeyboardMovement
1869 && !mCommitOnSelectionChange)
1870 {
1871 onCommit();
1872 }
1873
1874 break;
1875 }
1876 }
1877
1878 ++iter;
1879 if (iter == mItemList.end())
1880 {
1881 iter = mItemList.begin();
1882 }
1883 }
1884 }
1885
1886 // make sure selected item is on screen
1887 scrollToShowSelected();
1888 return TRUE;
1889}
1890
1891
1892void LLScrollListCtrl::reportInvalidInput()
1893{
1894 make_ui_sound("UISndBadKeystroke");
1895}
1896
1897BOOL LLScrollListCtrl::isRepeatedChars(const LLWString& string) const
1898{
1899 if (string.empty())
1900 {
1901 return FALSE;
1902 }
1903
1904 llwchar first_char = string[0];
1905
1906 for (U32 i = 0; i < string.size(); i++)
1907 {
1908 if (string[i] != first_char)
1909 {
1910 return FALSE;
1911 }
1912 }
1913
1914 return TRUE;
1915}
1916
1917void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_item)
1918{
1919 if (!itemp) return;
1920
1921 if (!itemp->getSelected())
1922 {
1923 if (mLastSelected)
1924 {
1925 LLScrollListCell* cellp = mLastSelected->getColumn(mSearchColumn);
1926 if (cellp)
1927 {
1928 cellp->highlightText(0);
1929 }
1930 }
1931 if (select_single_item)
1932 {
1933 deselectAllItems(TRUE);
1934 }
1935 itemp->setSelected(TRUE);
1936 mLastSelected = itemp;
1937 mSelectionChanged = TRUE;
1938 }
1939}
1940
1941void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp)
1942{
1943 if (!itemp) return;
1944
1945 if (itemp->getSelected())
1946 {
1947 if (mLastSelected == itemp)
1948 {
1949 mLastSelected = NULL;
1950 }
1951
1952 itemp->setSelected(FALSE);
1953 LLScrollListCell* cellp = itemp->getColumn(mSearchColumn);
1954 if (cellp)
1955 {
1956 cellp->highlightText(0);
1957 }
1958 mSelectionChanged = TRUE;
1959 }
1960}
1961
1962void LLScrollListCtrl::commitIfChanged()
1963{
1964 if (mSelectionChanged)
1965 {
1966 mSelectionChanged = FALSE;
1967 onCommit();
1968 }
1969}
1970
1971// Called by scrollbar
1972//static
1973void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata )
1974{
1975 LLScrollListCtrl* self = (LLScrollListCtrl*) userdata;
1976 self->mScrollLines = new_pos;
1977}
1978
1979
1980// First column is column 0
1981void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending)
1982{
1983 mSortColumn = column;
1984 mSortAscending = ascending;
1985 std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending));
1986}
1987
1988void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending)
1989{
1990 if (name.empty())
1991 {
1992 sortByColumn(mSortColumn, mSortAscending);
1993 return;
1994 }
1995
1996 std::map<LLString, LLScrollListColumn>::iterator itor = mColumns.find(name);
1997 if (itor != mColumns.end())
1998 {
1999 sortByColumn((*itor).second.mIndex, ascending);
2000 }
2001}
2002
2003S32 LLScrollListCtrl::getScrollPos()
2004{
2005 return mScrollbar->getDocPos();
2006}
2007
2008
2009void LLScrollListCtrl::setScrollPos( S32 pos )
2010{
2011 mScrollbar->setDocPos( pos );
2012
2013 onScrollChange(mScrollbar->getDocPos(), mScrollbar, this);
2014}
2015
2016
2017void LLScrollListCtrl::scrollToShowSelected()
2018{
2019 S32 index = getFirstSelectedIndex();
2020 if (index < 0)
2021 {
2022 return;
2023 }
2024
2025 LLScrollListItem* item = mItemList[index];
2026 if (!item)
2027 {
2028 // I don't THINK this should ever happen.
2029 return;
2030 }
2031
2032 S32 lowest = mScrollLines;
2033 S32 highest = mScrollLines + mPageLines;
2034
2035 if (index < lowest)
2036 {
2037 // need to scroll to show item
2038 setScrollPos(index);
2039 }
2040 else if (highest <= index)
2041 {
2042 setScrollPos(index - mPageLines + 1);
2043 }
2044}
2045
2046// virtual
2047LLXMLNodePtr LLScrollListCtrl::getXML(bool save_children) const
2048{
2049 LLXMLNodePtr node = LLUICtrl::getXML();
2050
2051 // Attributes
2052
2053 node->createChild("multi_select", TRUE)->setBoolValue(mAllowMultipleSelection);
2054
2055 node->createChild("draw_border", TRUE)->setBoolValue((mBorder != NULL));
2056
2057 node->createChild("draw_heading", TRUE)->setBoolValue(mDisplayColumnButtons);
2058
2059 node->createChild("background_visible", TRUE)->setBoolValue(mBackgroundVisible);
2060
2061 node->createChild("draw_stripes", TRUE)->setBoolValue(mDrawStripes);
2062
2063 node->createChild("column_padding", TRUE)->setIntValue(mColumnPadding);
2064
2065 addColorXML(node, mBgWriteableColor, "bg_writeable_color", "ScrollBgWriteableColor");
2066 addColorXML(node, mBgReadOnlyColor, "bg_read_only_color", "ScrollBgReadOnlyColor");
2067 addColorXML(node, mBgSelectedColor, "bg_selected_color", "ScrollSelectedBGColor");
2068 addColorXML(node, mBgStripeColor, "bg_stripe_color", "ScrollBGStripeColor");
2069 addColorXML(node, mFgSelectedColor, "fg_selected_color", "ScrollSelectedFGColor");
2070 addColorXML(node, mFgUnselectedColor, "fg_unselected_color", "ScrollUnselectedColor");
2071 addColorXML(node, mFgDisabledColor, "fg_disable_color", "ScrollDisabledColor");
2072 addColorXML(node, mHighlightedColor, "highlighted_color", "ScrollHighlightedColor");
2073
2074 // Contents
2075
2076 std::map<LLString, LLScrollListColumn>::const_iterator itor;
2077 std::vector<const LLScrollListColumn*> sorted_list;
2078 sorted_list.resize(mColumns.size());
2079 for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
2080 {
2081 sorted_list[itor->second.mIndex] = &itor->second;
2082 }
2083
2084 std::vector<const LLScrollListColumn*>::iterator itor2;
2085 for (itor2 = sorted_list.begin(); itor2 != sorted_list.end(); ++itor2)
2086 {
2087 LLXMLNodePtr child_node = node->createChild("column", FALSE);
2088 const LLScrollListColumn *column = *itor2;
2089
2090 child_node->createChild("name", TRUE)->setStringValue(column->mName);
2091 child_node->createChild("label", TRUE)->setStringValue(column->mLabel);
2092 child_node->createChild("width", TRUE)->setIntValue(column->mWidth);
2093 }
2094
2095 return node;
2096}
2097
2098void LLScrollListCtrl::setScrollListParameters(LLXMLNodePtr node)
2099{
2100 // James: This is not a good way to do colors. We need a central "UI style"
2101 // manager that sets the colors for ALL scroll lists, buttons, etc.
2102
2103 LLColor4 color;
2104 if(node->hasAttribute("fg_unselected_color"))
2105 {
2106 LLUICtrlFactory::getAttributeColor(node,"fg_unselected_color", color);
2107 setFgUnselectedColor(color);
2108 }
2109 if(node->hasAttribute("fg_selected_color"))
2110 {
2111 LLUICtrlFactory::getAttributeColor(node,"fg_selected_color", color);
2112 setFgSelectedColor(color);
2113 }
2114 if(node->hasAttribute("bg_selected_color"))
2115 {
2116 LLUICtrlFactory::getAttributeColor(node,"bg_selected_color", color);
2117 setBgSelectedColor(color);
2118 }
2119 if(node->hasAttribute("fg_disable_color"))
2120 {
2121 LLUICtrlFactory::getAttributeColor(node,"fg_disable_color", color);
2122 setFgDisableColor(color);
2123 }
2124 if(node->hasAttribute("bg_writeable_color"))
2125 {
2126 LLUICtrlFactory::getAttributeColor(node,"bg_writeable_color", color);
2127 setBgWriteableColor(color);
2128 }
2129 if(node->hasAttribute("bg_read_only_color"))
2130 {
2131 LLUICtrlFactory::getAttributeColor(node,"bg_read_only_color", color);
2132 setReadOnlyBgColor(color);
2133 }
2134 if (LLUICtrlFactory::getAttributeColor(node,"bg_stripe_color", color))
2135 {
2136 setBgStripeColor(color);
2137 }
2138 if (LLUICtrlFactory::getAttributeColor(node,"highlighted_color", color))
2139 {
2140 setHighlightedColor(color);
2141 }
2142
2143 if(node->hasAttribute("background_visible"))
2144 {
2145 BOOL background_visible;
2146 node->getAttributeBOOL("background_visible", background_visible);
2147 setBackgroundVisible(background_visible);
2148 }
2149
2150 if(node->hasAttribute("draw_stripes"))
2151 {
2152 BOOL draw_stripes;
2153 node->getAttributeBOOL("draw_stripes", draw_stripes);
2154 setDrawStripes(draw_stripes);
2155 }
2156
2157 if(node->hasAttribute("column_padding"))
2158 {
2159 S32 column_padding;
2160 node->getAttributeS32("column_padding", column_padding);
2161 setColumnPadding(column_padding);
2162 }
2163}
2164
2165// static
2166LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
2167{
2168 LLString name("scroll_list");
2169 node->getAttributeString("name", name);
2170
2171 LLRect rect;
2172 createRect(node, rect, parent, LLRect());
2173
2174 BOOL multi_select = FALSE;
2175 node->getAttributeBOOL("multi_select", multi_select);
2176
2177 BOOL draw_border = TRUE;
2178 node->getAttributeBOOL("draw_border", draw_border);
2179
2180 BOOL draw_heading = FALSE;
2181 node->getAttributeBOOL("draw_heading", draw_heading);
2182
2183 BOOL collapse_empty_columns = FALSE;
2184 node->getAttributeBOOL("collapse_empty_columns", collapse_empty_columns);
2185
2186 S32 search_column = 0;
2187 node->getAttributeS32("search_column", search_column);
2188
2189 LLUICtrlCallback callback = NULL;
2190
2191 LLScrollListCtrl* scroll_list = new LLScrollListCtrl(
2192 name,
2193 rect,
2194 callback,
2195 NULL,
2196 multi_select,
2197 draw_border);
2198
2199 scroll_list->setDisplayHeading(draw_heading);
2200 if (node->hasAttribute("heading_height"))
2201 {
2202 S32 heading_height;
2203 node->getAttributeS32("heading_height", heading_height);
2204 scroll_list->setHeadingHeight(heading_height);
2205 }
2206 if (node->hasAttribute("heading_font"))
2207 {
2208 LLString heading_font("");
2209 node->getAttributeString("heading_font", heading_font);
2210 LLFontGL* gl_font = LLFontGL::fontFromName(heading_font.c_str());
2211 scroll_list->setHeadingFont(gl_font);
2212 }
2213 scroll_list->setCollapseEmptyColumns(collapse_empty_columns);
2214
2215 scroll_list->setScrollListParameters(node);
2216
2217 scroll_list->initFromXML(node, parent);
2218
2219 scroll_list->setSearchColumn(search_column);
2220
2221 LLSD columns;
2222 S32 index = 0;
2223 LLXMLNodePtr child;
2224 S32 total_static = 0, num_dynamic = 0;
2225 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
2226 {
2227 if (child->hasName("column"))
2228 {
2229 LLString labelname("");
2230 child->getAttributeString("label", labelname);
2231
2232 LLString columnname(labelname);
2233 child->getAttributeString("name", columnname);
2234
2235 LLString sortname(columnname);
2236 child->getAttributeString("sort", sortname);
2237
2238 LLString imagename;
2239 child->getAttributeString("image", imagename);
2240
2241 BOOL columndynamicwidth = FALSE;
2242 child->getAttributeBOOL("dynamicwidth", columndynamicwidth);
2243
2244 S32 columnwidth = -1;
2245 child->getAttributeS32("width", columnwidth);
2246
2247 if(!columndynamicwidth) total_static += columnwidth;
2248 else ++num_dynamic;
2249
2250 F32 columnrelwidth = 0.f;
2251 child->getAttributeF32("relwidth", columnrelwidth);
2252
2253
2254 columns[index]["name"] = columnname;
2255 columns[index]["sort"] = sortname;
2256 columns[index]["image"] = imagename;
2257 columns[index]["label"] = labelname;
2258 columns[index]["width"] = columnwidth;
2259 columns[index]["relwidth"] = columnrelwidth;
2260 columns[index]["dynamicwidth"] = columndynamicwidth;
2261 index++;
2262 }
2263 }
2264 scroll_list->setNumDynamicColumns(num_dynamic);
2265 scroll_list->setTotalStaticColumnWidth(total_static);
2266 scroll_list->setColumnHeadings(columns);
2267
2268 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
2269 {
2270 if (child->hasName("row"))
2271 {
2272 LLUUID id;
2273 child->getAttributeUUID("id", id);
2274
2275 LLSD row;
2276
2277 row["id"] = id;
2278
2279 S32 column_idx = 0;
2280 LLXMLNodePtr row_child;
2281 for (row_child = child->getFirstChild(); row_child.notNull(); row_child = row_child->getNextSibling())
2282 {
2283 if (row_child->hasName("column"))
2284 {
2285 LLString value = row_child->getTextContents();
2286
2287 LLString columnname("");
2288 row_child->getAttributeString("name", columnname);
2289
2290 LLString font("");
2291 row_child->getAttributeString("font", font);
2292
2293 LLString font_style("");
2294 row_child->getAttributeString("font-style", font_style);
2295
2296 row["columns"][column_idx]["column"] = columnname;
2297 row["columns"][column_idx]["value"] = value;
2298 row["columns"][column_idx]["font"] = font;
2299 row["columns"][column_idx]["font-style"] = font_style;
2300 column_idx++;
2301 }
2302 }
2303 scroll_list->addElement(row);
2304 }
2305 }
2306
2307 LLString contents = node->getTextContents();
2308 if (!contents.empty())
2309 {
2310 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
2311 boost::char_separator<char> sep("\t\n");
2312 tokenizer tokens(contents, sep);
2313 tokenizer::iterator token_iter = tokens.begin();
2314
2315 while(token_iter != tokens.end())
2316 {
2317 const char* line = token_iter->c_str();
2318 scroll_list->addSimpleItem(line);
2319 ++token_iter;
2320 }
2321 }
2322
2323 return scroll_list;
2324}
2325
2326// LLEditMenuHandler functions
2327
2328// virtual
2329void LLScrollListCtrl::copy()
2330{
2331 LLString buffer;
2332
2333 std::vector<LLScrollListItem*> items = getAllSelected();
2334 std::vector<LLScrollListItem*>::iterator itor;
2335 for (itor = items.begin(); itor != items.end(); ++itor)
2336 {
2337 buffer += (*itor)->getContentsCSV() + "\n";
2338 }
2339 gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length());
2340}
2341
2342// virtual
2343BOOL LLScrollListCtrl::canCopy()
2344{
2345 return (getFirstSelected() != NULL);
2346}
2347
2348// virtual
2349void LLScrollListCtrl::cut()
2350{
2351 copy();
2352 doDelete();
2353}
2354
2355// virtual
2356BOOL LLScrollListCtrl::canCut()
2357{
2358 return canCopy() && canDoDelete();
2359}
2360
2361// virtual
2362void LLScrollListCtrl::doDelete()
2363{
2364 // Not yet implemented
2365}
2366
2367// virtual
2368BOOL LLScrollListCtrl::canDoDelete()
2369{
2370 // Not yet implemented
2371 return FALSE;
2372}
2373
2374// virtual
2375void LLScrollListCtrl::selectAll()
2376{
2377 // Deselects all other items
2378 item_list::iterator iter;
2379 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
2380 {
2381 LLScrollListItem *itemp = *iter;
2382 if( itemp->getEnabled() )
2383 {
2384 selectItem(itemp, FALSE);
2385 }
2386 }
2387
2388 if (mCommitOnSelectionChange)
2389 {
2390 commitIfChanged();
2391 }
2392}
2393
2394// virtual
2395BOOL LLScrollListCtrl::canSelectAll()
2396{
2397 return getCanSelect() && mAllowMultipleSelection && !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable);
2398}
2399
2400// virtual
2401void LLScrollListCtrl::deselect()
2402{
2403 deselectAllItems();
2404}
2405
2406// virtual
2407BOOL LLScrollListCtrl::canDeselect()
2408{
2409 return getCanSelect();
2410}
2411
2412void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos)
2413{
2414 LLString name = column["name"].asString();
2415 if (mColumns.size() == 0)
2416 {
2417 mDefaultColumn = 0;
2418 }
2419 if (mColumns.find(name) == mColumns.end())
2420 {
2421 // Add column
2422 mColumns[name] = LLScrollListColumn(column);
2423 LLScrollListColumn* new_column = &mColumns[name];
2424 new_column->mParentCtrl = this;
2425 new_column->mIndex = mColumns.size()-1;
2426
2427 // Add button
2428 if (new_column->mWidth > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth)
2429 {
2430 if (new_column->mRelWidth >= 0)
2431 {
2432 new_column->mWidth = (S32)llround(new_column->mRelWidth*mItemListRect.getWidth());
2433 }
2434 else if(new_column->mDynamicWidth)
2435 {
2436 new_column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns;
2437 }
2438 S32 top = mItemListRect.mTop;
2439 S32 left = mItemListRect.mLeft;
2440 {
2441 std::map<LLString, LLScrollListColumn>::iterator itor;
2442 for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
2443 {
2444 if (itor->second.mIndex < new_column->mIndex &&
2445 itor->second.mWidth > 0)
2446 {
2447 left += itor->second.mWidth + mColumnPadding;
2448 }
2449 }
2450 }
2451 LLString button_name = "btn_" + name;
2452 S32 right = left+new_column->mWidth;
2453 if (new_column->mIndex != (S32)mColumns.size()-1)
2454 {
2455 right += mColumnPadding;
2456 }
2457 LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top);
2458 new_column->mButton = new LLSquareButton(button_name, temp_rect, "", mHeadingFont, "", onClickColumn, NULL);
2459 if(column["image"].asString() != "")
2460 {
2461 //new_column->mButton->setScaleImage(false);
2462 new_column->mButton->setImageSelected(column["image"].asString());
2463 new_column->mButton->setImageUnselected(column["image"].asString());
2464 }
2465 else
2466 {
2467 new_column->mButton->setLabelSelected(new_column->mLabel);
2468 new_column->mButton->setLabelUnselected(new_column->mLabel);
2469 }
2470 //RN: although it might be useful to change sort order with the keyboard,
2471 // mixing tab stops on child items along with the parent item is not supported yet
2472 new_column->mButton->setTabStop(FALSE);
2473 addChild(new_column->mButton);
2474 new_column->mButton->setVisible(mDisplayColumnButtons);
2475
2476
2477 // Move scroll to front
2478 removeChild(mScrollbar);
2479 addChild(mScrollbar);
2480
2481 new_column->mButton->setCallbackUserData(new_column);
2482 }
2483 }
2484 updateColumns();
2485}
2486
2487// static
2488void LLScrollListCtrl::onClickColumn(void *userdata)
2489{
2490 LLScrollListColumn *info = (LLScrollListColumn*)userdata;
2491 if (!info) return;
2492
2493 LLScrollListCtrl *parent = info->mParentCtrl;
2494 if (!parent) return;
2495
2496 U32 column_index = info->mIndex;
2497
2498 LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex];
2499 if (column->mSortingColumn != column->mName)
2500 {
2501 if (parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end())
2502 {
2503 LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn];
2504 column_index = info_redir.mIndex;
2505 }
2506 }
2507
2508 bool ascending = true;
2509 if (column_index == parent->mSortColumn)
2510 {
2511 ascending = !parent->mSortAscending;
2512 }
2513
2514 parent->sortByColumn(column_index, ascending);
2515
2516 if (parent->mOnSortChangedCallback)
2517 {
2518 parent->mOnSortChangedCallback(parent->getCallbackUserData());
2519 }
2520}
2521
2522std::string LLScrollListCtrl::getSortColumnName()
2523{
2524 LLScrollListColumn* column = mColumnsIndexed[mSortColumn];
2525
2526 if (column) return column->mName;
2527 else return "";
2528}
2529
2530void LLScrollListCtrl::clearColumns()
2531{
2532 std::map<LLString, LLScrollListColumn>::iterator itor;
2533 for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
2534 {
2535 LLButton *button = itor->second.mButton;
2536 if (button)
2537 {
2538 removeChild(button);
2539 delete button;
2540 }
2541 }
2542 mColumns.clear();
2543}
2544
2545void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& label)
2546{
2547 std::map<LLString, LLScrollListColumn>::iterator itor = mColumns.find(column);
2548 if (itor != mColumns.end())
2549 {
2550 itor->second.mLabel = label;
2551 if (itor->second.mButton)
2552 {
2553 itor->second.mButton->setLabelSelected(label);
2554 itor->second.mButton->setLabelUnselected(label);
2555 }
2556 }
2557}
2558
2559void LLScrollListCtrl::setColumnHeadings(LLSD headings)
2560{
2561 mColumns.clear();
2562 LLSD::array_const_iterator itor;
2563 for (itor = headings.beginArray(); itor != headings.endArray(); ++itor)
2564 {
2565 addColumn(*itor);
2566 }
2567}
2568
2569LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition pos, void* userdata)
2570{
2571 // ID
2572 LLSD id = value["id"];
2573
2574 LLScrollListItem *new_item = new LLScrollListItem(id, userdata);
2575 if (value.has("enabled"))
2576 {
2577 new_item->setEnabled( value["enabled"].asBoolean() );
2578 }
2579
2580 new_item->setNumColumns(mColumns.size());
2581
2582 // Add any columns we don't already have
2583 LLSD columns = value["columns"];
2584 LLSD::array_const_iterator itor;
2585 for (itor = columns.beginArray(); itor != columns.endArray(); ++itor)
2586 {
2587 LLString column = (*itor)["column"].asString();
2588
2589 if (mColumns.size() == 0)
2590 {
2591 mDefaultColumn = 0;
2592 }
2593 std::map<LLString, LLScrollListColumn>::iterator column_itor = mColumns.find(column);
2594 if (column_itor == mColumns.end())
2595 {
2596 LLSD new_column;
2597 new_column["name"] = column;
2598 new_column["label"] = column;
2599 new_column["width"] = 0;
2600 addColumn(new_column);
2601 column_itor = mColumns.find(column);
2602 new_item->setNumColumns(mColumns.size());
2603 }
2604
2605 S32 index = column_itor->second.mIndex;
2606 S32 width = column_itor->second.mWidth;
2607
2608 LLSD value = (*itor)["value"];
2609 LLString fontname = (*itor)["font"].asString();
2610 LLString fontstyle = (*itor)["font-style"].asString();
2611 LLString type = (*itor)["type"].asString();
2612
2613 const LLFontGL *font = gResMgr->getRes(fontname);
2614 if (!font)
2615 {
2616 font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
2617 }
2618 U8 font_style = LLFontGL::getStyleFromString(fontstyle);
2619
2620 if (type == "icon")
2621 {
2622 LLUUID image_id = value.asUUID();
2623 LLImageGL* icon = LLUI::sImageProvider->getUIImageByID(image_id);
2624 new_item->setColumn(index, new LLScrollListIcon(icon, width, image_id));
2625 }
2626 else if (type == "checkbox")
2627 {
2628 LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl(value.asString(),
2629 LLRect(0, 0, width, width), "label");
2630 new_item->setColumn(index, new LLScrollListCheck(ctrl,width));
2631 }
2632 else
2633 {
2634 new_item->setColumn(index, new LLScrollListText(value.asString(), font, width, font_style));
2635 }
2636 }
2637
2638 S32 num_columns = mColumns.size();
2639 for (S32 column = 0; column < num_columns; ++column)
2640 {
2641 if (new_item->getColumn(column) == NULL)
2642 {
2643 LLScrollListColumn* column_ptr = mColumnsIndexed[column];
2644 new_item->setColumn(column, new LLScrollListText("", gResMgr->getRes( LLFONT_SANSSERIF_SMALL ), column_ptr->mWidth, LLFontGL::NORMAL));
2645 }
2646 }
2647
2648 addItem(new_item, pos);
2649
2650 return new_item;
2651}
2652
2653LLScrollListItem* LLScrollListCtrl::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id)
2654{
2655 LLSD entry_id = id;
2656
2657 if (id.isUndefined())
2658 {
2659 entry_id = value;
2660 }
2661
2662 LLScrollListItem *new_item = new LLScrollListItem(entry_id);
2663
2664 const LLFontGL *font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
2665
2666 new_item->addColumn(value, font, getRect().getWidth());
2667
2668 addItem(new_item, pos);
2669 return new_item;
2670}
2671
2672void LLScrollListCtrl::setValue(const LLSD& value )
2673{
2674 LLSD::array_const_iterator itor;
2675 for (itor = value.beginArray(); itor != value.endArray(); ++itor)
2676 {
2677 addElement(*itor);
2678 }
2679}
2680
2681LLSD LLScrollListCtrl::getValue() const
2682{
2683 LLScrollListItem *item = getFirstSelected();
2684 if (!item) return LLSD();
2685 return item->getValue();
2686}
2687
2688BOOL LLScrollListCtrl::operateOnSelection(EOperation op)
2689{
2690 if (op == OP_DELETE)
2691 {
2692 deleteSelectedItems();
2693 return TRUE;
2694 }
2695 else if (op == OP_DESELECT)
2696 {
2697 deselectAllItems();
2698 }
2699 return FALSE;
2700}
2701
2702BOOL LLScrollListCtrl::operateOnAll(EOperation op)
2703{
2704 if (op == OP_DELETE)
2705 {
2706 clearRows();
2707 return TRUE;
2708 }
2709 else if (op == OP_DESELECT)
2710 {
2711 deselectAllItems();
2712 }
2713 else if (op == OP_SELECT)
2714 {
2715 selectAll();
2716 }
2717 return FALSE;
2718}
2719//virtual
2720void LLScrollListCtrl::setFocus(BOOL b)
2721{
2722 mSearchString.clear();
2723 // for tabbing into pristine scroll lists (Finder)
2724 if (!getFirstSelected())
2725 {
2726 selectFirstItem();
2727 onCommit();
2728 }
2729 LLUICtrl::setFocus(b);
2730}
2731//virtual
2732void LLScrollListCtrl::onFocusLost()
2733{
2734 if (mIsPopup)
2735 {
2736 if (getParent())
2737 {
2738 getParent()->onFocusLost();
2739 }
2740 }
2741}