llscrolllistctrl.cpp

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

Generated on Thu Jul 1 06:09:06 2010 for Second Life Viewer by  doxygen 1.4.7