00001
00032 #include <algorithm>
00033
00034 #include "linden_common.h"
00035 #include "llstl.h"
00036 #include "llboost.h"
00037
00038 #include "llscrolllistctrl.h"
00039
00040 #include "indra_constants.h"
00041
00042 #include "llcheckboxctrl.h"
00043 #include "llclipboard.h"
00044 #include "llfocusmgr.h"
00045 #include "llgl.h"
00046 #include "llglimmediate.h"
00047 #include "llglheaders.h"
00048 #include "llresmgr.h"
00049 #include "llscrollbar.h"
00050 #include "llstring.h"
00051 #include "llui.h"
00052 #include "lluictrlfactory.h"
00053 #include "llwindow.h"
00054 #include "llcontrol.h"
00055 #include "llkeyboard.h"
00056 #include "llresizebar.h"
00057
00058 const S32 MIN_COLUMN_WIDTH = 20;
00059 const S32 LIST_SNAP_PADDING = 5;
00060
00061 static LLRegisterWidget<LLScrollListCtrl> r("scroll_list");
00062
00063
00064 struct SortScrollListItem
00065 {
00066 SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders)
00067 : mSortOrders(sort_orders)
00068 {}
00069
00070 bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2)
00071 {
00072 if ( mSortOrders.empty() )
00073 return i1 < i2;
00074
00075
00076 S32 sort_result = 0;
00077 for (sort_order_t::const_reverse_iterator it = mSortOrders.rbegin();
00078 it != mSortOrders.rend(); ++it)
00079 {
00080 S32 col_idx = it->first;
00081 BOOL sort_ascending = it->second;
00082
00083 const LLScrollListCell *cell1 = i1->getColumn(col_idx);
00084 const LLScrollListCell *cell2 = i2->getColumn(col_idx);
00085 S32 order = sort_ascending ? 1 : -1;
00086 if (cell1 && cell2)
00087 {
00088 sort_result = order * LLString::compareDict(cell1->getValue().asString(), cell2->getValue().asString());
00089 if (sort_result != 0)
00090 {
00091 break;
00092 }
00093 }
00094 }
00095
00096 return sort_result < 0;
00097 }
00098
00099 typedef std::vector<std::pair<S32, BOOL> > sort_order_t;
00100 const sort_order_t& mSortOrders;
00101 };
00102
00103
00104
00105
00106
00107 LLScrollListIcon::LLScrollListIcon(LLUIImagePtr icon, S32 width)
00108 : LLScrollListCell(width),
00109 mIcon(icon),
00110 mColor(LLColor4::white)
00111 {
00112 }
00113
00114 LLScrollListIcon::LLScrollListIcon(const LLSD& value, S32 width)
00115 : LLScrollListCell(width),
00116 mColor(LLColor4::white)
00117 {
00118 setValue(value);
00119 }
00120
00121
00122 LLScrollListIcon::~LLScrollListIcon()
00123 {
00124 }
00125
00126 void LLScrollListIcon::setValue(const LLSD& value)
00127 {
00128 if (value.isUUID())
00129 {
00130
00131 LLUUID image_id = value.asUUID();
00132 mIcon = image_id.notNull() ? LLUI::sImageProvider->getUIImageByID(image_id) : LLUIImagePtr(NULL);
00133 }
00134 else
00135 {
00136 LLString value_string = value.asString();
00137 if (LLUUID::validate(value_string))
00138 {
00139 setValue(LLUUID(value_string));
00140 }
00141 else if (!value_string.empty())
00142 {
00143 mIcon = LLUI::getUIImage(value.asString());
00144 }
00145 else
00146 {
00147 mIcon = NULL;
00148 }
00149 }
00150 }
00151
00152
00153 void LLScrollListIcon::setColor(const LLColor4& color)
00154 {
00155 mColor = color;
00156 }
00157
00158 S32 LLScrollListIcon::getWidth() const
00159 {
00160
00161 if (LLScrollListCell::getWidth() == 0 && mIcon.notNull())
00162 {
00163 return mIcon->getWidth();
00164 }
00165 return LLScrollListCell::getWidth();
00166 }
00167
00168
00169 void LLScrollListIcon::draw(const LLColor4& color, const LLColor4& highlight_color) const
00170 {
00171 if (mIcon)
00172 {
00173 mIcon->draw(0, 0, mColor);
00174 }
00175 }
00176
00177
00178
00179
00180 LLScrollListCheck::LLScrollListCheck(LLCheckBoxCtrl* check_box, S32 width)
00181 {
00182 mCheckBox = check_box;
00183 LLRect rect(mCheckBox->getRect());
00184 if (width)
00185 {
00186
00187 rect.mRight = rect.mLeft + width;
00188 mCheckBox->setRect(rect);
00189 setWidth(width);
00190 }
00191 else
00192 {
00193 setWidth(rect.getWidth());
00194 }
00195 }
00196
00197 LLScrollListCheck::~LLScrollListCheck()
00198 {
00199 delete mCheckBox;
00200 }
00201
00202 void LLScrollListCheck::draw(const LLColor4& color, const LLColor4& highlight_color) const
00203 {
00204 mCheckBox->draw();
00205 }
00206
00207 BOOL LLScrollListCheck::handleClick()
00208 {
00209 if (mCheckBox->getEnabled())
00210 {
00211 mCheckBox->toggle();
00212 }
00213
00214 return TRUE;
00215 }
00216
00217
00218
00219
00220 LLScrollListSeparator::LLScrollListSeparator(S32 width) : LLScrollListCell(width)
00221 {
00222 }
00223
00224
00225 S32 LLScrollListSeparator::getHeight() const
00226 {
00227 return 5;
00228 }
00229
00230
00231 void LLScrollListSeparator::draw(const LLColor4& color, const LLColor4& highlight_color) const
00232 {
00233
00234 gl_line_2d(5, 8, llmax(5, getWidth() - 5), 8, color);
00235 }
00236
00237
00238
00239
00240 U32 LLScrollListText::sCount = 0;
00241
00242 LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, S32 width, U8 font_style, LLFontGL::HAlign font_alignment, LLColor4& color, BOOL use_color, BOOL visible)
00243 : LLScrollListCell(width),
00244 mText( text ),
00245 mFont( font ),
00246 mColor(color),
00247 mUseColor(use_color),
00248 mFontStyle( font_style ),
00249 mFontAlignment( font_alignment ),
00250 mVisible( visible ),
00251 mHighlightCount( 0 ),
00252 mHighlightOffset( 0 )
00253 {
00254 sCount++;
00255
00256
00257 if (!mRoundedRectImage)
00258 {
00259 mRoundedRectImage = LLUI::sImageProvider->getUIImage("rounded_square.tga");
00260 }
00261 }
00262
00263 void LLScrollListText::highlightText(S32 offset, S32 num_chars)
00264 {
00265 mHighlightOffset = offset;
00266 mHighlightCount = num_chars;
00267 }
00268
00269
00270 BOOL LLScrollListText::isText() const
00271 {
00272 return TRUE;
00273 }
00274
00275
00276 BOOL LLScrollListText::getVisible() const
00277 {
00278 return mVisible;
00279 }
00280
00281
00282 S32 LLScrollListText::getHeight() const
00283 {
00284 return llround(mFont->getLineHeight());
00285 }
00286
00287
00288 LLScrollListText::~LLScrollListText()
00289 {
00290 sCount--;
00291 }
00292
00293 S32 LLScrollListText::getContentWidth() const
00294 {
00295 return mFont->getWidth(mText.getString());
00296 }
00297
00298
00299 void LLScrollListText::setColor(const LLColor4& color)
00300 {
00301 mColor = color;
00302 mUseColor = TRUE;
00303 }
00304
00305 void LLScrollListText::setText(const LLStringExplicit& text)
00306 {
00307 mText = text;
00308 }
00309
00310
00311 void LLScrollListText::setValue(const LLSD& text)
00312 {
00313 setText(text.asString());
00314 }
00315
00316
00317 const LLSD LLScrollListText::getValue() const
00318 {
00319 return LLSD(mText.getString());
00320 }
00321
00322
00323 void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_color) const
00324 {
00325 LLColor4 display_color;
00326 if (mUseColor)
00327 {
00328 display_color = mColor;
00329 }
00330 else
00331 {
00332 display_color = color;
00333 }
00334
00335 if (mHighlightCount > 0)
00336 {
00337 S32 left = 0;
00338 switch(mFontAlignment)
00339 {
00340 case LLFontGL::LEFT:
00341 left = mFont->getWidth(mText.getString(), 0, mHighlightOffset);
00342 break;
00343 case LLFontGL::RIGHT:
00344 left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX);
00345 break;
00346 case LLFontGL::HCENTER:
00347 left = (getWidth() - mFont->getWidth(mText.getString())) / 2;
00348 break;
00349 }
00350 LLRect highlight_rect(left - 2,
00351 llround(mFont->getLineHeight()) + 1,
00352 left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1,
00353 1);
00354 mRoundedRectImage->draw(highlight_rect, highlight_color);
00355 }
00356
00357
00358 F32 right_x;
00359 U32 string_chars = mText.length();
00360 F32 start_x = 0.f;
00361 switch(mFontAlignment)
00362 {
00363 case LLFontGL::LEFT:
00364 start_x = 0.f;
00365 break;
00366 case LLFontGL::RIGHT:
00367 start_x = (F32)getWidth();
00368 break;
00369 case LLFontGL::HCENTER:
00370 start_x = (F32)getWidth() * 0.5f;
00371 break;
00372 }
00373 mFont->render(mText.getWString(), 0,
00374 start_x, 2.f,
00375 display_color,
00376 mFontAlignment,
00377 LLFontGL::BOTTOM,
00378 mFontStyle,
00379 string_chars,
00380 getWidth(),
00381 &right_x,
00382 FALSE,
00383 TRUE);
00384 }
00385
00386
00387 LLScrollListItem::~LLScrollListItem()
00388 {
00389 std::for_each(mColumns.begin(), mColumns.end(), DeletePointer());
00390 }
00391
00392 void LLScrollListItem::setNumColumns(S32 columns)
00393 {
00394 S32 prev_columns = mColumns.size();
00395 if (columns < prev_columns)
00396 {
00397 std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer());
00398 }
00399
00400 mColumns.resize(columns);
00401
00402 for (S32 col = prev_columns; col < columns; ++col)
00403 {
00404 mColumns[col] = NULL;
00405 }
00406 }
00407
00408 void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell )
00409 {
00410 if (column < (S32)mColumns.size())
00411 {
00412 delete mColumns[column];
00413 mColumns[column] = cell;
00414 }
00415 else
00416 {
00417 llerrs << "LLScrollListItem::setColumn: bad column: " << column << llendl;
00418 }
00419 }
00420
00421 LLString LLScrollListItem::getContentsCSV() const
00422 {
00423 LLString ret;
00424
00425 S32 count = getNumColumns();
00426 for (S32 i=0; i<count; ++i)
00427 {
00428 ret += getColumn(i)->getValue().asString();
00429 if (i < count-1)
00430 {
00431 ret += ", ";
00432 }
00433 }
00434
00435 return ret;
00436 }
00437
00438 void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding)
00439 {
00440
00441 LLRect bg_rect = rect;
00442 {
00443 LLGLSNoTexture no_texture;
00444 gGL.color4fv(bg_color.mV);
00445 gl_rect_2d( bg_rect );
00446 }
00447
00448 S32 cur_x = rect.mLeft;
00449 S32 num_cols = getNumColumns();
00450 S32 cur_col = 0;
00451
00452 for (LLScrollListCell* cell = getColumn(0); cur_col < num_cols; cell = getColumn(++cur_col))
00453 {
00454
00455 if (cell->getWidth() < 0
00456 || !cell->getVisible()) continue;
00457
00458 LLUI::pushMatrix();
00459 {
00460 LLUI::translate((F32) cur_x, (F32) rect.mBottom, 0.0f);
00461
00462 cell->draw( fg_color, highlight_color );
00463 }
00464 LLUI::popMatrix();
00465
00466 cur_x += cell->getWidth() + column_padding;
00467 }
00468 }
00469
00470
00471 void LLScrollListItem::setEnabled(BOOL b)
00472 {
00473 mEnabled = b;
00474 }
00475
00476
00477
00478
00479 LLScrollListItemComment::LLScrollListItemComment(const LLString& comment_string, const LLColor4& color)
00480 : LLScrollListItem(FALSE),
00481 mColor(color)
00482 {
00483 addColumn( comment_string, LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL ) );
00484 }
00485
00486 void LLScrollListItemComment::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding)
00487 {
00488 LLScrollListCell* cell = getColumn(0);
00489 if (cell)
00490 {
00491
00492 if (cell->getWidth() < 0
00493 || !cell->getVisible()) return;
00494
00495 LLUI::pushMatrix();
00496 {
00497 LLUI::translate((F32)rect.mLeft, (F32)rect.mBottom, 0.0f);
00498
00499
00500 cell->setWidth(rect.getWidth());
00501 cell->draw( mColor, highlight_color );
00502 }
00503 LLUI::popMatrix();
00504 }
00505 }
00506
00507
00508
00509
00510 LLScrollListItemSeparator::LLScrollListItemSeparator()
00511 : LLScrollListItem(FALSE)
00512 {
00513 LLScrollListSeparator* cell = new LLScrollListSeparator(0);
00514 setNumColumns(1);
00515 setColumn(0, cell);
00516 }
00517
00518 void LLScrollListItemSeparator::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding)
00519 {
00520
00521 LLScrollListCell* cell = getColumn(0);
00522 if (cell)
00523 {
00524
00525 if (cell->getWidth() < 0
00526 || !cell->getVisible()) return;
00527
00528 LLUI::pushMatrix();
00529 {
00530 LLUI::translate((F32)rect.mLeft, (F32)rect.mBottom, 0.0f);
00531
00532
00533 cell->setWidth(rect.getWidth());
00534 cell->draw( fg_color, highlight_color );
00535 }
00536 LLUI::popMatrix();
00537 }
00538 }
00539
00540
00541
00542
00543
00544 LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect,
00545 void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
00546 void* callback_user_data,
00547 BOOL allow_multiple_selection,
00548 BOOL show_border
00549 )
00550 : LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data),
00551 mLineHeight(0),
00552 mScrollLines(0),
00553 mPageLines(0),
00554 mHeadingHeight(20),
00555 mMaxSelectable(0),
00556 mAllowMultipleSelection( allow_multiple_selection ),
00557 mAllowKeyboardMovement(TRUE),
00558 mCommitOnKeyboardMovement(TRUE),
00559 mCommitOnSelectionChange(FALSE),
00560 mSelectionChanged(FALSE),
00561 mNeedsScroll(FALSE),
00562 mCanSelect(TRUE),
00563 mDisplayColumnHeaders(FALSE),
00564 mColumnsDirty(FALSE),
00565 mMaxItemCount(INT_MAX),
00566 mMaxContentWidth(0),
00567 mBackgroundVisible( TRUE ),
00568 mDrawStripes(TRUE),
00569 mBgWriteableColor( LLUI::sColorsGroup->getColor( "ScrollBgWriteableColor" ) ),
00570 mBgReadOnlyColor( LLUI::sColorsGroup->getColor( "ScrollBgReadOnlyColor" ) ),
00571 mBgSelectedColor( LLUI::sColorsGroup->getColor("ScrollSelectedBGColor") ),
00572 mBgStripeColor( LLUI::sColorsGroup->getColor("ScrollBGStripeColor") ),
00573 mFgSelectedColor( LLUI::sColorsGroup->getColor("ScrollSelectedFGColor") ),
00574 mFgUnselectedColor( LLUI::sColorsGroup->getColor("ScrollUnselectedColor") ),
00575 mFgDisabledColor( LLUI::sColorsGroup->getColor("ScrollDisabledColor") ),
00576 mHighlightedColor( LLUI::sColorsGroup->getColor("ScrollHighlightedColor") ),
00577 mBorderThickness( 2 ),
00578 mOnDoubleClickCallback( NULL ),
00579 mOnMaximumSelectCallback( NULL ),
00580 mOnSortChangedCallback( NULL ),
00581 mHighlightedItem(-1),
00582 mBorder(NULL),
00583 mSearchColumn(0),
00584 mNumDynamicWidthColumns(0),
00585 mTotalStaticColumnWidth(0),
00586 mSorted(TRUE),
00587 mDirty(FALSE),
00588 mOriginalSelection(-1),
00589 mDrewSelected(FALSE)
00590 {
00591 mItemListRect.setOriginAndSize(
00592 mBorderThickness,
00593 mBorderThickness,
00594 getRect().getWidth() - 2 * mBorderThickness,
00595 getRect().getHeight() - 2 * mBorderThickness );
00596
00597 updateLineHeight();
00598
00599 mPageLines = mLineHeight? (mItemListRect.getHeight()) / mLineHeight : 0;
00600
00601
00602 LLRect scroll_rect;
00603 scroll_rect.setOriginAndSize(
00604 getRect().getWidth() - mBorderThickness - SCROLLBAR_SIZE,
00605 mItemListRect.mBottom,
00606 SCROLLBAR_SIZE,
00607 mItemListRect.getHeight());
00608 mScrollbar = new LLScrollbar( "Scrollbar", scroll_rect,
00609 LLScrollbar::VERTICAL,
00610 getItemCount(),
00611 mScrollLines,
00612 mPageLines,
00613 &LLScrollListCtrl::onScrollChange, this );
00614 mScrollbar->setFollowsRight();
00615 mScrollbar->setFollowsTop();
00616 mScrollbar->setFollowsBottom();
00617 mScrollbar->setEnabled( TRUE );
00618
00619 mScrollbar->setVisible(FALSE);
00620 addChild(mScrollbar);
00621
00622
00623 if (show_border)
00624 {
00625 LLRect border_rect( 0, getRect().getHeight(), getRect().getWidth(), 0 );
00626 mBorder = new LLViewBorder( "dlg border", border_rect, LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, 1 );
00627 addChild(mBorder);
00628 }
00629
00630 mColumnPadding = 5;
00631
00632 mLastSelected = NULL;
00633 }
00634
00635 LLScrollListCtrl::~LLScrollListCtrl()
00636 {
00637 std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
00638
00639 if( gEditMenuHandler == this )
00640 {
00641 gEditMenuHandler = NULL;
00642 }
00643 }
00644
00645
00646 BOOL LLScrollListCtrl::setMaxItemCount(S32 max_count)
00647 {
00648 if (max_count >= getItemCount())
00649 {
00650 mMaxItemCount = max_count;
00651 }
00652 return (max_count == mMaxItemCount);
00653 }
00654
00655 S32 LLScrollListCtrl::isEmpty() const
00656 {
00657 return mItemList.empty();
00658 }
00659
00660 S32 LLScrollListCtrl::getItemCount() const
00661 {
00662 return mItemList.size();
00663 }
00664
00665
00666 void LLScrollListCtrl::clearRows()
00667 {
00668 std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
00669 mItemList.clear();
00670
00671
00672
00673 mScrollbar->setDocParams(0, 0);
00674
00675 mScrollLines = 0;
00676 mLastSelected = NULL;
00677 updateLayout();
00678 mDirty = FALSE;
00679 }
00680
00681
00682 LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
00683 {
00684 item_list::const_iterator iter;
00685 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
00686 {
00687 LLScrollListItem* item = *iter;
00688 if (item->getSelected())
00689 {
00690 return item;
00691 }
00692 }
00693 return NULL;
00694 }
00695
00696 std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
00697 {
00698 std::vector<LLScrollListItem*> ret;
00699 item_list::const_iterator iter;
00700 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
00701 {
00702 LLScrollListItem* item = *iter;
00703 if (item->getSelected())
00704 {
00705 ret.push_back(item);
00706 }
00707 }
00708 return ret;
00709 }
00710
00711 S32 LLScrollListCtrl::getFirstSelectedIndex() const
00712 {
00713 S32 CurSelectedIndex = 0;
00714 item_list::const_iterator iter;
00715 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
00716 {
00717 LLScrollListItem* item = *iter;
00718 if (item->getSelected())
00719 {
00720 return CurSelectedIndex;
00721 }
00722 CurSelectedIndex++;
00723 }
00724
00725 return -1;
00726 }
00727
00728 LLScrollListItem* LLScrollListCtrl::getFirstData() const
00729 {
00730 if (mItemList.size() == 0)
00731 {
00732 return NULL;
00733 }
00734 return mItemList[0];
00735 }
00736
00737 LLScrollListItem* LLScrollListCtrl::getLastData() const
00738 {
00739 if (mItemList.size() == 0)
00740 {
00741 return NULL;
00742 }
00743 return mItemList[mItemList.size() - 1];
00744 }
00745
00746 std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const
00747 {
00748 std::vector<LLScrollListItem*> ret;
00749 item_list::const_iterator iter;
00750 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
00751 {
00752 LLScrollListItem* item = *iter;
00753 ret.push_back(item);
00754 }
00755 return ret;
00756 }
00757
00758
00759 LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const
00760 {
00761 LLString string_val = sd.asString();
00762
00763 item_list::const_iterator iter;
00764 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
00765 {
00766 LLScrollListItem* item = *iter;
00767
00768 if (item->getValue().asString() == string_val)
00769 {
00770 return item;
00771 }
00772 }
00773 return NULL;
00774 }
00775
00776
00777 void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent )
00778 {
00779 LLUICtrl::reshape( width, height, called_from_parent );
00780
00781 updateLayout();
00782 }
00783
00784 void LLScrollListCtrl::updateLayout()
00785 {
00786
00787 S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0);
00788 mItemListRect.setOriginAndSize(
00789 mBorderThickness,
00790 mBorderThickness,
00791 getRect().getWidth() - 2 * mBorderThickness,
00792 getRect().getHeight() - (2 * mBorderThickness ) - heading_size );
00793
00794
00795 mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0;
00796 BOOL scrollbar_visible = getItemCount() > mPageLines;
00797 if (scrollbar_visible)
00798 {
00799
00800 mItemListRect.mRight = getRect().getWidth() - mBorderThickness - SCROLLBAR_SIZE;
00801 }
00802
00803 mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0));
00804 mScrollbar->setPageSize( mPageLines );
00805 mScrollbar->setDocSize( getItemCount() );
00806 mScrollbar->setVisible(scrollbar_visible);
00807
00808 dirtyColumns();
00809 }
00810
00811
00812
00813 void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height)
00814 {
00815 S32 height = llmin( getRequiredRect().getHeight(), max_height );
00816 S32 width = getRect().getWidth();
00817
00818 reshape( width, height );
00819 }
00820
00821
00822 LLRect LLScrollListCtrl::getRequiredRect()
00823 {
00824 S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0);
00825 S32 height = (mLineHeight * getItemCount())
00826 + (2 * mBorderThickness )
00827 + heading_size;
00828 S32 width = getRect().getWidth();
00829
00830 return LLRect(0, height, width, 0);
00831 }
00832
00833
00834 BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL requires_column )
00835 {
00836 BOOL not_too_big = getItemCount() < mMaxItemCount;
00837 if (not_too_big)
00838 {
00839 switch( pos )
00840 {
00841 case ADD_TOP:
00842 mItemList.push_front(item);
00843 setSorted(FALSE);
00844 break;
00845
00846 case ADD_SORTED:
00847 {
00848
00849 std::vector<sort_column_t> single_sort_column;
00850 single_sort_column.push_back(std::make_pair(0, TRUE));
00851
00852 mItemList.push_back(item);
00853 std::stable_sort(
00854 mItemList.begin(),
00855 mItemList.end(),
00856 SortScrollListItem(single_sort_column));
00857
00858
00859
00860 setSorted(FALSE);
00861 break;
00862 }
00863 case ADD_BOTTOM:
00864 mItemList.push_back(item);
00865 setSorted(FALSE);
00866 break;
00867
00868 default:
00869 llassert(0);
00870 mItemList.push_back(item);
00871 setSorted(FALSE);
00872 break;
00873 }
00874
00875
00876 if (mColumns.empty() && requires_column)
00877 {
00878 LLSD new_column;
00879 new_column["name"] = "default_column";
00880 new_column["label"] = "";
00881 new_column["dynamicwidth"] = TRUE;
00882 addColumn(new_column);
00883 }
00884
00885 updateLineHeightInsert(item);
00886
00887 updateLayout();
00888 }
00889
00890 return not_too_big;
00891 }
00892
00893
00894
00895
00896 void LLScrollListCtrl::calcColumnWidths()
00897 {
00898 const S32 HEADING_TEXT_PADDING = 30;
00899 const S32 COLUMN_TEXT_PADDING = 20;
00900
00901 mMaxContentWidth = 0;
00902
00903 S32 max_item_width = 0;
00904
00905 ordered_columns_t::iterator column_itor;
00906 for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor)
00907 {
00908 LLScrollListColumn* column = *column_itor;
00909 if (!column) continue;
00910
00911
00912 S32 new_width = column->mWidth;
00913 if (column->mRelWidth >= 0)
00914 {
00915 new_width = (S32)llround(column->mRelWidth*mItemListRect.getWidth());
00916 }
00917 else if (column->mDynamicWidth)
00918 {
00919 new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns;
00920 }
00921
00922 if (new_width != column->mWidth)
00923 {
00924 column->mWidth = new_width;
00925 }
00926
00927
00928 column->mMaxContentWidth = column->mHeader ? LLFontGL::sSansSerifSmall->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0;
00929 item_list::iterator iter;
00930 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
00931 {
00932 LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex);
00933 if (!cellp) continue;
00934
00935 column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth);
00936 }
00937
00938 max_item_width += column->mMaxContentWidth;
00939 }
00940
00941 mMaxContentWidth = max_item_width;
00942 }
00943
00944 const S32 SCROLL_LIST_ROW_PAD = 2;
00945
00946
00947 void LLScrollListCtrl::updateLineHeight()
00948 {
00949 mLineHeight = 0;
00950 item_list::iterator iter;
00951 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
00952 {
00953 LLScrollListItem *itemp = *iter;
00954 S32 num_cols = itemp->getNumColumns();
00955 S32 i = 0;
00956 for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
00957 {
00958 mLineHeight = llmax( mLineHeight, cell->getHeight() + SCROLL_LIST_ROW_PAD );
00959 }
00960 }
00961 }
00962
00963
00964 void LLScrollListCtrl::updateLineHeightInsert(LLScrollListItem* itemp)
00965 {
00966 S32 num_cols = itemp->getNumColumns();
00967 S32 i = 0;
00968 for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
00969 {
00970 mLineHeight = llmax( mLineHeight, cell->getHeight() + SCROLL_LIST_ROW_PAD );
00971 }
00972 }
00973
00974
00975 void LLScrollListCtrl::updateColumns()
00976 {
00977 calcColumnWidths();
00978
00979
00980 item_list::iterator iter;
00981 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
00982 {
00983 LLScrollListItem *itemp = *iter;
00984 S32 num_cols = itemp->getNumColumns();
00985 S32 i = 0;
00986 for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
00987 {
00988 if (i >= (S32)mColumnsIndexed.size()) break;
00989
00990 cell->setWidth(mColumnsIndexed[i]->mWidth);
00991 }
00992 }
00993
00994
00995 std::vector<LLScrollListColumn*>::iterator column_ordered_it;
00996 S32 left = mItemListRect.mLeft;
00997 LLColumnHeader* last_header = NULL;
00998 for (column_ordered_it = mColumnsIndexed.begin(); column_ordered_it != mColumnsIndexed.end(); ++column_ordered_it)
00999 {
01000 if ((*column_ordered_it)->mWidth < 0)
01001 {
01002
01003 continue;
01004 }
01005 LLScrollListColumn* column = *column_ordered_it;
01006
01007 if (column->mHeader)
01008 {
01009 last_header = column->mHeader;
01010 S32 top = mItemListRect.mTop;
01011 S32 right = left + column->mWidth;
01012
01013 if (column->mIndex != (S32)mColumnsIndexed.size()-1)
01014 {
01015 right += mColumnPadding;
01016 }
01017 right = llmax(left, llmin(mItemListRect.getWidth(), right));
01018 S32 header_width = right - left;
01019
01020 last_header->reshape(header_width, mHeadingHeight);
01021 last_header->translate(
01022 left - last_header->getRect().mLeft,
01023 top - last_header->getRect().mBottom);
01024 last_header->setVisible(mDisplayColumnHeaders && header_width > 0);
01025 left = right;
01026 }
01027 }
01028
01029
01030
01031 if (last_header)
01032 {
01033 S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft);
01034 last_header->reshape(new_width, last_header->getRect().getHeight());
01035 last_header->setVisible(mDisplayColumnHeaders && new_width > 0);
01036 }
01037 }
01038
01039 void LLScrollListCtrl::setDisplayHeading(BOOL display)
01040 {
01041 mDisplayColumnHeaders = display;
01042
01043 updateLayout();
01044 }
01045
01046 void LLScrollListCtrl::setHeadingHeight(S32 heading_height)
01047 {
01048 mHeadingHeight = heading_height;
01049
01050 updateLayout();
01051
01052 }
01053
01054 BOOL LLScrollListCtrl::selectFirstItem()
01055 {
01056 BOOL success = FALSE;
01057
01058
01059 BOOL first_item = TRUE;
01060
01061 item_list::iterator iter;
01062 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01063 {
01064 LLScrollListItem *itemp = *iter;
01065 if( first_item && itemp->getEnabled() )
01066 {
01067 if (!itemp->getSelected())
01068 {
01069 selectItem(itemp);
01070 }
01071 success = TRUE;
01072 mOriginalSelection = 0;
01073 }
01074 else
01075 {
01076 deselectItem(itemp);
01077 }
01078 first_item = FALSE;
01079 }
01080 if (mCommitOnSelectionChange)
01081 {
01082 commitIfChanged();
01083 }
01084 return success;
01085 }
01086
01087
01088
01089 BOOL LLScrollListCtrl::selectNthItem( S32 target_index )
01090 {
01091 return selectItemRange(target_index, target_index);
01092 }
01093
01094
01095 BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index )
01096 {
01097 if (mItemList.empty())
01098 {
01099 return FALSE;
01100 }
01101
01102 S32 listlen = (S32)mItemList.size();
01103 first_index = llclamp(first_index, 0, listlen-1);
01104
01105 if (last_index < 0)
01106 last_index = listlen-1;
01107 else
01108 last_index = llclamp(last_index, first_index, listlen-1);
01109
01110 BOOL success = FALSE;
01111 S32 index = 0;
01112 for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
01113 {
01114 LLScrollListItem *itemp = *iter;
01115 if( index >= first_index && index <= last_index )
01116 {
01117 if( itemp->getEnabled() )
01118 {
01119 selectItem(itemp, FALSE);
01120 success = TRUE;
01121 }
01122 }
01123 else
01124 {
01125 deselectItem(itemp);
01126 }
01127 index++;
01128 }
01129
01130 if (mCommitOnSelectionChange)
01131 {
01132 commitIfChanged();
01133 }
01134
01135 mSearchString.clear();
01136
01137 return success;
01138 }
01139
01140
01141 void LLScrollListCtrl::swapWithNext(S32 index)
01142 {
01143 if (index >= ((S32)mItemList.size() - 1))
01144 {
01145
01146 return;
01147 }
01148 LLScrollListItem *cur_itemp = mItemList[index];
01149 mItemList[index] = mItemList[index + 1];
01150 mItemList[index + 1] = cur_itemp;
01151 }
01152
01153
01154 void LLScrollListCtrl::swapWithPrevious(S32 index)
01155 {
01156 if (index <= 0)
01157 {
01158
01159 }
01160
01161 LLScrollListItem *cur_itemp = mItemList[index];
01162 mItemList[index] = mItemList[index - 1];
01163 mItemList[index - 1] = cur_itemp;
01164 }
01165
01166
01167 void LLScrollListCtrl::deleteSingleItem(S32 target_index)
01168 {
01169 if (target_index < 0 || target_index >= (S32)mItemList.size())
01170 {
01171 return;
01172 }
01173
01174 LLScrollListItem *itemp;
01175 itemp = mItemList[target_index];
01176 if (itemp == mLastSelected)
01177 {
01178 mLastSelected = NULL;
01179 }
01180 delete itemp;
01181 mItemList.erase(mItemList.begin() + target_index);
01182 dirtyColumns();
01183 }
01184
01185
01186 void LLScrollListCtrl::deleteItems(const LLSD& sd)
01187 {
01188 item_list::iterator iter;
01189 for (iter = mItemList.begin(); iter < mItemList.end(); )
01190 {
01191 LLScrollListItem* itemp = *iter;
01192 if (itemp->getValue().asString() == sd.asString())
01193 {
01194 if (itemp == mLastSelected)
01195 {
01196 mLastSelected = NULL;
01197 }
01198 delete itemp;
01199 iter = mItemList.erase(iter);
01200 }
01201 else
01202 {
01203 iter++;
01204 }
01205 }
01206
01207 dirtyColumns();
01208 }
01209
01210 void LLScrollListCtrl::deleteSelectedItems()
01211 {
01212 item_list::iterator iter;
01213 for (iter = mItemList.begin(); iter < mItemList.end(); )
01214 {
01215 LLScrollListItem* itemp = *iter;
01216 if (itemp->getSelected())
01217 {
01218 delete itemp;
01219 iter = mItemList.erase(iter);
01220 }
01221 else
01222 {
01223 iter++;
01224 }
01225 }
01226 mLastSelected = NULL;
01227 dirtyColumns();
01228 }
01229
01230 void LLScrollListCtrl::highlightNthItem(S32 target_index)
01231 {
01232 if (mHighlightedItem != target_index)
01233 {
01234 mHighlightedItem = target_index;
01235 }
01236 }
01237
01238 S32 LLScrollListCtrl::selectMultiple( LLDynamicArray<LLUUID> ids )
01239 {
01240 item_list::iterator iter;
01241 S32 count = 0;
01242 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01243 {
01244 LLScrollListItem* item = *iter;
01245 LLDynamicArray<LLUUID>::iterator iditr;
01246 for(iditr = ids.begin(); iditr != ids.end(); ++iditr)
01247 {
01248 if (item->getEnabled() && (item->getUUID() == (*iditr)))
01249 {
01250 selectItem(item,FALSE);
01251 ++count;
01252 break;
01253 }
01254 }
01255 if(ids.end() != iditr) ids.erase(iditr);
01256 }
01257
01258 if (mCommitOnSelectionChange)
01259 {
01260 commitIfChanged();
01261 }
01262 return count;
01263 }
01264
01265 S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const
01266 {
01267 S32 index = 0;
01268 item_list::const_iterator iter;
01269 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01270 {
01271 LLScrollListItem *itemp = *iter;
01272 if (target_item == itemp)
01273 {
01274 return index;
01275 }
01276 index++;
01277 }
01278 return -1;
01279 }
01280
01281 S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const
01282 {
01283 S32 index = 0;
01284 item_list::const_iterator iter;
01285 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01286 {
01287 LLScrollListItem *itemp = *iter;
01288 if (target_id == itemp->getUUID())
01289 {
01290 return index;
01291 }
01292 index++;
01293 }
01294 return -1;
01295 }
01296
01297 void LLScrollListCtrl::selectPrevItem( BOOL extend_selection)
01298 {
01299 LLScrollListItem* prev_item = NULL;
01300
01301 if (!getFirstSelected())
01302 {
01303
01304 selectNthItem(getItemCount() - 1);
01305 }
01306 else
01307 {
01308 item_list::iterator iter;
01309 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01310 {
01311 LLScrollListItem* cur_item = *iter;
01312
01313 if (cur_item->getSelected())
01314 {
01315 if (prev_item)
01316 {
01317 selectItem(prev_item, !extend_selection);
01318 }
01319 else
01320 {
01321 reportInvalidInput();
01322 }
01323 break;
01324 }
01325
01326
01327 prev_item = cur_item->getEnabled() ? cur_item : prev_item;
01328 }
01329 }
01330
01331 if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement))
01332 {
01333 commitIfChanged();
01334 }
01335
01336 mSearchString.clear();
01337 }
01338
01339
01340 void LLScrollListCtrl::selectNextItem( BOOL extend_selection)
01341 {
01342 LLScrollListItem* next_item = NULL;
01343
01344 if (!getFirstSelected())
01345 {
01346 selectFirstItem();
01347 }
01348 else
01349 {
01350 item_list::reverse_iterator iter;
01351 for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++)
01352 {
01353 LLScrollListItem* cur_item = *iter;
01354
01355 if (cur_item->getSelected())
01356 {
01357 if (next_item)
01358 {
01359 selectItem(next_item, !extend_selection);
01360 }
01361 else
01362 {
01363 reportInvalidInput();
01364 }
01365 break;
01366 }
01367
01368
01369 next_item = cur_item->getEnabled() ? cur_item : next_item;
01370 }
01371 }
01372
01373 if (mCommitOnKeyboardMovement)
01374 {
01375 onCommit();
01376 }
01377
01378 mSearchString.clear();
01379 }
01380
01381
01382
01383 void LLScrollListCtrl::deselectAllItems(BOOL no_commit_on_change)
01384 {
01385 item_list::iterator iter;
01386 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01387 {
01388 LLScrollListItem* item = *iter;
01389 deselectItem(item);
01390 }
01391
01392 if (mCommitOnSelectionChange && !no_commit_on_change)
01393 {
01394 commitIfChanged();
01395 }
01396 }
01397
01399
01400
01401 LLScrollListItem* LLScrollListCtrl::addCommentText(const LLString& comment_text, EAddPosition pos)
01402 {
01403 LLScrollListItem* item = NULL;
01404 if (getItemCount() < mMaxItemCount)
01405 {
01406
01407 item = new LLScrollListItemComment( comment_text, mFgUnselectedColor );
01408 addItem( item, pos, FALSE );
01409 }
01410 return item;
01411 }
01412
01413 LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos)
01414 {
01415 LLScrollListItem* item = new LLScrollListItemSeparator();
01416 addItem(item, pos, FALSE);
01417 return item;
01418 }
01419
01420
01421
01422 BOOL LLScrollListCtrl::selectItemByLabel(const LLString& label, BOOL case_sensitive)
01423 {
01424
01425 deselectAllItems(TRUE);
01426
01427 if (label.empty())
01428 {
01429 return FALSE;
01430 }
01431
01432 LLString target_text = label;
01433 if (!case_sensitive)
01434 {
01435 LLString::toLower(target_text);
01436 }
01437
01438 BOOL found = FALSE;
01439
01440 item_list::iterator iter;
01441 S32 index = 0;
01442 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01443 {
01444 LLScrollListItem* item = *iter;
01445
01446 LLString item_text = item->getColumn(0)->getValue().asString();
01447 if (!case_sensitive)
01448 {
01449 LLString::toLower(item_text);
01450 }
01451 BOOL select = !found && item->getEnabled() && item_text == target_text;
01452 if (select)
01453 {
01454 selectItem(item);
01455 }
01456 found = found || select;
01457 index++;
01458 }
01459
01460 if (mCommitOnSelectionChange)
01461 {
01462 commitIfChanged();
01463 }
01464
01465 return found;
01466 }
01467
01468
01469 BOOL LLScrollListCtrl::selectItemByPrefix(const LLString& target, BOOL case_sensitive)
01470 {
01471 return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive);
01472 }
01473
01474
01475
01476 BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sensitive)
01477 {
01478 BOOL found = FALSE;
01479
01480 LLWString target_trimmed( target );
01481 S32 target_len = target_trimmed.size();
01482
01483 if( 0 == target_len )
01484 {
01485
01486 item_list::iterator iter;
01487 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01488 {
01489 LLScrollListItem* item = *iter;
01490
01491 LLScrollListCell* cellp = item->getColumn(mSearchColumn);
01492 BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE;
01493 if (select)
01494 {
01495 selectItem(item);
01496 found = TRUE;
01497 break;
01498 }
01499 }
01500 }
01501 else
01502 {
01503 if (!case_sensitive)
01504 {
01505
01506 LLWString::toLower(target_trimmed);
01507 }
01508
01509 for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
01510 {
01511 LLScrollListItem* item = *iter;
01512
01513
01514 LLScrollListCell* cellp = item->getColumn(mSearchColumn);
01515 if (!cellp)
01516 {
01517 continue;
01518 }
01519 LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
01520 if (!case_sensitive)
01521 {
01522 LLWString::toLower(item_label);
01523 }
01524
01525 LLWString trimmed_label = item_label;
01526 LLWString::trim(trimmed_label);
01527
01528 BOOL select = item->getEnabled() && trimmed_label.compare(0, target_trimmed.size(), target_trimmed) == 0;
01529
01530 if (select)
01531 {
01532
01533 S32 offset = item_label.find(target_trimmed);
01534 cellp->highlightText(offset, target_trimmed.size());
01535 selectItem(item);
01536 found = TRUE;
01537 break;
01538 }
01539 }
01540 }
01541
01542 if (mCommitOnSelectionChange)
01543 {
01544 commitIfChanged();
01545 }
01546
01547 return found;
01548 }
01549
01550 const LLString LLScrollListCtrl::getSelectedItemLabel(S32 column) const
01551 {
01552 LLScrollListItem* item;
01553
01554 item = getFirstSelected();
01555 if (item)
01556 {
01557 return item->getColumn(column)->getValue().asString();
01558 }
01559
01560 return LLString::null;
01561 }
01562
01564
01565
01566
01567 LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const LLString& item_text, const LLUUID& id, EAddPosition pos, BOOL enabled, S32 column_width)
01568 {
01569 LLScrollListItem* item = NULL;
01570 if (getItemCount() < mMaxItemCount)
01571 {
01572 item = new LLScrollListItem( enabled, NULL, id );
01573 item->addColumn(item_text, LLResMgr::getInstance()->getRes(LLFONT_SANSSERIF_SMALL), column_width);
01574 addItem( item, pos );
01575 }
01576 return item;
01577 }
01578
01579
01580 BOOL LLScrollListCtrl::selectByID( const LLUUID& id )
01581 {
01582 return selectByValue( LLSD(id) );
01583 }
01584
01585 BOOL LLScrollListCtrl::setSelectedByValue(const LLSD& value, BOOL selected)
01586 {
01587 BOOL found = FALSE;
01588
01589 if (selected && !mAllowMultipleSelection) deselectAllItems(TRUE);
01590
01591 item_list::iterator iter;
01592 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01593 {
01594 LLScrollListItem* item = *iter;
01595 if (item->getEnabled() && (item->getValue().asString() == value.asString()))
01596 {
01597 if (selected)
01598 {
01599 selectItem(item);
01600 }
01601 else
01602 {
01603 deselectItem(item);
01604 }
01605 found = TRUE;
01606 break;
01607 }
01608 }
01609
01610 if (mCommitOnSelectionChange)
01611 {
01612 commitIfChanged();
01613 }
01614
01615 return found;
01616 }
01617
01618 BOOL LLScrollListCtrl::isSelected(const LLSD& value) const
01619 {
01620 item_list::const_iterator iter;
01621 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01622 {
01623 LLScrollListItem* item = *iter;
01624 if (item->getValue().asString() == value.asString())
01625 {
01626 return item->getSelected();
01627 }
01628 }
01629 return FALSE;
01630 }
01631
01632 LLUUID LLScrollListCtrl::getStringUUIDSelectedItem() const
01633 {
01634 LLScrollListItem* item = getFirstSelected();
01635
01636 if (item)
01637 {
01638 return item->getUUID();
01639 }
01640
01641 return LLUUID::null;
01642 }
01643
01644 LLSD LLScrollListCtrl::getSelectedValue()
01645 {
01646 LLScrollListItem* item = getFirstSelected();
01647
01648 if (item)
01649 {
01650 return item->getValue();
01651 }
01652 else
01653 {
01654 return LLSD();
01655 }
01656 }
01657
01658 void LLScrollListCtrl::drawItems()
01659 {
01660 S32 x = mItemListRect.mLeft;
01661 S32 y = mItemListRect.mTop - mLineHeight;
01662
01663
01664 S32 num_page_lines = mPageLines + 1;
01665
01666 LLRect item_rect;
01667
01668 LLGLSUIDefault gls_ui;
01669
01670 {
01671 LLLocalClipRect clip(mItemListRect);
01672
01673 S32 cur_y = y;
01674
01675 mDrewSelected = FALSE;
01676
01677 S32 line = 0;
01678 S32 max_columns = 0;
01679
01680 LLColor4 highlight_color = LLColor4::white;
01681 F32 type_ahead_timeout = LLUI::sConfigGroup->getF32("TypeAheadTimeout");
01682 highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f);
01683
01684 item_list::iterator iter;
01685 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
01686 {
01687 LLScrollListItem* item = *iter;
01688
01689 item_rect.setOriginAndSize(
01690 x,
01691 cur_y,
01692 mItemListRect.getWidth(),
01693 mLineHeight );
01694
01695
01696
01697 if (item->getSelected())
01698 {
01699 mDrewSelected = TRUE;
01700 }
01701
01702 max_columns = llmax(max_columns, item->getNumColumns());
01703
01704 LLColor4 fg_color;
01705 LLColor4 bg_color(LLColor4::transparent);
01706
01707 if( mScrollLines <= line && line < mScrollLines + num_page_lines )
01708 {
01709 fg_color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor);
01710 if( item->getSelected() && mCanSelect)
01711 {
01712 bg_color = mBgSelectedColor;
01713 fg_color = (item->getEnabled() ? mFgSelectedColor : mFgDisabledColor);
01714 }
01715 else if (mHighlightedItem == line && mCanSelect)
01716 {
01717 bg_color = mHighlightedColor;
01718 }
01719 else
01720 {
01721 if (mDrawStripes && (line % 2 == 0) && (max_columns > 1))
01722 {
01723 bg_color = mBgStripeColor;
01724 }
01725 }
01726
01727 if (!item->getEnabled())
01728 {
01729 bg_color = mBgReadOnlyColor;
01730 }
01731
01732 item->draw(item_rect, fg_color, bg_color, highlight_color, mColumnPadding);
01733
01734 cur_y -= mLineHeight;
01735 }
01736 line++;
01737 }
01738 }
01739 }
01740
01741
01742 void LLScrollListCtrl::draw()
01743 {
01744
01745 if (needsSorting() && !isSorted())
01746 {
01747 sortItems();
01748 }
01749
01750 if (mNeedsScroll)
01751 {
01752 scrollToShowSelected();
01753 mNeedsScroll = FALSE;
01754 }
01755 LLRect background(0, getRect().getHeight(), getRect().getWidth(), 0);
01756
01757 if (mBackgroundVisible)
01758 {
01759 LLGLSNoTexture no_texture;
01760 gGL.color4fv( getEnabled() ? mBgWriteableColor.mV : mBgReadOnlyColor.mV );
01761 gl_rect_2d(background);
01762 }
01763
01764 if (mColumnsDirty)
01765 {
01766 updateColumns();
01767 mColumnsDirty = FALSE;
01768 }
01769
01770 drawItems();
01771
01772 if (mBorder)
01773 {
01774 mBorder->setKeyboardFocusHighlight(gFocusMgr.getKeyboardFocus() == this);
01775 }
01776
01777 LLUICtrl::draw();
01778 }
01779
01780 void LLScrollListCtrl::setEnabled(BOOL enabled)
01781 {
01782 mCanSelect = enabled;
01783 setTabStop(enabled);
01784 mScrollbar->setTabStop(!enabled && mScrollbar->getPageSize() < mScrollbar->getDocSize());
01785 }
01786
01787 BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
01788 {
01789 BOOL handled = FALSE;
01790
01791 handled = mScrollbar->handleScrollWheel( 0, 0, clicks );
01792 return handled;
01793 }
01794
01795 BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
01796 {
01797 S32 column_index = getColumnIndexFromOffset(x);
01798 LLScrollListColumn* columnp = getColumn(column_index);
01799
01800 if (columnp == NULL) return FALSE;
01801
01802 BOOL handled = FALSE;
01803
01804 LLScrollListItem* hit_item = hitItem(x, y);
01805 if (hit_item)
01806 {
01807 LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
01808 if (!hit_cell) return FALSE;
01809
01810 if (hit_cell
01811 && hit_cell->isText())
01812 {
01813
01814 S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft;
01815 S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item));
01816 LLRect cell_rect;
01817 cell_rect.setOriginAndSize(rect_left, rect_bottom, rect_left + columnp->mWidth, mLineHeight);
01818
01819 localPointToScreen(
01820 cell_rect.mLeft, cell_rect.mBottom,
01821 &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
01822 localPointToScreen(
01823 cell_rect.mRight, cell_rect.mTop,
01824 &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
01825
01826 msg = hit_cell->getValue().asString();
01827 }
01828 handled = TRUE;
01829 }
01830
01831
01832 LLColumnHeader* headerp = columnp->mHeader;
01833 if (headerp && !handled)
01834 {
01835 headerp->handleToolTip(x, y, msg, sticky_rect_screen);
01836 handled = !msg.empty();
01837 }
01838
01839 return handled;
01840 }
01841
01842 BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask)
01843 {
01844 if (!mCanSelect) return FALSE;
01845
01846 BOOL selection_changed = FALSE;
01847
01848 LLScrollListItem* hit_item = hitItem(x, y);
01849
01850 if( hit_item )
01851 {
01852 if( mAllowMultipleSelection )
01853 {
01854 if (mask & MASK_SHIFT)
01855 {
01856 if (mLastSelected == NULL)
01857 {
01858 selectItem(hit_item);
01859 }
01860 else
01861 {
01862
01863 bool selecting = false;
01864 item_list::iterator itor;
01865
01866
01867
01868 LLScrollListItem* lastSelected = mLastSelected;
01869 for (itor = mItemList.begin(); itor != mItemList.end(); ++itor)
01870 {
01871 if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)
01872 {
01873 if(mOnMaximumSelectCallback)
01874 {
01875 mOnMaximumSelectCallback(mCallbackUserData);
01876 }
01877 break;
01878 }
01879 LLScrollListItem *item = *itor;
01880 if (item == hit_item || item == lastSelected)
01881 {
01882 selectItem(item, FALSE);
01883 selecting = !selecting;
01884 if (hit_item == lastSelected)
01885 {
01886
01887 selecting = FALSE;
01888 }
01889 }
01890 if (selecting)
01891 {
01892 selectItem(item, FALSE);
01893 }
01894 }
01895 }
01896 }
01897 else if (mask & MASK_CONTROL)
01898 {
01899 if (hit_item->getSelected())
01900 {
01901 deselectItem(hit_item);
01902 }
01903 else
01904 {
01905 if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable))
01906 {
01907 selectItem(hit_item, FALSE);
01908 }
01909 else
01910 {
01911 if(mOnMaximumSelectCallback)
01912 {
01913 mOnMaximumSelectCallback(mCallbackUserData);
01914 }
01915 }
01916 }
01917 }
01918 else
01919 {
01920 deselectAllItems(TRUE);
01921 selectItem(hit_item);
01922 }
01923 }
01924 else
01925 {
01926 selectItem(hit_item);
01927 }
01928
01929 selection_changed = mSelectionChanged;
01930 if (mCommitOnSelectionChange)
01931 {
01932 commitIfChanged();
01933 }
01934
01935
01936 mSearchString.clear();
01937 }
01938 else
01939 {
01940
01941
01942 }
01943
01944 return selection_changed;
01945 }
01946
01947
01948 BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
01949 {
01950 BOOL handled = childrenHandleMouseDown(x, y, mask) != NULL;
01951
01952 if( !handled )
01953 {
01954
01955 setFocus(TRUE);
01956
01957
01958 mSelectionChanged = FALSE;
01959
01960 handleClick(x, y, mask);
01961 }
01962
01963 return TRUE;
01964 }
01965
01966 BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
01967 {
01968 if (hasMouseCapture())
01969 {
01970
01971
01972 gFocusMgr.setMouseCapture(NULL);
01973 if(mask == MASK_NONE)
01974 {
01975 selectItemAt(x, y, mask);
01976 mNeedsScroll = TRUE;
01977 }
01978 }
01979
01980
01981 if (mItemListRect.pointInRect(x,y))
01982 {
01983 mDirty |= mSelectionChanged;
01984 mSelectionChanged = FALSE;
01985 onCommit();
01986 }
01987
01988 return LLUICtrl::handleMouseUp(x, y, mask);
01989 }
01990
01991 BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
01992 {
01993
01994 BOOL handled = handleClick(x, y, mask);
01995
01996 if (!handled)
01997 {
01998
01999
02000 if (NULL == LLView::childrenHandleDoubleClick(x, y, mask))
02001 {
02002 if( mCanSelect && mOnDoubleClickCallback )
02003 {
02004 mOnDoubleClickCallback( mCallbackUserData );
02005 }
02006 }
02007 }
02008
02009 return TRUE;
02010 }
02011
02012 BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask)
02013 {
02014
02015 LLScrollListItem* hit_item = hitItem(x, y);
02016 if (!hit_item) return FALSE;
02017
02018
02019 S32 column_index = getColumnIndexFromOffset(x);
02020 LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
02021 if (!hit_cell) return FALSE;
02022
02023
02024 if (hit_cell->handleClick())
02025 {
02026
02027 if (!hit_item->getSelected())
02028 {
02029 selectItemAt(x, y, mask);
02030 gFocusMgr.setMouseCapture(this);
02031 mNeedsScroll = TRUE;
02032 }
02033
02034
02035 {
02036
02037
02038 LLSD item_value = hit_cell->getValue();
02039 for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
02040 {
02041 LLScrollListItem* item = *iter;
02042 if (item->getSelected())
02043 {
02044 LLScrollListCell* cellp = item->getColumn(column_index);
02045 cellp->setValue(item_value);
02046 cellp->onCommit();
02047 }
02048 }
02049
02050 onCommit();
02051 }
02052
02053 return TRUE;
02054 }
02055 else
02056 {
02057
02058 selectItemAt(x, y, mask);
02059 gFocusMgr.setMouseCapture(this);
02060 mNeedsScroll = TRUE;
02061
02062 return FALSE;
02063 }
02064 }
02065
02066 LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
02067 {
02068
02069 LLScrollListItem* hit_item = NULL;
02070
02071 LLRect item_rect;
02072 item_rect.setLeftTopAndSize(
02073 mItemListRect.mLeft,
02074 mItemListRect.mTop,
02075 mItemListRect.getWidth(),
02076 mLineHeight );
02077
02078
02079 S32 num_page_lines = mPageLines + 1;
02080
02081 S32 line = 0;
02082 item_list::iterator iter;
02083 for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
02084 {
02085 LLScrollListItem* item = *iter;
02086 if( mScrollLines <= line && line < mScrollLines + num_page_lines )
02087 {
02088 if( item->getEnabled() && item_rect.pointInRect( x, y ) )
02089 {
02090 hit_item = item;
02091 break;
02092 }
02093
02094 item_rect.translate(0, -mLineHeight);
02095 }
02096 line++;
02097 }
02098
02099 return hit_item;
02100 }
02101
02102 S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x)
02103 {
02104
02105 S32 left = 0;
02106 S32 right = 0;
02107 S32 width = 0;
02108 S32 column_index = 0;
02109
02110 ordered_columns_t::const_iterator iter = mColumnsIndexed.begin();
02111 ordered_columns_t::const_iterator end = mColumnsIndexed.end();
02112 for ( ; iter != end; ++iter)
02113 {
02114 width = (*iter)->mWidth + mColumnPadding;
02115 right += width;
02116 if (left <= x && x < right )
02117 {
02118 break;
02119 }
02120
02121
02122 left = right;
02123 column_index++;
02124 }
02125
02126 return llclamp(column_index, 0, getNumColumns() - 1);
02127 }
02128
02129
02130 S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index)
02131 {
02132 S32 column_offset = 0;
02133 ordered_columns_t::const_iterator iter = mColumnsIndexed.begin();
02134 ordered_columns_t::const_iterator end = mColumnsIndexed.end();
02135 for ( ; iter != end; ++iter)
02136 {
02137 if (index-- <= 0)
02138 {
02139 return column_offset;
02140 }
02141 column_offset += (*iter)->mWidth + mColumnPadding;
02142 }
02143
02144
02145 return mItemListRect.mRight;
02146 }
02147
02148 S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index)
02149 {
02150 S32 row_bottom = ((mItemListRect.mTop - (index - mScrollLines)) * mLineHeight)
02151 - mLineHeight;
02152 return row_bottom;
02153 }
02154
02155
02156 BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask)
02157 {
02158 BOOL handled = FALSE;
02159
02160 if (hasMouseCapture())
02161 {
02162 if(mask == MASK_NONE)
02163 {
02164 selectItemAt(x, y, mask);
02165 mNeedsScroll = TRUE;
02166 }
02167 }
02168 else
02169 if (mCanSelect)
02170 {
02171 LLScrollListItem* item = hitItem(x, y);
02172 if (item)
02173 {
02174 highlightNthItem(getItemIndex(item));
02175 }
02176 else
02177 {
02178 highlightNthItem(-1);
02179 }
02180 }
02181
02182 handled = LLUICtrl::handleHover( x, y, mask );
02183
02184 return handled;
02185 }
02186
02187
02188 BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
02189 {
02190 BOOL handled = FALSE;
02191
02192
02193 if (mCanSelect)
02194 {
02195
02196 mask = mask;
02197
02198 if (mask == MASK_NONE)
02199 {
02200 switch(key)
02201 {
02202 case KEY_UP:
02203 if (mAllowKeyboardMovement || hasFocus())
02204 {
02205
02206 selectPrevItem(FALSE);
02207 mNeedsScroll = TRUE;
02208 handled = TRUE;
02209 }
02210 break;
02211 case KEY_DOWN:
02212 if (mAllowKeyboardMovement || hasFocus())
02213 {
02214
02215 selectNextItem(FALSE);
02216 mNeedsScroll = TRUE;
02217 handled = TRUE;
02218 }
02219 break;
02220 case KEY_PAGE_UP:
02221 if (mAllowKeyboardMovement || hasFocus())
02222 {
02223 selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1));
02224 mNeedsScroll = TRUE;
02225 if (mCommitOnKeyboardMovement
02226 && !mCommitOnSelectionChange)
02227 {
02228 onCommit();
02229 }
02230 handled = TRUE;
02231 }
02232 break;
02233 case KEY_PAGE_DOWN:
02234 if (mAllowKeyboardMovement || hasFocus())
02235 {
02236 selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1));
02237 mNeedsScroll = TRUE;
02238 if (mCommitOnKeyboardMovement
02239 && !mCommitOnSelectionChange)
02240 {
02241 onCommit();
02242 }
02243 handled = TRUE;
02244 }
02245 break;
02246 case KEY_HOME:
02247 if (mAllowKeyboardMovement || hasFocus())
02248 {
02249 selectFirstItem();
02250 mNeedsScroll = TRUE;
02251 if (mCommitOnKeyboardMovement
02252 && !mCommitOnSelectionChange)
02253 {
02254 onCommit();
02255 }
02256 handled = TRUE;
02257 }
02258 break;
02259 case KEY_END:
02260 if (mAllowKeyboardMovement || hasFocus())
02261 {
02262 selectNthItem(getItemCount() - 1);
02263 mNeedsScroll = TRUE;
02264 if (mCommitOnKeyboardMovement
02265 && !mCommitOnSelectionChange)
02266 {
02267 onCommit();
02268 }
02269 handled = TRUE;
02270 }
02271 break;
02272 case KEY_RETURN:
02273
02274
02275
02276 if (!mCommitOnKeyboardMovement && mask == MASK_NONE)
02277 {
02278 onCommit();
02279 mSearchString.clear();
02280 handled = TRUE;
02281 }
02282 break;
02283 case KEY_BACKSPACE:
02284 mSearchTimer.reset();
02285 if (mSearchString.size())
02286 {
02287 mSearchString.erase(mSearchString.size() - 1, 1);
02288 }
02289 if (mSearchString.empty())
02290 {
02291 if (getFirstSelected())
02292 {
02293 LLScrollListCell* cellp = getFirstSelected()->getColumn(mSearchColumn);
02294 if (cellp)
02295 {
02296 cellp->highlightText(0, 0);
02297 }
02298 }
02299 }
02300 else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), FALSE))
02301 {
02302 mNeedsScroll = TRUE;
02303
02304 mSearchTimer.reset();
02305
02306 if (mCommitOnKeyboardMovement
02307 && !mCommitOnSelectionChange)
02308 {
02309 onCommit();
02310 }
02311 }
02312 break;
02313 default:
02314 break;
02315 }
02316 }
02317
02318 }
02319
02320 return handled;
02321 }
02322
02323 BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char)
02324 {
02325 if ((uni_char < 0x20) || (uni_char == 0x7F))
02326 {
02327 return FALSE;
02328 }
02329
02330
02331 if (mSearchTimer.getElapsedTimeF32() > LLUI::sConfigGroup->getF32("TypeAheadTimeout"))
02332 {
02333 mSearchString.clear();
02334 }
02335
02336
02337 uni_char = LLStringOps::toLower((llwchar)uni_char);
02338
02339 if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE))
02340 {
02341
02342 mNeedsScroll = TRUE;
02343 mSearchString += uni_char;
02344 mSearchTimer.reset();
02345
02346 if (mCommitOnKeyboardMovement
02347 && !mCommitOnSelectionChange)
02348 {
02349 onCommit();
02350 }
02351 }
02352
02353 else if (isRepeatedChars(mSearchString + (llwchar)uni_char) && !mItemList.empty())
02354 {
02355
02356
02357 item_list::iterator start_iter = mItemList.begin();
02358 S32 first_selected = getFirstSelectedIndex();
02359
02360
02361 if (first_selected > 0)
02362 {
02363
02364 start_iter += first_selected;
02365 }
02366
02367
02368 item_list::iterator iter = start_iter;
02369 ++iter;
02370 if (iter == mItemList.end())
02371 {
02372 iter = mItemList.begin();
02373 }
02374
02375
02376 while(iter != start_iter)
02377 {
02378 LLScrollListItem* item = *iter;
02379
02380 LLScrollListCell* cellp = item->getColumn(mSearchColumn);
02381 if (cellp)
02382 {
02383
02384 LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
02385 if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char)
02386 {
02387 selectItem(item);
02388 mNeedsScroll = TRUE;
02389 cellp->highlightText(0, 1);
02390 mSearchTimer.reset();
02391
02392 if (mCommitOnKeyboardMovement
02393 && !mCommitOnSelectionChange)
02394 {
02395 onCommit();
02396 }
02397
02398 break;
02399 }
02400 }
02401
02402 ++iter;
02403 if (iter == mItemList.end())
02404 {
02405 iter = mItemList.begin();
02406 }
02407 }
02408 }
02409
02410 return TRUE;
02411 }
02412
02413
02414 void LLScrollListCtrl::reportInvalidInput()
02415 {
02416 make_ui_sound("UISndBadKeystroke");
02417 }
02418
02419 BOOL LLScrollListCtrl::isRepeatedChars(const LLWString& string) const
02420 {
02421 if (string.empty())
02422 {
02423 return FALSE;
02424 }
02425
02426 llwchar first_char = string[0];
02427
02428 for (U32 i = 0; i < string.size(); i++)
02429 {
02430 if (string[i] != first_char)
02431 {
02432 return FALSE;
02433 }
02434 }
02435
02436 return TRUE;
02437 }
02438
02439 void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_item)
02440 {
02441 if (!itemp) return;
02442
02443 if (!itemp->getSelected())
02444 {
02445 if (mLastSelected)
02446 {
02447 LLScrollListCell* cellp = mLastSelected->getColumn(mSearchColumn);
02448 if (cellp)
02449 {
02450 cellp->highlightText(0, 0);
02451 }
02452 }
02453 if (select_single_item)
02454 {
02455 deselectAllItems(TRUE);
02456 }
02457 itemp->setSelected(TRUE);
02458 mLastSelected = itemp;
02459 mSelectionChanged = TRUE;
02460 }
02461 }
02462
02463 void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp)
02464 {
02465 if (!itemp) return;
02466
02467 if (itemp->getSelected())
02468 {
02469 if (mLastSelected == itemp)
02470 {
02471 mLastSelected = NULL;
02472 }
02473
02474 itemp->setSelected(FALSE);
02475 LLScrollListCell* cellp = itemp->getColumn(mSearchColumn);
02476 if (cellp)
02477 {
02478 cellp->highlightText(0, 0);
02479 }
02480 mSelectionChanged = TRUE;
02481 }
02482 }
02483
02484 void LLScrollListCtrl::commitIfChanged()
02485 {
02486 if (mSelectionChanged)
02487 {
02488 mDirty = TRUE;
02489 mSelectionChanged = FALSE;
02490 onCommit();
02491 }
02492 }
02493
02494 struct SameSortColumn
02495 {
02496 SameSortColumn(S32 column) : mColumn(column) {}
02497 S32 mColumn;
02498
02499 bool operator()(std::pair<S32, BOOL> sort_column) { return sort_column.first == mColumn; }
02500 };
02501
02502 BOOL LLScrollListCtrl::setSort(S32 column, BOOL ascending)
02503 {
02504 sort_column_t new_sort_column(column, ascending);
02505
02506 if (mSortColumns.empty())
02507 {
02508 mSortColumns.push_back(new_sort_column);
02509 return TRUE;
02510 }
02511 else
02512 {
02513
02514 sort_column_t cur_sort_column = mSortColumns.back();
02515
02516
02517
02518 mSortColumns.erase(remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column)), mSortColumns.end());
02519 mSortColumns.push_back(new_sort_column);
02520
02521
02522 return (cur_sort_column != new_sort_column);
02523 }
02524 }
02525
02526
02527
02528 void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata )
02529 {
02530 LLScrollListCtrl* self = (LLScrollListCtrl*) userdata;
02531 self->mScrollLines = new_pos;
02532 }
02533
02534
02535 void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending)
02536 {
02537 if (name.empty())
02538 {
02539 sortItems();
02540 return;
02541 }
02542
02543 std::map<LLString, LLScrollListColumn>::iterator itor = mColumns.find(name);
02544 if (itor != mColumns.end())
02545 {
02546 sortByColumn((*itor).second.mIndex, ascending);
02547 }
02548 }
02549
02550
02551 void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending)
02552 {
02553 if (setSort(column, ascending))
02554 {
02555 sortItems();
02556 }
02557 }
02558
02559 void LLScrollListCtrl::sortItems()
02560 {
02561
02562 std::stable_sort(
02563 mItemList.begin(),
02564 mItemList.end(),
02565 SortScrollListItem(mSortColumns));
02566
02567 setSorted(TRUE);
02568 }
02569
02570 void LLScrollListCtrl::dirtyColumns()
02571 {
02572 mColumnsDirty = TRUE;
02573
02574
02575
02576 mColumnsIndexed.resize(mColumns.size());
02577
02578 std::map<LLString, LLScrollListColumn>::iterator column_itor;
02579 for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)
02580 {
02581 LLScrollListColumn *column = &column_itor->second;
02582 mColumnsIndexed[column_itor->second.mIndex] = column;
02583 }
02584 }
02585
02586
02587 S32 LLScrollListCtrl::getScrollPos() const
02588 {
02589 return mScrollbar->getDocPos();
02590 }
02591
02592
02593 void LLScrollListCtrl::setScrollPos( S32 pos )
02594 {
02595 mScrollbar->setDocPos( pos );
02596
02597 onScrollChange(mScrollbar->getDocPos(), mScrollbar, this);
02598 }
02599
02600
02601 void LLScrollListCtrl::scrollToShowSelected()
02602 {
02603
02604
02605 if (hasMouseCapture())
02606 {
02607 return;
02608 }
02609
02610 S32 index = getFirstSelectedIndex();
02611 if (index < 0)
02612 {
02613 return;
02614 }
02615
02616 LLScrollListItem* item = mItemList[index];
02617 if (!item)
02618 {
02619
02620 return;
02621 }
02622
02623 S32 lowest = mScrollLines;
02624 S32 highest = mScrollLines + mPageLines;
02625
02626 if (index < lowest)
02627 {
02628
02629 setScrollPos(index);
02630 }
02631 else if (highest <= index)
02632 {
02633 setScrollPos(index - mPageLines + 1);
02634 }
02635 }
02636
02637
02638 LLXMLNodePtr LLScrollListCtrl::getXML(bool save_children) const
02639 {
02640 LLXMLNodePtr node = LLUICtrl::getXML();
02641
02642
02643
02644 node->createChild("multi_select", TRUE)->setBoolValue(mAllowMultipleSelection);
02645
02646 node->createChild("draw_border", TRUE)->setBoolValue((mBorder != NULL));
02647
02648 node->createChild("draw_heading", TRUE)->setBoolValue(mDisplayColumnHeaders);
02649
02650 node->createChild("background_visible", TRUE)->setBoolValue(mBackgroundVisible);
02651
02652 node->createChild("draw_stripes", TRUE)->setBoolValue(mDrawStripes);
02653
02654 node->createChild("column_padding", TRUE)->setIntValue(mColumnPadding);
02655
02656 addColorXML(node, mBgWriteableColor, "bg_writeable_color", "ScrollBgWriteableColor");
02657 addColorXML(node, mBgReadOnlyColor, "bg_read_only_color", "ScrollBgReadOnlyColor");
02658 addColorXML(node, mBgSelectedColor, "bg_selected_color", "ScrollSelectedBGColor");
02659 addColorXML(node, mBgStripeColor, "bg_stripe_color", "ScrollBGStripeColor");
02660 addColorXML(node, mFgSelectedColor, "fg_selected_color", "ScrollSelectedFGColor");
02661 addColorXML(node, mFgUnselectedColor, "fg_unselected_color", "ScrollUnselectedColor");
02662 addColorXML(node, mFgDisabledColor, "fg_disable_color", "ScrollDisabledColor");
02663 addColorXML(node, mHighlightedColor, "highlighted_color", "ScrollHighlightedColor");
02664
02665
02666
02667 std::map<LLString, LLScrollListColumn>::const_iterator itor;
02668 std::vector<const LLScrollListColumn*> sorted_list;
02669 sorted_list.resize(mColumns.size());
02670 for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
02671 {
02672 sorted_list[itor->second.mIndex] = &itor->second;
02673 }
02674
02675 std::vector<const LLScrollListColumn*>::iterator itor2;
02676 for (itor2 = sorted_list.begin(); itor2 != sorted_list.end(); ++itor2)
02677 {
02678 LLXMLNodePtr child_node = node->createChild("column", FALSE);
02679 const LLScrollListColumn *column = *itor2;
02680
02681 child_node->createChild("name", TRUE)->setStringValue(column->mName);
02682 child_node->createChild("label", TRUE)->setStringValue(column->mLabel);
02683 child_node->createChild("width", TRUE)->setIntValue(column->mWidth);
02684 }
02685
02686 return node;
02687 }
02688
02689 void LLScrollListCtrl::setScrollListParameters(LLXMLNodePtr node)
02690 {
02691
02692
02693
02694 LLColor4 color;
02695 if(node->hasAttribute("fg_unselected_color"))
02696 {
02697 LLUICtrlFactory::getAttributeColor(node,"fg_unselected_color", color);
02698 setFgUnselectedColor(color);
02699 }
02700 if(node->hasAttribute("fg_selected_color"))
02701 {
02702 LLUICtrlFactory::getAttributeColor(node,"fg_selected_color", color);
02703 setFgSelectedColor(color);
02704 }
02705 if(node->hasAttribute("bg_selected_color"))
02706 {
02707 LLUICtrlFactory::getAttributeColor(node,"bg_selected_color", color);
02708 setBgSelectedColor(color);
02709 }
02710 if(node->hasAttribute("fg_disable_color"))
02711 {
02712 LLUICtrlFactory::getAttributeColor(node,"fg_disable_color", color);
02713 setFgDisableColor(color);
02714 }
02715 if(node->hasAttribute("bg_writeable_color"))
02716 {
02717 LLUICtrlFactory::getAttributeColor(node,"bg_writeable_color", color);
02718 setBgWriteableColor(color);
02719 }
02720 if(node->hasAttribute("bg_read_only_color"))
02721 {
02722 LLUICtrlFactory::getAttributeColor(node,"bg_read_only_color", color);
02723 setReadOnlyBgColor(color);
02724 }
02725 if (LLUICtrlFactory::getAttributeColor(node,"bg_stripe_color", color))
02726 {
02727 setBgStripeColor(color);
02728 }
02729 if (LLUICtrlFactory::getAttributeColor(node,"highlighted_color", color))
02730 {
02731 setHighlightedColor(color);
02732 }
02733
02734 if(node->hasAttribute("background_visible"))
02735 {
02736 BOOL background_visible;
02737 node->getAttributeBOOL("background_visible", background_visible);
02738 setBackgroundVisible(background_visible);
02739 }
02740
02741 if(node->hasAttribute("draw_stripes"))
02742 {
02743 BOOL draw_stripes;
02744 node->getAttributeBOOL("draw_stripes", draw_stripes);
02745 setDrawStripes(draw_stripes);
02746 }
02747
02748 if(node->hasAttribute("column_padding"))
02749 {
02750 S32 column_padding;
02751 node->getAttributeS32("column_padding", column_padding);
02752 setColumnPadding(column_padding);
02753 }
02754 }
02755
02756
02757 LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
02758 {
02759 LLString name("scroll_list");
02760 node->getAttributeString("name", name);
02761
02762 LLRect rect;
02763 createRect(node, rect, parent, LLRect());
02764
02765 BOOL multi_select = FALSE;
02766 node->getAttributeBOOL("multi_select", multi_select);
02767
02768 BOOL draw_border = TRUE;
02769 node->getAttributeBOOL("draw_border", draw_border);
02770
02771 BOOL draw_heading = FALSE;
02772 node->getAttributeBOOL("draw_heading", draw_heading);
02773
02774 S32 search_column = 0;
02775 node->getAttributeS32("search_column", search_column);
02776
02777 S32 sort_column = -1;
02778 node->getAttributeS32("sort_column", sort_column);
02779
02780 BOOL sort_ascending = TRUE;
02781 node->getAttributeBOOL("sort_ascending", sort_ascending);
02782
02783 LLUICtrlCallback callback = NULL;
02784
02785 LLScrollListCtrl* scroll_list = new LLScrollListCtrl(
02786 name,
02787 rect,
02788 callback,
02789 NULL,
02790 multi_select,
02791 draw_border);
02792
02793 scroll_list->setDisplayHeading(draw_heading);
02794 if (node->hasAttribute("heading_height"))
02795 {
02796 S32 heading_height;
02797 node->getAttributeS32("heading_height", heading_height);
02798 scroll_list->setHeadingHeight(heading_height);
02799 }
02800
02801 scroll_list->setScrollListParameters(node);
02802
02803 scroll_list->initFromXML(node, parent);
02804
02805 scroll_list->setSearchColumn(search_column);
02806
02807 if (sort_column >= 0)
02808 {
02809 scroll_list->sortByColumn(sort_column, sort_ascending);
02810 }
02811
02812 LLSD columns;
02813 S32 index = 0;
02814 LLXMLNodePtr child;
02815 S32 total_static = 0;
02816 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
02817 {
02818 if (child->hasName("column"))
02819 {
02820 LLString labelname("");
02821 child->getAttributeString("label", labelname);
02822
02823 LLString columnname(labelname);
02824 child->getAttributeString("name", columnname);
02825
02826 LLString sortname(columnname);
02827 child->getAttributeString("sort", sortname);
02828
02829 BOOL sort_ascending = TRUE;
02830 child->getAttributeBOOL("sort_ascending", sort_ascending);
02831
02832 LLString imagename;
02833 child->getAttributeString("image", imagename);
02834
02835 BOOL columndynamicwidth = FALSE;
02836 child->getAttributeBOOL("dynamicwidth", columndynamicwidth);
02837
02838 S32 columnwidth = -1;
02839 child->getAttributeS32("width", columnwidth);
02840
02841 LLString tooltip;
02842 child->getAttributeString("tool_tip", tooltip);
02843
02844 if(!columndynamicwidth) total_static += llmax(0, columnwidth);
02845
02846 F32 columnrelwidth = 0.f;
02847 child->getAttributeF32("relwidth", columnrelwidth);
02848
02849 LLFontGL::HAlign h_align = LLFontGL::LEFT;
02850 h_align = LLView::selectFontHAlign(child);
02851
02852 columns[index]["name"] = columnname;
02853 columns[index]["sort"] = sortname;
02854 columns[index]["sort_ascending"] = sort_ascending;
02855 columns[index]["image"] = imagename;
02856 columns[index]["label"] = labelname;
02857 columns[index]["width"] = columnwidth;
02858 columns[index]["relwidth"] = columnrelwidth;
02859 columns[index]["dynamicwidth"] = columndynamicwidth;
02860 columns[index]["halign"] = (S32)h_align;
02861 columns[index]["tool_tip"] = tooltip;
02862
02863 index++;
02864 }
02865 }
02866 scroll_list->setTotalStaticColumnWidth(total_static);
02867 scroll_list->setColumnHeadings(columns);
02868
02869 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
02870 {
02871 if (child->hasName("row"))
02872 {
02873 LLUUID id;
02874 child->getAttributeUUID("id", id);
02875
02876 LLSD row;
02877
02878 row["id"] = id;
02879
02880 S32 column_idx = 0;
02881 LLXMLNodePtr row_child;
02882 for (row_child = child->getFirstChild(); row_child.notNull(); row_child = row_child->getNextSibling())
02883 {
02884 if (row_child->hasName("column"))
02885 {
02886 LLString value = row_child->getTextContents();
02887
02888 LLString columnname("");
02889 row_child->getAttributeString("name", columnname);
02890
02891 LLString font("");
02892 row_child->getAttributeString("font", font);
02893
02894 LLString font_style("");
02895 row_child->getAttributeString("font-style", font_style);
02896
02897 row["columns"][column_idx]["column"] = columnname;
02898 row["columns"][column_idx]["value"] = value;
02899 row["columns"][column_idx]["font"] = font;
02900 row["columns"][column_idx]["font-style"] = font_style;
02901 column_idx++;
02902 }
02903 }
02904 scroll_list->addElement(row);
02905 }
02906 }
02907
02908 LLString contents = node->getTextContents();
02909 if (!contents.empty())
02910 {
02911 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
02912 boost::char_separator<char> sep("\t\n");
02913 tokenizer tokens(contents, sep);
02914 tokenizer::iterator token_iter = tokens.begin();
02915
02916 while(token_iter != tokens.end())
02917 {
02918 const char* line = token_iter->c_str();
02919 scroll_list->addSimpleElement(line);
02920 ++token_iter;
02921 }
02922 }
02923
02924 return scroll_list;
02925 }
02926
02927
02928
02929
02930 void LLScrollListCtrl::copy()
02931 {
02932 LLString buffer;
02933
02934 std::vector<LLScrollListItem*> items = getAllSelected();
02935 std::vector<LLScrollListItem*>::iterator itor;
02936 for (itor = items.begin(); itor != items.end(); ++itor)
02937 {
02938 buffer += (*itor)->getContentsCSV() + "\n";
02939 }
02940 gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length());
02941 }
02942
02943
02944 BOOL LLScrollListCtrl::canCopy() const
02945 {
02946 return (getFirstSelected() != NULL);
02947 }
02948
02949
02950 void LLScrollListCtrl::cut()
02951 {
02952 copy();
02953 doDelete();
02954 }
02955
02956
02957 BOOL LLScrollListCtrl::canCut() const
02958 {
02959 return canCopy() && canDoDelete();
02960 }
02961
02962
02963 void LLScrollListCtrl::selectAll()
02964 {
02965
02966 item_list::iterator iter;
02967 for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
02968 {
02969 LLScrollListItem *itemp = *iter;
02970 if( itemp->getEnabled() )
02971 {
02972 selectItem(itemp, FALSE);
02973 }
02974 }
02975
02976 if (mCommitOnSelectionChange)
02977 {
02978 commitIfChanged();
02979 }
02980 }
02981
02982
02983 BOOL LLScrollListCtrl::canSelectAll() const
02984 {
02985 return getCanSelect() && mAllowMultipleSelection && !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable);
02986 }
02987
02988
02989 void LLScrollListCtrl::deselect()
02990 {
02991 deselectAllItems();
02992 }
02993
02994
02995 BOOL LLScrollListCtrl::canDeselect() const
02996 {
02997 return getCanSelect();
02998 }
02999
03000 void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos)
03001 {
03002 LLString name = column["name"].asString();
03003
03004 if (name.empty())
03005 {
03006 std::ostringstream new_name;
03007 new_name << mColumnsIndexed.size();
03008 name = new_name.str();
03009 }
03010 if (mColumns.find(name) == mColumns.end())
03011 {
03012
03013 mColumns[name] = LLScrollListColumn(column);
03014 LLScrollListColumn* new_column = &mColumns[name];
03015 new_column->mParentCtrl = this;
03016 new_column->mIndex = mColumns.size()-1;
03017
03018
03019 if (new_column->mWidth > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth)
03020 {
03021 if (new_column->mRelWidth >= 0)
03022 {
03023 new_column->mWidth = (S32)llround(new_column->mRelWidth*mItemListRect.getWidth());
03024 }
03025 else if(new_column->mDynamicWidth)
03026 {
03027 mNumDynamicWidthColumns++;
03028 new_column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns;
03029 }
03030 S32 top = mItemListRect.mTop;
03031 S32 left = mItemListRect.mLeft;
03032 {
03033 std::map<LLString, LLScrollListColumn>::iterator itor;
03034 for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
03035 {
03036 if (itor->second.mIndex < new_column->mIndex &&
03037 itor->second.mWidth > 0)
03038 {
03039 left += itor->second.mWidth + mColumnPadding;
03040 }
03041 }
03042 }
03043 LLString button_name = "btn_" + name;
03044 S32 right = left+new_column->mWidth;
03045 if (new_column->mIndex != (S32)mColumns.size()-1)
03046 {
03047 right += mColumnPadding;
03048 }
03049 LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top);
03050 new_column->mHeader = new LLColumnHeader(button_name, temp_rect, new_column);
03051 if(column["image"].asString() != "")
03052 {
03053
03054 new_column->mHeader->setImage(column["image"].asString());
03055 }
03056 else
03057 {
03058 new_column->mHeader->setLabel(new_column->mLabel);
03059
03060 }
03061
03062 new_column->mHeader->setToolTip(column["tool_tip"].asString());
03063
03064
03065
03066 new_column->mHeader->setTabStop(FALSE);
03067 addChild(new_column->mHeader);
03068 new_column->mHeader->setVisible(mDisplayColumnHeaders);
03069
03070 sendChildToFront(mScrollbar);
03071 }
03072 }
03073
03074 dirtyColumns();
03075 }
03076
03077
03078 void LLScrollListCtrl::onClickColumn(void *userdata)
03079 {
03080 LLScrollListColumn *info = (LLScrollListColumn*)userdata;
03081 if (!info) return;
03082
03083 LLScrollListCtrl *parent = info->mParentCtrl;
03084 if (!parent) return;
03085
03086 S32 column_index = info->mIndex;
03087
03088 LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex];
03089 bool ascending = column->mSortAscending;
03090 if (column->mSortingColumn != column->mName
03091 && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end())
03092 {
03093 LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn];
03094 column_index = info_redir.mIndex;
03095 }
03096
03097
03098 sort_column_t cur_sort_column;
03099 if (!parent->mSortColumns.empty() && parent->mSortColumns.back().first == column_index)
03100 {
03101 ascending = !parent->mSortColumns.back().second;
03102 }
03103
03104 parent->sortByColumn(column_index, ascending);
03105
03106 if (parent->mOnSortChangedCallback)
03107 {
03108 parent->mOnSortChangedCallback(parent->getCallbackUserData());
03109 }
03110 }
03111
03112 std::string LLScrollListCtrl::getSortColumnName()
03113 {
03114 LLScrollListColumn* column = mSortColumns.empty() ? NULL : mColumnsIndexed[mSortColumns.back().first];
03115
03116 if (column) return column->mName;
03117 else return "";
03118 }
03119
03120 BOOL LLScrollListCtrl::needsSorting()
03121 {
03122 return !mSortColumns.empty();
03123 }
03124
03125 void LLScrollListCtrl::clearColumns()
03126 {
03127 std::map<LLString, LLScrollListColumn>::iterator itor;
03128 for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
03129 {
03130 LLColumnHeader *header = itor->second.mHeader;
03131 if (header)
03132 {
03133 removeChild(header);
03134 delete header;
03135 }
03136 }
03137 mColumns.clear();
03138 mSortColumns.clear();
03139 }
03140
03141 void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& label)
03142 {
03143 std::map<LLString, LLScrollListColumn>::iterator itor = mColumns.find(column);
03144 if (itor != mColumns.end())
03145 {
03146 itor->second.mLabel = label;
03147 if (itor->second.mHeader)
03148 {
03149 itor->second.mHeader->setLabel(label);
03150 }
03151 }
03152 }
03153
03154 LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index)
03155 {
03156 if (index < 0 || index >= (S32)mColumnsIndexed.size())
03157 {
03158 return NULL;
03159 }
03160 return mColumnsIndexed[index];
03161 }
03162
03163 void LLScrollListCtrl::setColumnHeadings(LLSD headings)
03164 {
03165 mColumns.clear();
03166 LLSD::array_const_iterator itor;
03167 for (itor = headings.beginArray(); itor != headings.endArray(); ++itor)
03168 {
03169 addColumn(*itor);
03170 }
03171 }
03172
03173 LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition pos, void* userdata)
03174 {
03175
03176 LLSD id = value["id"];
03177
03178 LLScrollListItem *new_item = new LLScrollListItem(id, userdata);
03179 if (value.has("enabled"))
03180 {
03181 new_item->setEnabled( value["enabled"].asBoolean() );
03182 }
03183
03184 new_item->setNumColumns(mColumns.size());
03185
03186
03187 LLSD columns = value["columns"];
03188 LLSD::array_const_iterator itor;
03189 S32 col_index = 0 ;
03190 for (itor = columns.beginArray(); itor != columns.endArray(); ++itor)
03191 {
03192 if (itor->isUndefined())
03193 {
03194
03195 continue;
03196 }
03197 LLString column = (*itor)["column"].asString();
03198
03199 LLScrollListColumn* columnp = NULL;
03200
03201
03202 if (column.empty())
03203 {
03204 std::ostringstream new_name;
03205 new_name << col_index;
03206 column = new_name.str();
03207 }
03208
03209 std::map<LLString, LLScrollListColumn>::iterator column_itor;
03210 column_itor = mColumns.find(column);
03211 if (column_itor != mColumns.end())
03212 {
03213 columnp = &column_itor->second;
03214 }
03215
03216
03217 if (!columnp)
03218 {
03219 LLSD new_column;
03220 new_column["name"] = column;
03221 new_column["label"] = column;
03222
03223
03224 if (itor->has("width"))
03225 {
03226 new_column["width"] = (*itor)["width"];
03227 }
03228 else
03229 {
03230 new_column["dynamicwidth"] = true;
03231 }
03232 addColumn(new_column);
03233 columnp = &mColumns[column];
03234 new_item->setNumColumns(mColumns.size());
03235 }
03236
03237 S32 index = columnp->mIndex;
03238 S32 width = columnp->mWidth;
03239 LLFontGL::HAlign font_alignment = columnp->mFontAlignment;
03240
03241 LLSD value = (*itor)["value"];
03242 LLString fontname = (*itor)["font"].asString();
03243 LLString fontstyle = (*itor)["font-style"].asString();
03244 LLString type = (*itor)["type"].asString();
03245 BOOL has_color = (*itor).has("color");
03246 LLColor4 color = ((*itor)["color"]);
03247 BOOL enabled = !(*itor).has("enabled") || (*itor)["enabled"].asBoolean() == true;
03248
03249 const LLFontGL *font = LLResMgr::getInstance()->getRes(fontname);
03250 if (!font)
03251 {
03252 font = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL );
03253 }
03254 U8 font_style = LLFontGL::getStyleFromString(fontstyle);
03255
03256 if (type == "icon")
03257 {
03258 LLScrollListIcon* cell = new LLScrollListIcon(value, width);
03259 if (has_color)
03260 {
03261 cell->setColor(color);
03262 }
03263 new_item->setColumn(index, cell);
03264 }
03265 else if (type == "checkbox")
03266 {
03267 LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl("check",
03268 LLRect(0, width, width, 0), " ");
03269 ctrl->setEnabled(enabled);
03270 ctrl->setValue(value);
03271 LLScrollListCheck* cell = new LLScrollListCheck(ctrl,width);
03272 if (has_color)
03273 {
03274 cell->setColor(color);
03275 }
03276 new_item->setColumn(index, cell);
03277 }
03278 else if (type == "separator")
03279 {
03280 LLScrollListSeparator* cell = new LLScrollListSeparator(width);
03281 if (has_color)
03282 {
03283 cell->setColor(color);
03284 }
03285 new_item->setColumn(index, cell);
03286 }
03287 else
03288 {
03289 LLScrollListText* cell = new LLScrollListText(value.asString(), font, width, font_style, font_alignment);
03290 if (has_color)
03291 {
03292 cell->setColor(color);
03293 }
03294 new_item->setColumn(index, cell);
03295 if (columnp->mHeader && !value.asString().empty())
03296 {
03297 columnp->mHeader->setHasResizableElement(TRUE);
03298 }
03299 }
03300
03301 col_index++;
03302 }
03303
03304
03305 for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it)
03306 {
03307 S32 column_idx = column_it->second.mIndex;
03308 if (new_item->getColumn(column_idx) == NULL)
03309 {
03310 LLScrollListColumn* column_ptr = &column_it->second;
03311 new_item->setColumn(column_idx, new LLScrollListText("", LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL ), column_ptr->mWidth, LLFontGL::NORMAL));
03312 }
03313 }
03314
03315 addItem(new_item, pos);
03316
03317 return new_item;
03318 }
03319
03320 LLScrollListItem* LLScrollListCtrl::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id)
03321 {
03322 LLSD entry_id = id;
03323
03324 if (id.isUndefined())
03325 {
03326 entry_id = value;
03327 }
03328
03329 LLScrollListItem *new_item = new LLScrollListItem(entry_id);
03330
03331 const LLFontGL *font = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL );
03332
03333 new_item->addColumn(value, font, getRect().getWidth());
03334
03335 addItem(new_item, pos);
03336 return new_item;
03337 }
03338
03339 void LLScrollListCtrl::setValue(const LLSD& value )
03340 {
03341 LLSD::array_const_iterator itor;
03342 for (itor = value.beginArray(); itor != value.endArray(); ++itor)
03343 {
03344 addElement(*itor);
03345 }
03346 }
03347
03348 LLSD LLScrollListCtrl::getValue() const
03349 {
03350 LLScrollListItem *item = getFirstSelected();
03351 if (!item) return LLSD();
03352 return item->getValue();
03353 }
03354
03355 BOOL LLScrollListCtrl::operateOnSelection(EOperation op)
03356 {
03357 if (op == OP_DELETE)
03358 {
03359 deleteSelectedItems();
03360 return TRUE;
03361 }
03362 else if (op == OP_DESELECT)
03363 {
03364 deselectAllItems();
03365 }
03366 return FALSE;
03367 }
03368
03369 BOOL LLScrollListCtrl::operateOnAll(EOperation op)
03370 {
03371 if (op == OP_DELETE)
03372 {
03373 clearRows();
03374 return TRUE;
03375 }
03376 else if (op == OP_DESELECT)
03377 {
03378 deselectAllItems();
03379 }
03380 else if (op == OP_SELECT)
03381 {
03382 selectAll();
03383 }
03384 return FALSE;
03385 }
03386
03387 void LLScrollListCtrl::setFocus(BOOL b)
03388 {
03389 mSearchString.clear();
03390
03391 if (!getFirstSelected())
03392 {
03393 selectFirstItem();
03394
03395 }
03396 LLUICtrl::setFocus(b);
03397 }
03398
03399
03400
03401 BOOL LLScrollListCtrl::isDirty() const
03402 {
03403 BOOL grubby = mDirty;
03404 if ( !mAllowMultipleSelection )
03405 {
03406 grubby = (mOriginalSelection != getFirstSelectedIndex());
03407 }
03408 return grubby;
03409 }
03410
03411
03412 void LLScrollListCtrl::resetDirty()
03413 {
03414 mDirty = FALSE;
03415 mOriginalSelection = getFirstSelectedIndex();
03416 }
03417
03418
03419
03420 void LLScrollListCtrl::onFocusReceived()
03421 {
03422
03423 mSelectionChanged = FALSE;
03424 LLUICtrl::onFocusReceived();
03425 }
03426
03427
03428 void LLScrollListCtrl::onFocusLost()
03429 {
03430 if (hasMouseCapture())
03431 {
03432 gFocusMgr.setMouseCapture(NULL);
03433 }
03434 LLUICtrl::onFocusLost();
03435 }
03436
03437 LLColumnHeader::LLColumnHeader(const LLString& label, const LLRect &rect, LLScrollListColumn* column, const LLFontGL* fontp) :
03438 LLComboBox(label, rect, label, NULL, NULL),
03439 mColumn(column),
03440 mOrigLabel(label),
03441 mShowSortOptions(FALSE),
03442 mHasResizableElement(FALSE)
03443 {
03444 mListPosition = LLComboBox::ABOVE;
03445 setCommitCallback(onSelectSort);
03446 setCallbackUserData(this);
03447 mButton->setTabStop(FALSE);
03448
03449 mButton->setHeldDownDelay(LLUI::sConfigGroup->getF32("ColumnHeaderDropDownDelay"), 2);
03450 mButton->setHeldDownCallback(onHeldDown);
03451 mButton->setClickedCallback(onClick);
03452 mButton->setMouseDownCallback(onMouseDown);
03453
03454 mButton->setCallbackUserData(this);
03455
03456 mAscendingText = "[LOW]...[HIGH](Ascending)";
03457 mDescendingText = "[HIGH]...[LOW](Descending)";
03458
03459 mList->reshape(llmax(mList->getRect().getWidth(), 110, getRect().getWidth()), mList->getRect().getHeight());
03460
03461
03462 const S32 RESIZE_BAR_THICKNESS = 3;
03463 mResizeBar = new LLResizeBar(
03464 "resizebar",
03465 this,
03466 LLRect( getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0),
03467 MIN_COLUMN_WIDTH, S32_MAX, LLResizeBar::RIGHT );
03468 addChild(mResizeBar);
03469
03470 mResizeBar->setEnabled(FALSE);
03471 }
03472
03473 LLColumnHeader::~LLColumnHeader()
03474 {
03475 }
03476
03477 void LLColumnHeader::draw()
03478 {
03479 BOOL draw_arrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn;
03480
03481 BOOL is_ascending = mColumn->mParentCtrl->getSortAscending();
03482 mButton->setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, draw_arrow ? LLColor4::white : LLColor4::transparent);
03483 mArrowImage = mButton->getImageOverlay();
03484
03485
03486
03487
03488
03489
03490
03491
03492 LLComboBox::draw();
03493
03494 if (mList->getVisible())
03495 {
03496
03497 mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn, getCurrentIndex() == 0);
03498 }
03499 }
03500
03501 BOOL LLColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask)
03502 {
03503 if (canResize() && mResizeBar->getRect().pointInRect(x, y))
03504 {
03505
03506 LLRect column_rect = getRect();
03507 column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth;
03508 userSetShape(column_rect);
03509 }
03510 else
03511 {
03512 onClick(this);
03513 }
03514 return TRUE;
03515 }
03516
03517 void LLColumnHeader::setImage(const LLString &image_name)
03518 {
03519 if (mButton)
03520 {
03521 mButton->setImageSelected(image_name);
03522 mButton->setImageUnselected(image_name);
03523 }
03524 }
03525
03526
03527 void LLColumnHeader::onClick(void* user_data)
03528 {
03529 LLColumnHeader* headerp = (LLColumnHeader*)user_data;
03530 if (!headerp) return;
03531
03532 LLScrollListColumn* column = headerp->mColumn;
03533 if (!column) return;
03534
03535 if (headerp->mList->getVisible())
03536 {
03537 headerp->hideList();
03538 }
03539
03540 LLScrollListCtrl::onClickColumn(column);
03541
03542
03543 headerp->mList->selectNthItem(column->mParentCtrl->getSortAscending() ? 0 : 1);
03544 }
03545
03546
03547 void LLColumnHeader::onMouseDown(void* user_data)
03548 {
03549
03550 return;
03551 }
03552
03553
03554 void LLColumnHeader::onHeldDown(void* user_data)
03555 {
03556 LLColumnHeader* headerp = (LLColumnHeader*)user_data;
03557 headerp->showList();
03558 }
03559
03560 void LLColumnHeader::showList()
03561 {
03562 if (mShowSortOptions)
03563 {
03564
03565 mOrigLabel = mButton->getLabelSelected();
03566
03567
03568 mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn, mColumn->mParentCtrl->getSortAscending());
03569
03570 LLString low_item_text;
03571 LLString high_item_text;
03572
03573 LLScrollListItem* itemp = mColumn->mParentCtrl->getFirstData();
03574 if (itemp)
03575 {
03576 LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex);
03577 if (cell && cell->isText())
03578 {
03579 if (mColumn->mParentCtrl->getSortAscending())
03580 {
03581 low_item_text = cell->getValue().asString();
03582 }
03583 else
03584 {
03585 high_item_text = cell->getValue().asString();
03586 }
03587 }
03588 }
03589
03590 itemp = mColumn->mParentCtrl->getLastData();
03591 if (itemp)
03592 {
03593 LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex);
03594 if (cell && cell->isText())
03595 {
03596 if (mColumn->mParentCtrl->getSortAscending())
03597 {
03598 high_item_text = cell->getValue().asString();
03599 }
03600 else
03601 {
03602 low_item_text = cell->getValue().asString();
03603 }
03604 }
03605 }
03606
03607 LLString::truncate(low_item_text, 3);
03608 LLString::truncate(high_item_text, 3);
03609
03610 LLString ascending_string;
03611 LLString descending_string;
03612
03613 if (low_item_text.empty() || high_item_text.empty())
03614 {
03615 ascending_string = "Ascending";
03616 descending_string = "Descending";
03617 }
03618 else
03619 {
03620 mAscendingText.setArg("[LOW]", low_item_text);
03621 mAscendingText.setArg("[HIGH]", high_item_text);
03622 mDescendingText.setArg("[LOW]", low_item_text);
03623 mDescendingText.setArg("[HIGH]", high_item_text);
03624 ascending_string = mAscendingText.getString();
03625 descending_string = mDescendingText.getString();
03626 }
03627
03628 S32 text_width = LLFontGL::sSansSerifSmall->getWidth(ascending_string);
03629 text_width = llmax(text_width, LLFontGL::sSansSerifSmall->getWidth(descending_string)) + 10;
03630 text_width = llmax(text_width, getRect().getWidth() - 30);
03631
03632 mList->getColumn(0)->mWidth = text_width;
03633 ((LLScrollListText*)mList->getFirstData()->getColumn(0))->setText(ascending_string);
03634 ((LLScrollListText*)mList->getLastData()->getColumn(0))->setText(descending_string);
03635
03636 mList->reshape(llmax(text_width + 30, 110, getRect().getWidth()), mList->getRect().getHeight());
03637
03638 LLComboBox::showList();
03639 }
03640 }
03641
03642
03643 void LLColumnHeader::onSelectSort(LLUICtrl* ctrl, void* user_data)
03644 {
03645 LLColumnHeader* headerp = (LLColumnHeader*)user_data;
03646 if (!headerp) return;
03647
03648 LLScrollListColumn* column = headerp->mColumn;
03649 if (!column) return;
03650 LLScrollListCtrl *parent = column->mParentCtrl;
03651 if (!parent) return;
03652
03653 if (headerp->getCurrentIndex() == 0)
03654 {
03655
03656 parent->sortByColumn(column->mSortingColumn, TRUE);
03657 }
03658 else
03659 {
03660
03661 parent->sortByColumn(column->mSortingColumn, FALSE);
03662 }
03663
03664
03665 headerp->setLabel(headerp->mOrigLabel);
03666 }
03667
03668 LLView* LLColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
03669 {
03670
03671 llassert(snap_edge == SNAP_RIGHT);
03672
03673
03674 threshold = llmin(threshold, 15);
03675
03676 LLRect snap_rect = getSnapRect();
03677
03678 S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth();
03679
03680
03681 if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 )
03682 {
03683 new_edge_val = snap_rect.mRight + snap_delta;
03684 }
03685 else
03686 {
03687 LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1);
03688 while (next_column)
03689 {
03690 if (next_column->mHeader)
03691 {
03692 snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight;
03693 if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 )
03694 {
03695 new_edge_val = snap_rect.mRight + snap_delta;
03696 }
03697 break;
03698 }
03699 next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1);
03700 }
03701 }
03702
03703 return this;
03704 }
03705
03706 void LLColumnHeader::userSetShape(const LLRect& new_rect)
03707 {
03708 S32 new_width = new_rect.getWidth();
03709 S32 delta_width = new_width - (getRect().getWidth() );
03710
03711 if (delta_width != 0)
03712 {
03713 S32 remaining_width = delta_width;
03714 S32 col;
03715 for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++)
03716 {
03717 LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
03718 if (!columnp) break;
03719
03720 if (columnp->mHeader && columnp->mHeader->canResize())
03721 {
03722
03723 S32 resize_buffer_amt = llmax(0, columnp->mWidth - MIN_COLUMN_WIDTH);
03724
03725
03726 if (delta_width < 0)
03727 {
03728 if (!columnp->mDynamicWidth && columnp->mWidth > 0)
03729 {
03730
03731 columnp->mWidth -= remaining_width;
03732 if (columnp->mRelWidth > 0.f)
03733 {
03734 columnp->mRelWidth = (F32)columnp->mWidth / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
03735 }
03736 }
03737 break;
03738 }
03739 else
03740 {
03741
03742 remaining_width -= resize_buffer_amt;
03743
03744 if (!columnp->mDynamicWidth && columnp->mWidth > 0)
03745 {
03746 columnp->mWidth -= llmin(columnp->mWidth - MIN_COLUMN_WIDTH, delta_width);
03747 if (columnp->mRelWidth > 0.f)
03748 {
03749 columnp->mRelWidth = (F32)columnp->mWidth / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
03750 }
03751 }
03752
03753 if (remaining_width <= 0)
03754 {
03755
03756 break;
03757 }
03758 }
03759 }
03760 }
03761
03762
03763 if (delta_width > 0)
03764 {
03765 delta_width -= llmax(remaining_width, 0);
03766 }
03767
03768
03769 new_width = getRect().getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding();
03770
03771
03772 mColumn->mWidth = new_width;
03773
03774
03775 if (mColumn->mRelWidth > 0.f)
03776 {
03777 mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
03778 }
03779
03780
03781
03782
03783 mColumn->mParentCtrl->updateColumns();
03784 }
03785 }
03786
03787 void LLColumnHeader::setHasResizableElement(BOOL resizable)
03788 {
03789
03790 if (mColumn->mDynamicWidth) return;
03791
03792 if (resizable != mHasResizableElement)
03793 {
03794 mHasResizableElement = resizable;
03795
03796 S32 num_resizable_columns = 0;
03797 S32 col;
03798 for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++)
03799 {
03800 LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
03801 if (columnp->mHeader && columnp->mHeader->canResize())
03802 {
03803 num_resizable_columns++;
03804 }
03805 }
03806
03807 S32 num_resizers_enabled = 0;
03808
03809
03810 for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++)
03811 {
03812 LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
03813 if (!columnp->mHeader) continue;
03814 BOOL enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize();
03815 columnp->mHeader->enableResizeBar(enable);
03816 if (enable)
03817 {
03818 num_resizers_enabled++;
03819 }
03820 }
03821 }
03822 }
03823
03824 void LLColumnHeader::enableResizeBar(BOOL enable)
03825 {
03826
03827 if (!mColumn->mDynamicWidth)
03828 {
03829 mResizeBar->setEnabled(enable);
03830 }
03831 }
03832
03833 BOOL LLColumnHeader::canResize()
03834 {
03835 return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth);
03836 }