diff options
Diffstat (limited to 'linden/indra/llui/llscrolllistctrl.cpp')
-rw-r--r-- | linden/indra/llui/llscrolllistctrl.cpp | 1048 |
1 files changed, 674 insertions, 374 deletions
diff --git a/linden/indra/llui/llscrolllistctrl.cpp b/linden/indra/llui/llscrolllistctrl.cpp index b80787f..d65ebd2 100644 --- a/linden/indra/llui/llscrolllistctrl.cpp +++ b/linden/indra/llui/llscrolllistctrl.cpp | |||
@@ -12,12 +12,12 @@ | |||
12 | * ("GPL"), unless you have obtained a separate licensing agreement | 12 | * ("GPL"), unless you have obtained a separate licensing agreement |
13 | * ("Other License"), formally executed by you and Linden Lab. Terms of | 13 | * ("Other License"), formally executed by you and Linden Lab. Terms of |
14 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | 14 | * the GPL can be found in doc/GPL-license.txt in this distribution, or |
15 | * online at http://secondlife.com/developers/opensource/gplv2 | 15 | * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 |
16 | * | 16 | * |
17 | * There are special exceptions to the terms and conditions of the GPL as | 17 | * There are special exceptions to the terms and conditions of the GPL as |
18 | * it is applied to this Source Code. View the full text of the exception | 18 | * it is applied to this Source Code. View the full text of the exception |
19 | * in the file doc/FLOSS-exception.txt in this software distribution, or | 19 | * in the file doc/FLOSS-exception.txt in this software distribution, or |
20 | * online at http://secondlife.com/developers/opensource/flossexception | 20 | * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception |
21 | * | 21 | * |
22 | * By copying, modifying or distributing this software, you acknowledge | 22 | * By copying, modifying or distributing this software, you acknowledge |
23 | * that you have read and understood your obligations described above, | 23 | * that you have read and understood your obligations described above, |
@@ -61,73 +61,101 @@ const S32 LIST_SNAP_PADDING = 5; | |||
61 | // local structures & classes. | 61 | // local structures & classes. |
62 | struct SortScrollListItem | 62 | struct SortScrollListItem |
63 | { | 63 | { |
64 | SortScrollListItem(const S32 sort_col, BOOL sort_ascending) | 64 | SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders) |
65 | { | 65 | : mSortOrders(sort_orders) |
66 | mSortCol = sort_col; | 66 | {} |
67 | mSortAscending = sort_ascending; | ||
68 | } | ||
69 | 67 | ||
70 | bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2) | 68 | bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2) |
71 | { | 69 | { |
72 | const LLScrollListCell *cell1; | 70 | if ( mSortOrders.empty() ) return true; |
73 | const LLScrollListCell *cell2; | 71 | |
74 | 72 | const LLScrollListCell *cell1 = NULL; | |
75 | cell1 = i1->getColumn(mSortCol); | 73 | const LLScrollListCell *cell2 = NULL; |
76 | cell2 = i2->getColumn(mSortCol); | ||
77 | 74 | ||
78 | S32 order = 1; | 75 | sort_order_t::const_reverse_iterator end_it = mSortOrders.rend(); |
79 | if (!mSortAscending) | 76 | sort_order_t::const_reverse_iterator it; |
77 | |||
78 | // sort over all columns in order specified by mSortOrders | ||
79 | S32 sort_result = 0; | ||
80 | for (it = mSortOrders.rbegin(); it != end_it; ++it) | ||
80 | { | 81 | { |
81 | order = -1; | 82 | S32 col_idx = it->first; |
82 | } | 83 | BOOL sort_ascending = it->second; |
83 | 84 | ||
84 | BOOL retval = FALSE; | 85 | cell1 = i1->getColumn(col_idx); |
86 | cell2 = i2->getColumn(col_idx); | ||
87 | // ascending or descending sort for this column? | ||
88 | S32 order = 1; | ||
89 | if (!sort_ascending) | ||
90 | { | ||
91 | order = -1; | ||
92 | } | ||
85 | 93 | ||
86 | if (cell1 && cell2) | 94 | if (cell1 && cell2) |
87 | { | 95 | { |
88 | retval = ((order * LLString::compareDict(cell1->getText(), cell2->getText())) < 0); | 96 | sort_result = (order * LLString::compareDict(cell1->getValue().asString(), cell2->getValue().asString())); |
97 | if (sort_result != 0) | ||
98 | { | ||
99 | // we have a sort order! | ||
100 | break; | ||
101 | } | ||
102 | } | ||
89 | } | 103 | } |
90 | 104 | ||
91 | return (retval ? TRUE : FALSE); | 105 | return sort_result < 0; |
92 | } | 106 | } |
93 | 107 | ||
94 | protected: | 108 | typedef std::vector<std::pair<S32, BOOL> > sort_order_t; |
95 | S32 mSortCol; | 109 | const sort_order_t& mSortOrders; |
96 | S32 mSortAscending; | ||
97 | }; | 110 | }; |
98 | 111 | ||
99 | 112 | ||
100 | |||
101 | // | 113 | // |
102 | // LLScrollListIcon | 114 | // LLScrollListIcon |
103 | // | 115 | // |
104 | LLScrollListIcon::LLScrollListIcon(LLImageGL* icon, S32 width, LLUUID image_id) | 116 | LLScrollListIcon::LLScrollListIcon(const LLUUID& icon_id, S32 width) |
105 | : mIcon(icon), | 117 | : LLScrollListCell(width), |
106 | mImageUUID(image_id.asString()), | 118 | mColor(LLColor4::white), |
107 | mColor(LLColor4::white) | 119 | mImageUUID(icon_id) |
108 | { | 120 | { |
109 | if (width) | 121 | // don't use default image specified by LLUUID::null, use no image in that case |
110 | { | 122 | mIcon = icon_id.isNull() ? NULL : LLUI::sImageProvider->getImageByID(icon_id); |
111 | mWidth = width; | ||
112 | } | ||
113 | else | ||
114 | { | ||
115 | mWidth = icon->getWidth(); | ||
116 | } | ||
117 | } | 123 | } |
118 | 124 | ||
119 | LLScrollListIcon::~LLScrollListIcon() | 125 | LLScrollListIcon::~LLScrollListIcon() |
120 | { | 126 | { |
121 | } | 127 | } |
122 | 128 | ||
129 | void LLScrollListIcon::setValue(LLSD value) | ||
130 | { | ||
131 | mImageUUID = value.asUUID(); | ||
132 | // don't use default image specified by LLUUID::null, use no image in that case | ||
133 | mIcon = mImageUUID.isNull() ? NULL : LLUI::sImageProvider->getImageByID(mImageUUID); | ||
134 | } | ||
135 | |||
136 | |||
123 | void LLScrollListIcon::setColor(const LLColor4& color) | 137 | void LLScrollListIcon::setColor(const LLColor4& color) |
124 | { | 138 | { |
125 | mColor = color; | 139 | mColor = color; |
126 | } | 140 | } |
127 | 141 | ||
128 | void LLScrollListIcon::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const | 142 | S32 LLScrollListIcon::getWidth() const |
143 | { | ||
144 | // if no specified fix width, use width of icon | ||
145 | if (mWidth == 0) | ||
146 | { | ||
147 | return mIcon->getWidth(); | ||
148 | } | ||
149 | return mWidth; | ||
150 | } | ||
151 | |||
152 | |||
153 | void LLScrollListIcon::draw(const LLColor4& color, const LLColor4& highlight_color) const | ||
129 | { | 154 | { |
130 | gl_draw_image(0, 0, mIcon, mColor); | 155 | if (mIcon) |
156 | { | ||
157 | gl_draw_image(0, 0, mIcon, mColor); | ||
158 | } | ||
131 | } | 159 | } |
132 | 160 | ||
133 | // | 161 | // |
@@ -155,32 +183,32 @@ LLScrollListCheck::~LLScrollListCheck() | |||
155 | delete mCheckBox; | 183 | delete mCheckBox; |
156 | } | 184 | } |
157 | 185 | ||
158 | void LLScrollListCheck::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const | 186 | void LLScrollListCheck::draw(const LLColor4& color, const LLColor4& highlight_color) const |
159 | { | 187 | { |
160 | mCheckBox->draw(); | 188 | mCheckBox->draw(); |
161 | |||
162 | } | 189 | } |
163 | 190 | ||
164 | BOOL LLScrollListCheck::handleClick() | 191 | BOOL LLScrollListCheck::handleClick() |
165 | { | 192 | { |
166 | if ( mCheckBox->getEnabled() ) | 193 | if (mCheckBox->getEnabled()) |
167 | { | 194 | { |
168 | LLCheckBoxCtrl::onButtonPress(mCheckBox); | 195 | mCheckBox->toggle(); |
169 | } | 196 | } |
197 | // don't change selection when clicking on embedded checkbox | ||
170 | return TRUE; | 198 | return TRUE; |
171 | } | 199 | } |
172 | 200 | ||
173 | // | 201 | // |
174 | // LLScrollListSeparator | 202 | // LLScrollListSeparator |
175 | // | 203 | // |
176 | LLScrollListSeparator::LLScrollListSeparator(S32 width) : mWidth(width) | 204 | LLScrollListSeparator::LLScrollListSeparator(S32 width) : LLScrollListCell(width) |
177 | { | 205 | { |
178 | } | 206 | } |
179 | 207 | ||
180 | void LLScrollListSeparator::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const | 208 | void LLScrollListSeparator::draw(const LLColor4& color, const LLColor4& highlight_color) const |
181 | { | 209 | { |
182 | //*FIXME: use dynamic item heights and make separators narrow, and inactive | 210 | //*FIXME: use dynamic item heights and make separators narrow, and inactive |
183 | gl_line_2d(5, 8, llmax(5, width - 5), 8, color); | 211 | gl_line_2d(5, 8, llmax(5, getWidth() - 5), 8, color); |
184 | } | 212 | } |
185 | 213 | ||
186 | // | 214 | // |
@@ -189,11 +217,11 @@ void LLScrollListSeparator::drawToWidth(S32 width, const LLColor4& color, const | |||
189 | U32 LLScrollListText::sCount = 0; | 217 | U32 LLScrollListText::sCount = 0; |
190 | 218 | ||
191 | LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, S32 width, U8 font_style, LLFontGL::HAlign font_alignment, LLColor4& color, BOOL use_color, BOOL visible) | 219 | LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, S32 width, U8 font_style, LLFontGL::HAlign font_alignment, LLColor4& color, BOOL use_color, BOOL visible) |
192 | : mText( text ), | 220 | : LLScrollListCell(width), |
221 | mText( text ), | ||
193 | mFont( font ), | 222 | mFont( font ), |
194 | mFontStyle( font_style ), | 223 | mFontStyle( font_style ), |
195 | mFontAlignment( font_alignment ), | 224 | mFontAlignment( font_alignment ), |
196 | mWidth( width ), | ||
197 | mVisible( visible ), | 225 | mVisible( visible ), |
198 | mHighlightCount( 0 ), | 226 | mHighlightCount( 0 ), |
199 | mHighlightOffset( 0 ) | 227 | mHighlightOffset( 0 ) |
@@ -213,7 +241,7 @@ LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, | |||
213 | // initialize rounded rect image | 241 | // initialize rounded rect image |
214 | if (!mRoundedRectImage) | 242 | if (!mRoundedRectImage) |
215 | { | 243 | { |
216 | mRoundedRectImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("rounded_square.tga"))); | 244 | mRoundedRectImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("rounded_square.tga"))); |
217 | } | 245 | } |
218 | } | 246 | } |
219 | 247 | ||
@@ -223,6 +251,12 @@ LLScrollListText::~LLScrollListText() | |||
223 | delete mColor; | 251 | delete mColor; |
224 | } | 252 | } |
225 | 253 | ||
254 | S32 LLScrollListText::getContentWidth() const | ||
255 | { | ||
256 | return mFont->getWidth(mText.getString()); | ||
257 | } | ||
258 | |||
259 | |||
226 | void LLScrollListText::setColor(const LLColor4& color) | 260 | void LLScrollListText::setColor(const LLColor4& color) |
227 | { | 261 | { |
228 | if (!mColor) | 262 | if (!mColor) |
@@ -237,14 +271,8 @@ void LLScrollListText::setText(const LLStringExplicit& text) | |||
237 | mText = text; | 271 | mText = text; |
238 | } | 272 | } |
239 | 273 | ||
240 | void LLScrollListText::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const | 274 | void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_color) const |
241 | { | 275 | { |
242 | // If the user has specified a small minimum width, use that. | ||
243 | if (mWidth > 0 && mWidth < width) | ||
244 | { | ||
245 | width = mWidth; | ||
246 | } | ||
247 | |||
248 | const LLColor4* display_color; | 276 | const LLColor4* display_color; |
249 | if (mColor) | 277 | if (mColor) |
250 | { | 278 | { |
@@ -266,10 +294,10 @@ void LLScrollListText::drawToWidth(S32 width, const LLColor4& color, const LLCol | |||
266 | left = mFont->getWidth(mText.getString(), 0, mHighlightOffset); | 294 | left = mFont->getWidth(mText.getString(), 0, mHighlightOffset); |
267 | break; | 295 | break; |
268 | case LLFontGL::RIGHT: | 296 | case LLFontGL::RIGHT: |
269 | left = width - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX); | 297 | left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX); |
270 | break; | 298 | break; |
271 | case LLFontGL::HCENTER: | 299 | case LLFontGL::HCENTER: |
272 | left = (width - mFont->getWidth(mText.getString())) / 2; | 300 | left = (getWidth() - mFont->getWidth(mText.getString())) / 2; |
273 | break; | 301 | break; |
274 | } | 302 | } |
275 | gl_segmented_rect_2d_tex(left - 2, | 303 | gl_segmented_rect_2d_tex(left - 2, |
@@ -291,10 +319,10 @@ void LLScrollListText::drawToWidth(S32 width, const LLColor4& color, const LLCol | |||
291 | start_x = 0.f; | 319 | start_x = 0.f; |
292 | break; | 320 | break; |
293 | case LLFontGL::RIGHT: | 321 | case LLFontGL::RIGHT: |
294 | start_x = (F32)width; | 322 | start_x = (F32)getWidth(); |
295 | break; | 323 | break; |
296 | case LLFontGL::HCENTER: | 324 | case LLFontGL::HCENTER: |
297 | start_x = (F32)width * 0.5f; | 325 | start_x = (F32)getWidth() * 0.5f; |
298 | break; | 326 | break; |
299 | } | 327 | } |
300 | mFont->render(mText.getWString(), 0, | 328 | mFont->render(mText.getWString(), 0, |
@@ -304,8 +332,10 @@ void LLScrollListText::drawToWidth(S32 width, const LLColor4& color, const LLCol | |||
304 | LLFontGL::BOTTOM, | 332 | LLFontGL::BOTTOM, |
305 | mFontStyle, | 333 | mFontStyle, |
306 | string_chars, | 334 | string_chars, |
307 | width, | 335 | getWidth(), |
308 | &right_x, FALSE, TRUE); | 336 | &right_x, |
337 | FALSE, | ||
338 | TRUE); | ||
309 | } | 339 | } |
310 | 340 | ||
311 | 341 | ||
@@ -314,31 +344,6 @@ LLScrollListItem::~LLScrollListItem() | |||
314 | std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); | 344 | std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); |
315 | } | 345 | } |
316 | 346 | ||
317 | BOOL LLScrollListItem::handleClick(S32 x, S32 y, MASK mask) | ||
318 | { | ||
319 | BOOL handled = FALSE; | ||
320 | |||
321 | S32 left = 0; | ||
322 | S32 right = 0; | ||
323 | S32 width = 0; | ||
324 | |||
325 | std::vector<LLScrollListCell *>::iterator iter = mColumns.begin(); | ||
326 | std::vector<LLScrollListCell *>::iterator end = mColumns.end(); | ||
327 | for ( ; iter != end; ++iter) | ||
328 | { | ||
329 | width = (*iter)->getWidth(); | ||
330 | right += width; | ||
331 | if (left <= x && x < right ) | ||
332 | { | ||
333 | handled = (*iter)->handleClick(); | ||
334 | break; | ||
335 | } | ||
336 | |||
337 | left += width; | ||
338 | } | ||
339 | return handled; | ||
340 | } | ||
341 | |||
342 | void LLScrollListItem::setNumColumns(S32 columns) | 347 | void LLScrollListItem::setNumColumns(S32 columns) |
343 | { | 348 | { |
344 | S32 prev_columns = mColumns.size(); | 349 | S32 prev_columns = mColumns.size(); |
@@ -375,7 +380,7 @@ LLString LLScrollListItem::getContentsCSV() | |||
375 | S32 count = getNumColumns(); | 380 | S32 count = getNumColumns(); |
376 | for (S32 i=0; i<count; ++i) | 381 | for (S32 i=0; i<count; ++i) |
377 | { | 382 | { |
378 | ret += getColumn(i)->getText(); | 383 | ret += getColumn(i)->getValue().asString(); |
379 | if (i < count-1) | 384 | if (i < count-1) |
380 | { | 385 | { |
381 | ret += ", "; | 386 | ret += ", "; |
@@ -385,17 +390,107 @@ LLString LLScrollListItem::getContentsCSV() | |||
385 | return ret; | 390 | return ret; |
386 | } | 391 | } |
387 | 392 | ||
393 | void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) | ||
394 | { | ||
395 | // draw background rect | ||
396 | LLRect bg_rect = rect; | ||
397 | // pad background rectangle to separate it from contents | ||
398 | bg_rect.stretch(LIST_BORDER_PAD, 0); | ||
399 | { | ||
400 | LLGLSNoTexture no_texture; | ||
401 | glColor4fv(bg_color.mV); | ||
402 | gl_rect_2d( bg_rect ); | ||
403 | } | ||
404 | |||
405 | S32 cur_x = rect.mLeft; | ||
406 | S32 num_cols = getNumColumns(); | ||
407 | S32 cur_col = 0; | ||
408 | |||
409 | for (LLScrollListCell* cell = getColumn(0); cur_col < num_cols; cell = getColumn(++cur_col)) | ||
410 | { | ||
411 | // Two ways a cell could be hidden | ||
412 | if (cell->getWidth() < 0 | ||
413 | || !cell->getVisible()) continue; | ||
414 | |||
415 | LLUI::pushMatrix(); | ||
416 | { | ||
417 | LLUI::translate((F32) cur_x, (F32) rect.mBottom, 0.0f); | ||
418 | |||
419 | cell->draw( fg_color, highlight_color ); | ||
420 | } | ||
421 | LLUI::popMatrix(); | ||
422 | |||
423 | cur_x += cell->getWidth() + column_padding; | ||
424 | } | ||
425 | } | ||
426 | |||
427 | |||
388 | void LLScrollListItem::setEnabled(BOOL b) | 428 | void LLScrollListItem::setEnabled(BOOL b) |
389 | { | 429 | { |
390 | if (b != mEnabled) | 430 | mEnabled = b; |
431 | } | ||
432 | |||
433 | //--------------------------------------------------------------------------- | ||
434 | // LLScrollListItemComment | ||
435 | //--------------------------------------------------------------------------- | ||
436 | LLScrollListItemComment::LLScrollListItemComment(const LLString& comment_string, const LLColor4& color) | ||
437 | : LLScrollListItem(FALSE), | ||
438 | mColor(color) | ||
439 | { | ||
440 | addColumn( comment_string, gResMgr->getRes( LLFONT_SANSSERIF_SMALL ) ); | ||
441 | } | ||
442 | |||
443 | void LLScrollListItemComment::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) | ||
444 | { | ||
445 | LLScrollListCell* cell = getColumn(0); | ||
446 | if (cell) | ||
447 | { | ||
448 | // Two ways a cell could be hidden | ||
449 | if (cell->getWidth() < 0 | ||
450 | || !cell->getVisible()) return; | ||
451 | |||
452 | LLUI::pushMatrix(); | ||
453 | { | ||
454 | LLUI::translate((F32)rect.mLeft, (F32)rect.mBottom, 0.0f); | ||
455 | |||
456 | // force first cell to be width of entire item | ||
457 | cell->setWidth(rect.getWidth()); | ||
458 | cell->draw( mColor, highlight_color ); | ||
459 | } | ||
460 | LLUI::popMatrix(); | ||
461 | } | ||
462 | } | ||
463 | |||
464 | //--------------------------------------------------------------------------- | ||
465 | // LLScrollListItemSeparator | ||
466 | //--------------------------------------------------------------------------- | ||
467 | LLScrollListItemSeparator::LLScrollListItemSeparator() | ||
468 | : LLScrollListItem(FALSE) | ||
469 | { | ||
470 | LLScrollListSeparator* cell = new LLScrollListSeparator(0); | ||
471 | setNumColumns(1); | ||
472 | setColumn(0, cell); | ||
473 | } | ||
474 | |||
475 | void LLScrollListItemSeparator::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) | ||
476 | { | ||
477 | //TODO* move LLScrollListSeparator::draw into here and get rid of it | ||
478 | LLScrollListCell* cell = getColumn(0); | ||
479 | if (cell) | ||
391 | { | 480 | { |
392 | std::vector<LLScrollListCell *>::iterator iter = mColumns.begin(); | 481 | // Two ways a cell could be hidden |
393 | std::vector<LLScrollListCell *>::iterator end = mColumns.end(); | 482 | if (cell->getWidth() < 0 |
394 | for ( ; iter != end; ++iter) | 483 | || !cell->getVisible()) return; |
484 | |||
485 | LLUI::pushMatrix(); | ||
395 | { | 486 | { |
396 | (*iter)->setEnabled(b); | 487 | LLUI::translate((F32)rect.mLeft, (F32)rect.mBottom, 0.0f); |
488 | |||
489 | // force first cell to be width of entire item | ||
490 | cell->setWidth(rect.getWidth()); | ||
491 | cell->draw( fg_color, highlight_color ); | ||
397 | } | 492 | } |
398 | mEnabled = b; | 493 | LLUI::popMatrix(); |
399 | } | 494 | } |
400 | } | 495 | } |
401 | 496 | ||
@@ -423,10 +518,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, | |||
423 | mNeedsScroll(FALSE), | 518 | mNeedsScroll(FALSE), |
424 | mCanSelect(TRUE), | 519 | mCanSelect(TRUE), |
425 | mDisplayColumnHeaders(FALSE), | 520 | mDisplayColumnHeaders(FALSE), |
426 | mCollapseEmptyColumns(FALSE), | ||
427 | mIsPopup(FALSE), | ||
428 | mMaxItemCount(INT_MAX), | 521 | mMaxItemCount(INT_MAX), |
429 | //mItemCount(0), | 522 | mMaxContentWidth(0), |
430 | mBackgroundVisible( TRUE ), | 523 | mBackgroundVisible( TRUE ), |
431 | mDrawStripes(TRUE), | 524 | mDrawStripes(TRUE), |
432 | mBgWriteableColor( LLUI::sColorsGroup->getColor( "ScrollBgWriteableColor" ) ), | 525 | mBgWriteableColor( LLUI::sColorsGroup->getColor( "ScrollBgWriteableColor" ) ), |
@@ -443,12 +536,9 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, | |||
443 | mOnSortChangedCallback( NULL ), | 536 | mOnSortChangedCallback( NULL ), |
444 | mHighlightedItem(-1), | 537 | mHighlightedItem(-1), |
445 | mBorder(NULL), | 538 | mBorder(NULL), |
446 | mDefaultColumn("SIMPLE"), | ||
447 | mSearchColumn(0), | 539 | mSearchColumn(0), |
448 | mNumDynamicWidthColumns(0), | 540 | mNumDynamicWidthColumns(0), |
449 | mTotalStaticColumnWidth(0), | 541 | mTotalStaticColumnWidth(0), |
450 | mSortColumn(-1), | ||
451 | mSortAscending(TRUE), | ||
452 | mSorted(TRUE), | 542 | mSorted(TRUE), |
453 | mDirty(FALSE), | 543 | mDirty(FALSE), |
454 | mOriginalSelection(-1), | 544 | mOriginalSelection(-1), |
@@ -457,7 +547,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, | |||
457 | mItemListRect.setOriginAndSize( | 547 | mItemListRect.setOriginAndSize( |
458 | mBorderThickness + LIST_BORDER_PAD, | 548 | mBorderThickness + LIST_BORDER_PAD, |
459 | mBorderThickness + LIST_BORDER_PAD, | 549 | mBorderThickness + LIST_BORDER_PAD, |
460 | mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE, | 550 | mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ), |
461 | mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) ); | 551 | mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) ); |
462 | 552 | ||
463 | updateLineHeight(); | 553 | updateLineHeight(); |
@@ -481,7 +571,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, | |||
481 | mScrollbar->setFollowsTop(); | 571 | mScrollbar->setFollowsTop(); |
482 | mScrollbar->setFollowsBottom(); | 572 | mScrollbar->setFollowsBottom(); |
483 | mScrollbar->setEnabled( TRUE ); | 573 | mScrollbar->setEnabled( TRUE ); |
484 | mScrollbar->setVisible( TRUE ); | 574 | // scrollbar is visible only when needed |
575 | mScrollbar->setVisible(FALSE); | ||
485 | addChild(mScrollbar); | 576 | addChild(mScrollbar); |
486 | 577 | ||
487 | // Border | 578 | // Border |
@@ -539,7 +630,8 @@ void LLScrollListCtrl::clearRows() | |||
539 | 630 | ||
540 | mScrollLines = 0; | 631 | mScrollLines = 0; |
541 | mLastSelected = NULL; | 632 | mLastSelected = NULL; |
542 | updateMaxContentWidth(NULL); | 633 | calcMaxContentWidth(NULL); |
634 | updateLayout(); | ||
543 | mDirty = FALSE; | 635 | mDirty = FALSE; |
544 | } | 636 | } |
545 | 637 | ||
@@ -620,38 +712,64 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const | |||
620 | return ret; | 712 | return ret; |
621 | } | 713 | } |
622 | 714 | ||
715 | // returns first matching item | ||
716 | LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const | ||
717 | { | ||
718 | LLString string_val = sd.asString(); | ||
719 | |||
720 | item_list::const_iterator iter; | ||
721 | for(iter = mItemList.begin(); iter != mItemList.end(); iter++) | ||
722 | { | ||
723 | LLScrollListItem* item = *iter; | ||
724 | // assumes string representation is good enough for comparison | ||
725 | if (item->getValue().asString() == string_val) | ||
726 | { | ||
727 | return item; | ||
728 | } | ||
729 | } | ||
730 | return NULL; | ||
731 | } | ||
732 | |||
623 | 733 | ||
624 | void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) | 734 | void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) |
625 | { | 735 | { |
626 | S32 old_height = mRect.getHeight(); | ||
627 | LLUICtrl::reshape( width, height, called_from_parent ); | 736 | LLUICtrl::reshape( width, height, called_from_parent ); |
628 | 737 | ||
629 | S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); | 738 | updateLayout(); |
739 | } | ||
630 | 740 | ||
741 | void LLScrollListCtrl::updateLayout() | ||
742 | { | ||
743 | // reserve room for column headers, if needed | ||
744 | S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); | ||
631 | mItemListRect.setOriginAndSize( | 745 | mItemListRect.setOriginAndSize( |
632 | mBorderThickness + LIST_BORDER_PAD, | 746 | mBorderThickness + LIST_BORDER_PAD, |
633 | mBorderThickness + LIST_BORDER_PAD, | 747 | mBorderThickness + LIST_BORDER_PAD, |
634 | mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE, | 748 | mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ), |
635 | mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) - heading_size ); | 749 | mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) - heading_size ); |
636 | 750 | ||
751 | // how many lines of content in a single "page" | ||
637 | mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0; | 752 | mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0; |
638 | if(old_height < height && getScrollPos() == mScrollbar->getDocPosMax()) | 753 | BOOL scrollbar_visible = getItemCount() > mPageLines; |
754 | if (scrollbar_visible) | ||
639 | { | 755 | { |
640 | setScrollPos(mScrollbar->getDocPosMax()); | 756 | // provide space on the right for scrollbar |
757 | mItemListRect.mRight = mRect.getWidth() - ( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE; | ||
641 | } | 758 | } |
642 | mScrollbar->setVisible(mPageLines < getItemCount()); | 759 | |
760 | mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); | ||
643 | mScrollbar->setPageSize( mPageLines ); | 761 | mScrollbar->setPageSize( mPageLines ); |
644 | 762 | mScrollbar->setDocSize( getItemCount() ); | |
763 | mScrollbar->setVisible(scrollbar_visible); | ||
764 | |||
645 | updateColumns(); | 765 | updateColumns(); |
646 | } | 766 | } |
647 | 767 | ||
648 | // Attempt to size the control to show all items. | 768 | // Attempt to size the control to show all items. |
649 | // Do not make larger than width or height. | 769 | // Do not make larger than width or height. |
650 | void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) | 770 | void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height) |
651 | { | 771 | { |
652 | S32 height = mLineHeight * (getItemCount() + 1); | 772 | S32 height = llmin( getRequiredRect().getHeight(), max_height ); |
653 | height = llmin( height, max_height ); | ||
654 | |||
655 | S32 width = mRect.getWidth(); | 773 | S32 width = mRect.getWidth(); |
656 | 774 | ||
657 | reshape( width, height ); | 775 | reshape( width, height ); |
@@ -660,14 +778,17 @@ void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) | |||
660 | 778 | ||
661 | LLRect LLScrollListCtrl::getRequiredRect() | 779 | LLRect LLScrollListCtrl::getRequiredRect() |
662 | { | 780 | { |
663 | S32 height = mLineHeight * (getItemCount() + 1); | 781 | S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); |
782 | S32 height = (mLineHeight * getItemCount()) | ||
783 | + (2 * ( mBorderThickness + LIST_BORDER_PAD )) | ||
784 | + heading_size; | ||
664 | S32 width = mRect.getWidth(); | 785 | S32 width = mRect.getWidth(); |
665 | 786 | ||
666 | return LLRect(0, height, width, 0); | 787 | return LLRect(0, height, width, 0); |
667 | } | 788 | } |
668 | 789 | ||
669 | 790 | ||
670 | BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) | 791 | BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL requires_column ) |
671 | { | 792 | { |
672 | BOOL not_too_big = getItemCount() < mMaxItemCount; | 793 | BOOL not_too_big = getItemCount() < mMaxItemCount; |
673 | if (not_too_big) | 794 | if (not_too_big) |
@@ -680,15 +801,22 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) | |||
680 | break; | 801 | break; |
681 | 802 | ||
682 | case ADD_SORTED: | 803 | case ADD_SORTED: |
683 | if (mSortColumn == -1) | ||
684 | { | 804 | { |
685 | mSortColumn = 0; | 805 | // sort by column 0, in ascending order |
686 | mSortAscending = TRUE; | 806 | std::vector<sort_column_t> single_sort_column; |
687 | } | 807 | single_sort_column.push_back(std::make_pair(0, TRUE)); |
688 | mItemList.push_back(item); | 808 | |
689 | std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); | 809 | mItemList.push_back(item); |
690 | break; | 810 | std::stable_sort( |
691 | 811 | mItemList.begin(), | |
812 | mItemList.end(), | ||
813 | SortScrollListItem(single_sort_column)); | ||
814 | |||
815 | // ADD_SORTED just sorts by first column... | ||
816 | // this might not match user sort criteria, so flag list as being in unsorted state | ||
817 | setSorted(FALSE); | ||
818 | break; | ||
819 | } | ||
692 | case ADD_BOTTOM: | 820 | case ADD_BOTTOM: |
693 | mItemList.push_back(item); | 821 | mItemList.push_back(item); |
694 | setSorted(FALSE); | 822 | setSorted(FALSE); |
@@ -701,34 +829,42 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) | |||
701 | break; | 829 | break; |
702 | } | 830 | } |
703 | 831 | ||
704 | updateLineHeightInsert(item); | 832 | // create new column on demand |
705 | mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0; | 833 | if (mColumns.empty() && requires_column) |
706 | BOOL scrollbar_visible = mPageLines < getItemCount(); | ||
707 | |||
708 | if (scrollbar_visible != mScrollbar->getVisible()) | ||
709 | { | 834 | { |
710 | mScrollbar->setVisible(mPageLines < getItemCount()); | 835 | LLSD new_column; |
711 | updateColumns(); | 836 | new_column["name"] = "default_column"; |
837 | new_column["label"] = ""; | ||
838 | new_column["dynamicwidth"] = TRUE; | ||
839 | addColumn(new_column); | ||
712 | } | 840 | } |
713 | mScrollbar->setPageSize( mPageLines ); | ||
714 | |||
715 | mScrollbar->setDocSize( getItemCount() ); | ||
716 | 841 | ||
717 | updateMaxContentWidth(item); | 842 | updateLineHeightInsert(item); |
843 | calcMaxContentWidth(item); | ||
844 | |||
845 | updateLayout(); | ||
718 | } | 846 | } |
719 | 847 | ||
720 | return not_too_big; | 848 | return not_too_big; |
721 | } | 849 | } |
722 | 850 | ||
723 | void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) | 851 | void LLScrollListCtrl::calcMaxContentWidth(LLScrollListItem* added_item) |
724 | { | 852 | { |
725 | const S32 HEADING_TEXT_PADDING = 30; | 853 | const S32 HEADING_TEXT_PADDING = 30; |
726 | const S32 COLUMN_TEXT_PADDING = 20; | 854 | const S32 COLUMN_TEXT_PADDING = 20; |
727 | 855 | ||
728 | std::map<LLString, LLScrollListColumn>::iterator column_itor; | 856 | if (added_item == NULL) |
729 | for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) | ||
730 | { | 857 | { |
731 | LLScrollListColumn* column = &column_itor->second; | 858 | mMaxContentWidth = 0; |
859 | } | ||
860 | |||
861 | S32 item_content_width = 0; | ||
862 | |||
863 | ordered_columns_t::iterator column_itor; | ||
864 | for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) | ||
865 | { | ||
866 | LLScrollListColumn* column = *column_itor; | ||
867 | if (!column) continue; | ||
732 | 868 | ||
733 | if (!added_item) | 869 | if (!added_item) |
734 | { | 870 | { |
@@ -740,7 +876,7 @@ void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) | |||
740 | LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); | 876 | LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); |
741 | if (!cellp) continue; | 877 | if (!cellp) continue; |
742 | 878 | ||
743 | column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); | 879 | column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); |
744 | } | 880 | } |
745 | } | 881 | } |
746 | else | 882 | else |
@@ -748,9 +884,13 @@ void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) | |||
748 | LLScrollListCell* cellp = added_item->getColumn(column->mIndex); | 884 | LLScrollListCell* cellp = added_item->getColumn(column->mIndex); |
749 | if (!cellp) continue; | 885 | if (!cellp) continue; |
750 | 886 | ||
751 | column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); | 887 | column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); |
752 | } | 888 | } |
889 | |||
890 | item_content_width += column->mMaxContentWidth; | ||
753 | } | 891 | } |
892 | |||
893 | mMaxContentWidth = llmax(mMaxContentWidth, item_content_width); | ||
754 | } | 894 | } |
755 | 895 | ||
756 | const S32 SCROLL_LIST_ROW_PAD = 2; | 896 | const S32 SCROLL_LIST_ROW_PAD = 2; |
@@ -789,7 +929,6 @@ void LLScrollListCtrl::updateColumns() | |||
789 | mColumnsIndexed.resize(mColumns.size()); | 929 | mColumnsIndexed.resize(mColumns.size()); |
790 | 930 | ||
791 | std::map<LLString, LLScrollListColumn>::iterator column_itor; | 931 | std::map<LLString, LLScrollListColumn>::iterator column_itor; |
792 | bool first_dynamic = true; | ||
793 | for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) | 932 | for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) |
794 | { | 933 | { |
795 | LLScrollListColumn *column = &column_itor->second; | 934 | LLScrollListColumn *column = &column_itor->second; |
@@ -801,11 +940,6 @@ void LLScrollListCtrl::updateColumns() | |||
801 | else if (column->mDynamicWidth) | 940 | else if (column->mDynamicWidth) |
802 | { | 941 | { |
803 | new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; | 942 | new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; |
804 | if(first_dynamic) | ||
805 | { | ||
806 | first_dynamic = false; | ||
807 | new_width += (mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE); | ||
808 | } | ||
809 | } | 943 | } |
810 | 944 | ||
811 | if (new_width != column->mWidth) | 945 | if (new_width != column->mWidth) |
@@ -854,48 +988,38 @@ void LLScrollListCtrl::updateColumns() | |||
854 | } | 988 | } |
855 | right = llmax(left, llmin(mItemListRect.getWidth(), right)); | 989 | right = llmax(left, llmin(mItemListRect.getWidth(), right)); |
856 | S32 header_width = right - left; | 990 | S32 header_width = right - left; |
857 | 991 | ||
858 | last_header->reshape(header_width, mHeadingHeight); | 992 | last_header->reshape(header_width, mHeadingHeight); |
859 | last_header->translate(left - last_header->getRect().mLeft, top - last_header->getRect().mBottom); | 993 | last_header->translate( |
994 | left - last_header->getRect().mLeft, | ||
995 | top - last_header->getRect().mBottom); | ||
860 | last_header->setVisible(mDisplayColumnHeaders && header_width > 0); | 996 | last_header->setVisible(mDisplayColumnHeaders && header_width > 0); |
861 | left = right; | 997 | left = right; |
862 | } | 998 | } |
863 | } | 999 | } |
864 | 1000 | ||
865 | // expand last column header we encountered to full list width | 1001 | // expand last column header we encountered to full list width |
866 | |||
867 | if (last_header) | 1002 | if (last_header) |
868 | { | 1003 | { |
869 | S32 header_strip_width = mItemListRect.getWidth() + (mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE); | 1004 | S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft); |
870 | S32 new_width = llmax(0, mItemListRect.mLeft + header_strip_width - last_header->getRect().mLeft); | ||
871 | last_header->reshape(new_width, last_header->getRect().getHeight()); | 1005 | last_header->reshape(new_width, last_header->getRect().getHeight()); |
872 | last_header->setVisible(mDisplayColumnHeaders && new_width > 0); | 1006 | last_header->setVisible(mDisplayColumnHeaders && new_width > 0); |
873 | } | 1007 | } |
874 | |||
875 | } | 1008 | } |
876 | 1009 | ||
877 | void LLScrollListCtrl::setDisplayHeading(BOOL display) | 1010 | void LLScrollListCtrl::setDisplayHeading(BOOL display) |
878 | { | 1011 | { |
879 | mDisplayColumnHeaders = display; | 1012 | mDisplayColumnHeaders = display; |
880 | 1013 | ||
881 | updateColumns(); | 1014 | updateLayout(); |
882 | |||
883 | setHeadingHeight(mHeadingHeight); | ||
884 | } | 1015 | } |
885 | 1016 | ||
886 | void LLScrollListCtrl::setHeadingHeight(S32 heading_height) | 1017 | void LLScrollListCtrl::setHeadingHeight(S32 heading_height) |
887 | { | 1018 | { |
888 | mHeadingHeight = heading_height; | 1019 | mHeadingHeight = heading_height; |
889 | 1020 | ||
890 | reshape(mRect.getWidth(), mRect.getHeight()); | 1021 | updateLayout(); |
891 | |||
892 | // Resize | ||
893 | mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); | ||
894 | } | ||
895 | 1022 | ||
896 | void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse) | ||
897 | { | ||
898 | mCollapseEmptyColumns = collapse; | ||
899 | } | 1023 | } |
900 | 1024 | ||
901 | BOOL LLScrollListCtrl::selectFirstItem() | 1025 | BOOL LLScrollListCtrl::selectFirstItem() |
@@ -934,6 +1058,8 @@ BOOL LLScrollListCtrl::selectFirstItem() | |||
934 | 1058 | ||
935 | BOOL LLScrollListCtrl::selectNthItem( S32 target_index ) | 1059 | BOOL LLScrollListCtrl::selectNthItem( S32 target_index ) |
936 | { | 1060 | { |
1061 | if (mItemList.empty()) return FALSE; | ||
1062 | |||
937 | // Deselects all other items | 1063 | // Deselects all other items |
938 | BOOL success = FALSE; | 1064 | BOOL success = FALSE; |
939 | S32 index = 0; | 1065 | S32 index = 0; |
@@ -1012,7 +1138,32 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index) | |||
1012 | } | 1138 | } |
1013 | delete itemp; | 1139 | delete itemp; |
1014 | mItemList.erase(mItemList.begin() + target_index); | 1140 | mItemList.erase(mItemList.begin() + target_index); |
1015 | updateMaxContentWidth(NULL); | 1141 | calcMaxContentWidth(NULL); |
1142 | } | ||
1143 | |||
1144 | //FIXME: refactor item deletion | ||
1145 | void LLScrollListCtrl::deleteItems(const LLSD& sd) | ||
1146 | { | ||
1147 | item_list::iterator iter; | ||
1148 | for (iter = mItemList.begin(); iter < mItemList.end(); ) | ||
1149 | { | ||
1150 | LLScrollListItem* itemp = *iter; | ||
1151 | if (itemp->getValue().asString() == sd.asString()) | ||
1152 | { | ||
1153 | if (itemp == mLastSelected) | ||
1154 | { | ||
1155 | mLastSelected = NULL; | ||
1156 | } | ||
1157 | delete itemp; | ||
1158 | mItemList.erase(iter++); | ||
1159 | } | ||
1160 | else | ||
1161 | { | ||
1162 | iter++; | ||
1163 | } | ||
1164 | } | ||
1165 | |||
1166 | calcMaxContentWidth(NULL); | ||
1016 | } | 1167 | } |
1017 | 1168 | ||
1018 | void LLScrollListCtrl::deleteSelectedItems() | 1169 | void LLScrollListCtrl::deleteSelectedItems() |
@@ -1032,7 +1183,7 @@ void LLScrollListCtrl::deleteSelectedItems() | |||
1032 | } | 1183 | } |
1033 | } | 1184 | } |
1034 | mLastSelected = NULL; | 1185 | mLastSelected = NULL; |
1035 | updateMaxContentWidth(NULL); | 1186 | calcMaxContentWidth(NULL); |
1036 | } | 1187 | } |
1037 | 1188 | ||
1038 | void LLScrollListCtrl::highlightNthItem(S32 target_index) | 1189 | void LLScrollListCtrl::highlightNthItem(S32 target_index) |
@@ -1108,7 +1259,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) | |||
1108 | 1259 | ||
1109 | if (!getFirstSelected()) | 1260 | if (!getFirstSelected()) |
1110 | { | 1261 | { |
1111 | selectFirstItem(); | 1262 | // select last item |
1263 | selectNthItem(getItemCount() - 1); | ||
1112 | } | 1264 | } |
1113 | else | 1265 | else |
1114 | { | 1266 | { |
@@ -1130,7 +1282,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) | |||
1130 | break; | 1282 | break; |
1131 | } | 1283 | } |
1132 | 1284 | ||
1133 | prev_item = cur_item; | 1285 | // don't allow navigation to disabled elements |
1286 | prev_item = cur_item->getEnabled() ? cur_item : prev_item; | ||
1134 | } | 1287 | } |
1135 | } | 1288 | } |
1136 | 1289 | ||
@@ -1145,32 +1298,34 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) | |||
1145 | 1298 | ||
1146 | void LLScrollListCtrl::selectNextItem( BOOL extend_selection) | 1299 | void LLScrollListCtrl::selectNextItem( BOOL extend_selection) |
1147 | { | 1300 | { |
1301 | LLScrollListItem* next_item = NULL; | ||
1302 | |||
1148 | if (!getFirstSelected()) | 1303 | if (!getFirstSelected()) |
1149 | { | 1304 | { |
1150 | selectFirstItem(); | 1305 | selectFirstItem(); |
1151 | } | 1306 | } |
1152 | else | 1307 | else |
1153 | { | 1308 | { |
1154 | item_list::iterator iter; | 1309 | item_list::reverse_iterator iter; |
1155 | for (iter = mItemList.begin(); iter != mItemList.end(); iter++) | 1310 | for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++) |
1156 | { | 1311 | { |
1157 | LLScrollListItem* item = *iter; | 1312 | LLScrollListItem* cur_item = *iter; |
1158 | if (item->getSelected()) | 1313 | |
1314 | if (cur_item->getSelected()) | ||
1159 | { | 1315 | { |
1160 | if (++iter != mItemList.end()) | 1316 | if (next_item) |
1161 | { | 1317 | { |
1162 | LLScrollListItem *next_item = *iter; | 1318 | selectItem(next_item, !extend_selection); |
1163 | if (next_item) | 1319 | } |
1164 | { | 1320 | else |
1165 | selectItem(next_item, !extend_selection); | 1321 | { |
1166 | } | 1322 | reportInvalidInput(); |
1167 | else | ||
1168 | { | ||
1169 | reportInvalidInput(); | ||
1170 | } | ||
1171 | } | 1323 | } |
1172 | break; | 1324 | break; |
1173 | } | 1325 | } |
1326 | |||
1327 | // don't allow navigation to disabled items | ||
1328 | next_item = cur_item->getEnabled() ? cur_item : next_item; | ||
1174 | } | 1329 | } |
1175 | } | 1330 | } |
1176 | 1331 | ||
@@ -1200,27 +1355,31 @@ void LLScrollListCtrl::deselectAllItems(BOOL no_commit_on_change) | |||
1200 | } | 1355 | } |
1201 | 1356 | ||
1202 | /////////////////////////////////////////////////////////////////////////////////////////////////// | 1357 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
1203 | // "Simple" interface: use this when you're creating a list that contains only unique strings, only | 1358 | // Use this to add comment text such as "Searching", which ignores column settings of list |
1204 | // one of which can be selected at a time. | ||
1205 | 1359 | ||
1206 | LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, EAddPosition pos, BOOL enabled) | 1360 | LLScrollListItem* LLScrollListCtrl::addCommentText(const LLString& comment_text, EAddPosition pos) |
1207 | { | 1361 | { |
1208 | LLScrollListItem* item = NULL; | 1362 | LLScrollListItem* item = NULL; |
1209 | if (getItemCount() < mMaxItemCount) | 1363 | if (getItemCount() < mMaxItemCount) |
1210 | { | 1364 | { |
1211 | // simple items have their LLSD data set to their label | 1365 | // simple items have their LLSD data set to their label |
1212 | item = new LLScrollListItem( LLSD(item_text) ); | 1366 | // always draw comment text with "enabled" color |
1213 | item->setEnabled(enabled); | 1367 | item = new LLScrollListItemComment( comment_text, mFgUnselectedColor ); |
1214 | item->addColumn( item_text, gResMgr->getRes( LLFONT_SANSSERIF_SMALL ) ); | 1368 | addItem( item, pos, FALSE ); |
1215 | addItem( item, pos ); | ||
1216 | } | 1369 | } |
1217 | return item; | 1370 | return item; |
1218 | } | 1371 | } |
1219 | 1372 | ||
1373 | LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos) | ||
1374 | { | ||
1375 | LLScrollListItem* item = new LLScrollListItemSeparator(); | ||
1376 | addItem(item, pos, FALSE); | ||
1377 | return item; | ||
1378 | } | ||
1220 | 1379 | ||
1221 | // Selects first enabled item of the given name. | 1380 | // Selects first enabled item of the given name. |
1222 | // Returns false if item not found. | 1381 | // Returns false if item not found. |
1223 | BOOL LLScrollListCtrl::selectSimpleItem(const LLString& label, BOOL case_sensitive) | 1382 | BOOL LLScrollListCtrl::selectItemByLabel(const LLString& label, BOOL case_sensitive) |
1224 | { | 1383 | { |
1225 | //RN: assume no empty items | 1384 | //RN: assume no empty items |
1226 | if (label.empty()) | 1385 | if (label.empty()) |
@@ -1242,7 +1401,7 @@ BOOL LLScrollListCtrl::selectSimpleItem(const LLString& label, BOOL case_sensiti | |||
1242 | { | 1401 | { |
1243 | LLScrollListItem* item = *iter; | 1402 | LLScrollListItem* item = *iter; |
1244 | // Only select enabled items with matching names | 1403 | // Only select enabled items with matching names |
1245 | LLString item_text = item->getColumn(0)->getText(); | 1404 | LLString item_text = item->getColumn(0)->getValue().asString(); |
1246 | if (!case_sensitive) | 1405 | if (!case_sensitive) |
1247 | { | 1406 | { |
1248 | LLString::toLower(item_text); | 1407 | LLString::toLower(item_text); |
@@ -1265,14 +1424,14 @@ BOOL LLScrollListCtrl::selectSimpleItem(const LLString& label, BOOL case_sensiti | |||
1265 | } | 1424 | } |
1266 | 1425 | ||
1267 | 1426 | ||
1268 | BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLString& target, BOOL case_sensitive) | 1427 | BOOL LLScrollListCtrl::selectItemByPrefix(const LLString& target, BOOL case_sensitive) |
1269 | { | 1428 | { |
1270 | return selectSimpleItemByPrefix(utf8str_to_wstring(target), case_sensitive); | 1429 | return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive); |
1271 | } | 1430 | } |
1272 | 1431 | ||
1273 | // Selects first enabled item that has a name where the name's first part matched the target string. | 1432 | // Selects first enabled item that has a name where the name's first part matched the target string. |
1274 | // Returns false if item not found. | 1433 | // Returns false if item not found. |
1275 | BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL case_sensitive) | 1434 | BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sensitive) |
1276 | { | 1435 | { |
1277 | BOOL found = FALSE; | 1436 | BOOL found = FALSE; |
1278 | 1437 | ||
@@ -1288,7 +1447,7 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca | |||
1288 | LLScrollListItem* item = *iter; | 1447 | LLScrollListItem* item = *iter; |
1289 | // Only select enabled items with matching names | 1448 | // Only select enabled items with matching names |
1290 | LLScrollListCell* cellp = item->getColumn(mSearchColumn); | 1449 | LLScrollListCell* cellp = item->getColumn(mSearchColumn); |
1291 | BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getText()[0]) : FALSE; | 1450 | BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE; |
1292 | if (select) | 1451 | if (select) |
1293 | { | 1452 | { |
1294 | selectItem(item); | 1453 | selectItem(item); |
@@ -1315,7 +1474,7 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca | |||
1315 | { | 1474 | { |
1316 | continue; | 1475 | continue; |
1317 | } | 1476 | } |
1318 | LLWString item_label = utf8str_to_wstring(cellp->getText()); | 1477 | LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); |
1319 | if (!case_sensitive) | 1478 | if (!case_sensitive) |
1320 | { | 1479 | { |
1321 | LLWString::toLower(item_label); | 1480 | LLWString::toLower(item_label); |
@@ -1346,14 +1505,14 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca | |||
1346 | return found; | 1505 | return found; |
1347 | } | 1506 | } |
1348 | 1507 | ||
1349 | const LLString& LLScrollListCtrl::getSimpleSelectedItem(S32 column) const | 1508 | const LLString LLScrollListCtrl::getSelectedItemLabel(S32 column) const |
1350 | { | 1509 | { |
1351 | LLScrollListItem* item; | 1510 | LLScrollListItem* item; |
1352 | 1511 | ||
1353 | item = getFirstSelected(); | 1512 | item = getFirstSelected(); |
1354 | if (item) | 1513 | if (item) |
1355 | { | 1514 | { |
1356 | return item->getColumn(column)->getText(); | 1515 | return item->getColumn(column)->getValue().asString(); |
1357 | } | 1516 | } |
1358 | 1517 | ||
1359 | return LLString::null; | 1518 | return LLString::null; |
@@ -1375,20 +1534,6 @@ LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const LLString& item_text, | |||
1375 | return item; | 1534 | return item; |
1376 | } | 1535 | } |
1377 | 1536 | ||
1378 | LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, LLSD sd, EAddPosition pos, BOOL enabled, S32 column_width) | ||
1379 | { | ||
1380 | LLScrollListItem* item = NULL; | ||
1381 | if (getItemCount() < mMaxItemCount) | ||
1382 | { | ||
1383 | item = new LLScrollListItem( sd ); | ||
1384 | item->setEnabled(enabled); | ||
1385 | item->addColumn(item_text, gResMgr->getRes(LLFONT_SANSSERIF_SMALL), column_width); | ||
1386 | addItem( item, pos ); | ||
1387 | } | ||
1388 | return item; | ||
1389 | } | ||
1390 | |||
1391 | |||
1392 | // Select the line or lines that match this UUID | 1537 | // Select the line or lines that match this UUID |
1393 | BOOL LLScrollListCtrl::selectByID( const LLUUID& id ) | 1538 | BOOL LLScrollListCtrl::selectByID( const LLUUID& id ) |
1394 | { | 1539 | { |
@@ -1454,7 +1599,7 @@ LLUUID LLScrollListCtrl::getStringUUIDSelectedItem() | |||
1454 | return LLUUID::null; | 1599 | return LLUUID::null; |
1455 | } | 1600 | } |
1456 | 1601 | ||
1457 | LLSD LLScrollListCtrl::getSimpleSelectedValue() | 1602 | LLSD LLScrollListCtrl::getSelectedValue() |
1458 | { | 1603 | { |
1459 | LLScrollListItem* item = getFirstSelected(); | 1604 | LLScrollListItem* item = getFirstSelected(); |
1460 | 1605 | ||
@@ -1481,28 +1626,28 @@ void LLScrollListCtrl::drawItems() | |||
1481 | LLGLSUIDefault gls_ui; | 1626 | LLGLSUIDefault gls_ui; |
1482 | 1627 | ||
1483 | { | 1628 | { |
1484 | LLRect clip_rect = mItemListRect; | 1629 | LLLocalClipRect clip(mItemListRect); |
1485 | if(!mScrollbar->getVisible()) clip_rect.mRight += SCROLLBAR_SIZE; | ||
1486 | LLLocalClipRect clip(clip_rect); | ||
1487 | 1630 | ||
1488 | S32 cur_x = x; | ||
1489 | S32 cur_y = y; | 1631 | S32 cur_y = y; |
1490 | 1632 | ||
1491 | mDrewSelected = FALSE; | 1633 | mDrewSelected = FALSE; |
1492 | 1634 | ||
1493 | S32 line = 0; | 1635 | S32 line = 0; |
1494 | LLColor4 color; | ||
1495 | S32 max_columns = 0; | 1636 | S32 max_columns = 0; |
1496 | 1637 | ||
1638 | LLColor4 highlight_color = LLColor4::white; | ||
1639 | F32 type_ahead_timeout = LLUI::sConfigGroup->getF32("TypeAheadTimeout"); | ||
1640 | highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f); | ||
1641 | |||
1497 | item_list::iterator iter; | 1642 | item_list::iterator iter; |
1498 | for (iter = mItemList.begin(); iter != mItemList.end(); iter++) | 1643 | for (iter = mItemList.begin(); iter != mItemList.end(); iter++) |
1499 | { | 1644 | { |
1500 | LLScrollListItem* item = *iter; | 1645 | LLScrollListItem* item = *iter; |
1501 | 1646 | ||
1502 | item_rect.setOriginAndSize( | 1647 | item_rect.setOriginAndSize( |
1503 | cur_x, | 1648 | x, |
1504 | cur_y, | 1649 | cur_y, |
1505 | mScrollbar->getVisible() ? mItemListRect.getWidth() : mItemListRect.getWidth() + SCROLLBAR_SIZE, | 1650 | mItemListRect.getWidth(), |
1506 | mLineHeight ); | 1651 | mLineHeight ); |
1507 | 1652 | ||
1508 | //llinfos << item_rect.getWidth() << llendl; | 1653 | //llinfos << item_rect.getWidth() << llendl; |
@@ -1514,85 +1659,37 @@ void LLScrollListCtrl::drawItems() | |||
1514 | 1659 | ||
1515 | max_columns = llmax(max_columns, item->getNumColumns()); | 1660 | max_columns = llmax(max_columns, item->getNumColumns()); |
1516 | 1661 | ||
1517 | LLRect bg_rect = item_rect; | 1662 | LLColor4 fg_color; |
1518 | // pad background rectangle to separate it from contents | 1663 | LLColor4 bg_color(LLColor4::transparent); |
1519 | bg_rect.stretch(LIST_BORDER_PAD, 0); | ||
1520 | 1664 | ||
1521 | if( mScrollLines <= line && line < mScrollLines + num_page_lines ) | 1665 | if( mScrollLines <= line && line < mScrollLines + num_page_lines ) |
1522 | { | 1666 | { |
1667 | fg_color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); | ||
1523 | if( item->getSelected() && mCanSelect) | 1668 | if( item->getSelected() && mCanSelect) |
1524 | { | 1669 | { |
1525 | // Draw background of selected item | 1670 | // Draw background of selected item |
1526 | LLGLSNoTexture no_texture; | 1671 | bg_color = mBgSelectedColor; |
1527 | glColor4fv(mBgSelectedColor.mV); | 1672 | fg_color = (item->getEnabled() ? mFgSelectedColor : mFgDisabledColor); |
1528 | gl_rect_2d( bg_rect ); | ||
1529 | |||
1530 | color = mFgSelectedColor; | ||
1531 | } | 1673 | } |
1532 | else if (mHighlightedItem == line && mCanSelect) | 1674 | else if (mHighlightedItem == line && mCanSelect) |
1533 | { | 1675 | { |
1534 | LLGLSNoTexture no_texture; | 1676 | bg_color = mHighlightedColor; |
1535 | glColor4fv(mHighlightedColor.mV); | ||
1536 | gl_rect_2d( bg_rect ); | ||
1537 | color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); | ||
1538 | } | 1677 | } |
1539 | else | 1678 | else |
1540 | { | 1679 | { |
1541 | color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); | 1680 | if (mDrawStripes && (line % 2 == 0) && (max_columns > 1)) |
1542 | if (mDrawStripes && (line%2 == 0) && (max_columns > 1)) | ||
1543 | { | 1681 | { |
1544 | LLGLSNoTexture no_texture; | 1682 | bg_color = mBgStripeColor; |
1545 | glColor4fv(mBgStripeColor.mV); | ||
1546 | gl_rect_2d( bg_rect ); | ||
1547 | } | 1683 | } |
1548 | } | 1684 | } |
1549 | 1685 | ||
1550 | S32 line_x = cur_x; | 1686 | if (!item->getEnabled()) |
1551 | { | 1687 | { |
1552 | S32 num_cols = item->getNumColumns(); | 1688 | bg_color = mBgReadOnlyColor; |
1553 | S32 cur_col = 0; | 1689 | } |
1554 | S32 dynamic_width = 0; | ||
1555 | S32 dynamic_remainder = 0; | ||
1556 | bool first_dynamic = true; | ||
1557 | if(mNumDynamicWidthColumns > 0) | ||
1558 | { | ||
1559 | dynamic_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; | ||
1560 | dynamic_remainder = (mItemListRect.getWidth() - mTotalStaticColumnWidth) % mNumDynamicWidthColumns; | ||
1561 | } | ||
1562 | 1690 | ||
1563 | for (LLScrollListCell* cell = item->getColumn(0); cur_col < num_cols; cell = item->getColumn(++cur_col)) | 1691 | item->draw(item_rect, fg_color, bg_color, highlight_color, mColumnPadding); |
1564 | { | ||
1565 | S32 cell_width = cell->getWidth(); | ||
1566 | |||
1567 | if(mColumnsIndexed.size() > (U32)cur_col && mColumnsIndexed[cur_col] && mColumnsIndexed[cur_col]->mDynamicWidth) | ||
1568 | { | ||
1569 | cell_width = dynamic_width + (--dynamic_remainder ? 1 : 0); | ||
1570 | if(first_dynamic) | ||
1571 | { | ||
1572 | cell_width += mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE; | ||
1573 | first_dynamic = false; | ||
1574 | } | ||
1575 | cell->setWidth(cell_width); | ||
1576 | } | ||
1577 | // Two ways a cell could be hidden | ||
1578 | if (cell_width < 0 | ||
1579 | || !cell->getVisible()) continue; | ||
1580 | |||
1581 | LLUI::pushMatrix(); | ||
1582 | LLUI::translate((F32) cur_x, (F32) cur_y, 0.0f); | ||
1583 | S32 space_left = mItemListRect.mRight - cur_x; | ||
1584 | LLColor4 highlight_color = LLColor4::white; | ||
1585 | F32 type_ahead_timeout = LLUI::sConfigGroup->getF32("TypeAheadTimeout"); | ||
1586 | |||
1587 | highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f); | ||
1588 | cell->drawToWidth( space_left, color, highlight_color ); | ||
1589 | LLUI::popMatrix(); | ||
1590 | |||
1591 | cur_x += cell_width + mColumnPadding; | ||
1592 | 1692 | ||
1593 | } | ||
1594 | } | ||
1595 | cur_x = line_x; | ||
1596 | cur_y -= mLineHeight; | 1693 | cur_y -= mLineHeight; |
1597 | } | 1694 | } |
1598 | line++; | 1695 | line++; |
@@ -1605,6 +1702,12 @@ void LLScrollListCtrl::draw() | |||
1605 | { | 1702 | { |
1606 | if( getVisible() ) | 1703 | if( getVisible() ) |
1607 | { | 1704 | { |
1705 | // if user specifies sort, make sure it is maintained | ||
1706 | if (needsSorting() && !isSorted()) | ||
1707 | { | ||
1708 | sortItems(); | ||
1709 | } | ||
1710 | |||
1608 | if (mNeedsScroll) | 1711 | if (mNeedsScroll) |
1609 | { | 1712 | { |
1610 | scrollToShowSelected(); | 1713 | scrollToShowSelected(); |
@@ -1645,6 +1748,54 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) | |||
1645 | return handled; | 1748 | return handled; |
1646 | } | 1749 | } |
1647 | 1750 | ||
1751 | BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) | ||
1752 | { | ||
1753 | S32 column_index = getColumnIndexFromOffset(x); | ||
1754 | LLScrollListColumn* columnp = getColumn(column_index); | ||
1755 | |||
1756 | if (columnp == NULL) return FALSE; | ||
1757 | |||
1758 | BOOL handled = FALSE; | ||
1759 | // show tooltip for full name of hovered item if it has been truncated | ||
1760 | LLScrollListItem* hit_item = hitItem(x, y); | ||
1761 | if (hit_item) | ||
1762 | { | ||
1763 | LLScrollListCell* hit_cell = hit_item->getColumn(column_index); | ||
1764 | if (!hit_cell) return FALSE; | ||
1765 | S32 cell_required_width = hit_cell->getContentWidth(); | ||
1766 | if (hit_cell | ||
1767 | && hit_cell->isText() | ||
1768 | && cell_required_width > columnp->mWidth) | ||
1769 | { | ||
1770 | |||
1771 | S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; | ||
1772 | S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item)); | ||
1773 | LLRect cell_rect; | ||
1774 | cell_rect.setOriginAndSize(rect_left, rect_bottom, rect_left + columnp->mWidth, mLineHeight); | ||
1775 | // Convert rect local to screen coordinates | ||
1776 | localPointToScreen( | ||
1777 | cell_rect.mLeft, cell_rect.mBottom, | ||
1778 | &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); | ||
1779 | localPointToScreen( | ||
1780 | cell_rect.mRight, cell_rect.mTop, | ||
1781 | &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); | ||
1782 | |||
1783 | msg = hit_cell->getValue().asString(); | ||
1784 | handled = TRUE; | ||
1785 | } | ||
1786 | } | ||
1787 | |||
1788 | // otherwise, look for a tooltip associated with this column | ||
1789 | LLColumnHeader* headerp = columnp->mHeader; | ||
1790 | if (headerp && !handled) | ||
1791 | { | ||
1792 | headerp->handleToolTip(x, y, msg, sticky_rect_screen); | ||
1793 | handled = !msg.empty(); | ||
1794 | } | ||
1795 | |||
1796 | return handled; | ||
1797 | } | ||
1798 | |||
1648 | BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) | 1799 | BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) |
1649 | { | 1800 | { |
1650 | if (!mCanSelect) return FALSE; | 1801 | if (!mCanSelect) return FALSE; |
@@ -1652,6 +1803,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) | |||
1652 | BOOL selection_changed = FALSE; | 1803 | BOOL selection_changed = FALSE; |
1653 | 1804 | ||
1654 | LLScrollListItem* hit_item = hitItem(x, y); | 1805 | LLScrollListItem* hit_item = hitItem(x, y); |
1806 | |||
1655 | if( hit_item ) | 1807 | if( hit_item ) |
1656 | { | 1808 | { |
1657 | if( mAllowMultipleSelection ) | 1809 | if( mAllowMultipleSelection ) |
@@ -1686,6 +1838,11 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) | |||
1686 | { | 1838 | { |
1687 | selectItem(item, FALSE); | 1839 | selectItem(item, FALSE); |
1688 | selecting = !selecting; | 1840 | selecting = !selecting; |
1841 | if (hit_item == lastSelected) | ||
1842 | { | ||
1843 | // stop selecting now, since we just clicked on our last selected item | ||
1844 | selecting = FALSE; | ||
1845 | } | ||
1689 | } | 1846 | } |
1690 | if (selecting) | 1847 | if (selecting) |
1691 | { | 1848 | { |
@@ -1726,9 +1883,6 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) | |||
1726 | selectItem(hit_item); | 1883 | selectItem(hit_item); |
1727 | } | 1884 | } |
1728 | 1885 | ||
1729 | hit_item->handleClick(x - mBorderThickness - LIST_BORDER_PAD, | ||
1730 | 1, mask); | ||
1731 | |||
1732 | selection_changed = mSelectionChanged; | 1886 | selection_changed = mSelectionChanged; |
1733 | if (mCommitOnSelectionChange) | 1887 | if (mCommitOnSelectionChange) |
1734 | { | 1888 | { |
@@ -1750,19 +1904,17 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) | |||
1750 | 1904 | ||
1751 | BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) | 1905 | BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) |
1752 | { | 1906 | { |
1753 | BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; | 1907 | BOOL handled = childrenHandleMouseDown(x, y, mask) != NULL; |
1754 | 1908 | ||
1755 | if( !handled ) | 1909 | if( !handled ) |
1756 | { | 1910 | { |
1757 | // set keyboard focus first, in case click action wants to move focus elsewhere | 1911 | // set keyboard focus first, in case click action wants to move focus elsewhere |
1758 | setFocus(TRUE); | 1912 | setFocus(TRUE); |
1759 | 1913 | ||
1760 | // clear selection changed flag so because user is starting a selection operation | 1914 | // clear selection changed flag because user is starting a selection operation |
1761 | mSelectionChanged = FALSE; | 1915 | mSelectionChanged = FALSE; |
1762 | 1916 | ||
1763 | gFocusMgr.setMouseCapture(this); | 1917 | handleClick(x, y, mask); |
1764 | selectItemAt(x, y, mask); | ||
1765 | mNeedsScroll = TRUE; | ||
1766 | } | 1918 | } |
1767 | 1919 | ||
1768 | return TRUE; | 1920 | return TRUE; |
@@ -1798,19 +1950,79 @@ BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) | |||
1798 | //BOOL handled = FALSE; | 1950 | //BOOL handled = FALSE; |
1799 | if(getVisible()) | 1951 | if(getVisible()) |
1800 | { | 1952 | { |
1801 | // Offer the click to the children, even if we aren't enabled | 1953 | BOOL handled = handleClick(x, y, mask); |
1802 | // so the scroll bars will work. | 1954 | |
1803 | if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) | 1955 | if (!handled) |
1804 | { | 1956 | { |
1805 | if( mCanSelect && mOnDoubleClickCallback ) | 1957 | // Offer the click to the children, even if we aren't enabled |
1958 | // so the scroll bars will work. | ||
1959 | if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) | ||
1806 | { | 1960 | { |
1807 | mOnDoubleClickCallback( mCallbackUserData ); | 1961 | if( mCanSelect && mOnDoubleClickCallback ) |
1962 | { | ||
1963 | mOnDoubleClickCallback( mCallbackUserData ); | ||
1964 | } | ||
1808 | } | 1965 | } |
1809 | } | 1966 | } |
1810 | } | 1967 | } |
1811 | return TRUE; | 1968 | return TRUE; |
1812 | } | 1969 | } |
1813 | 1970 | ||
1971 | BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) | ||
1972 | { | ||
1973 | // which row was clicked on? | ||
1974 | LLScrollListItem* hit_item = hitItem(x, y); | ||
1975 | if (!hit_item) return FALSE; | ||
1976 | |||
1977 | // get appropriate cell from that row | ||
1978 | S32 column_index = getColumnIndexFromOffset(x); | ||
1979 | LLScrollListCell* hit_cell = hit_item->getColumn(column_index); | ||
1980 | if (!hit_cell) return FALSE; | ||
1981 | |||
1982 | // if cell handled click directly (i.e. clicked on an embedded checkbox) | ||
1983 | if (hit_cell->handleClick()) | ||
1984 | { | ||
1985 | // if item not currently selected, select it | ||
1986 | if (!hit_item->getSelected()) | ||
1987 | { | ||
1988 | selectItemAt(x, y, mask); | ||
1989 | gFocusMgr.setMouseCapture(this); | ||
1990 | mNeedsScroll = TRUE; | ||
1991 | } | ||
1992 | // otherwise we already have this item selected | ||
1993 | // so propagate state of cell to rest of selected column | ||
1994 | else | ||
1995 | { | ||
1996 | // propagate value of this cell to other selected items | ||
1997 | // and commit the respective widgets | ||
1998 | LLSD item_value = hit_cell->getValue(); | ||
1999 | for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) | ||
2000 | { | ||
2001 | LLScrollListItem* item = *iter; | ||
2002 | if (item->getSelected()) | ||
2003 | { | ||
2004 | LLScrollListCell* cellp = item->getColumn(column_index); | ||
2005 | cellp->setValue(item_value); | ||
2006 | cellp->onCommit(); | ||
2007 | } | ||
2008 | } | ||
2009 | //FIXME: find a better way to signal cell changes | ||
2010 | onCommit(); | ||
2011 | } | ||
2012 | // eat click (e.g. do not trigger double click callback) | ||
2013 | return TRUE; | ||
2014 | } | ||
2015 | else | ||
2016 | { | ||
2017 | // treat this as a normal single item selection | ||
2018 | selectItemAt(x, y, mask); | ||
2019 | gFocusMgr.setMouseCapture(this); | ||
2020 | mNeedsScroll = TRUE; | ||
2021 | // do not eat click (allow double click callback) | ||
2022 | return FALSE; | ||
2023 | } | ||
2024 | } | ||
2025 | |||
1814 | LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) | 2026 | LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) |
1815 | { | 2027 | { |
1816 | // Excludes disabled items. | 2028 | // Excludes disabled items. |
@@ -1847,6 +2059,59 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) | |||
1847 | return hit_item; | 2059 | return hit_item; |
1848 | } | 2060 | } |
1849 | 2061 | ||
2062 | S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x) | ||
2063 | { | ||
2064 | // which column did we hit? | ||
2065 | S32 left = 0; | ||
2066 | S32 right = 0; | ||
2067 | S32 width = 0; | ||
2068 | S32 column_index = 0; | ||
2069 | |||
2070 | ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); | ||
2071 | ordered_columns_t::const_iterator end = mColumnsIndexed.end(); | ||
2072 | for ( ; iter != end; ++iter) | ||
2073 | { | ||
2074 | width = (*iter)->mWidth + mColumnPadding; | ||
2075 | right += width; | ||
2076 | if (left <= x && x < right ) | ||
2077 | { | ||
2078 | break; | ||
2079 | } | ||
2080 | |||
2081 | // set left for next column as right of current column | ||
2082 | left = right; | ||
2083 | column_index++; | ||
2084 | } | ||
2085 | |||
2086 | return llclamp(column_index, 0, getNumColumns() - 1); | ||
2087 | } | ||
2088 | |||
2089 | |||
2090 | S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index) | ||
2091 | { | ||
2092 | S32 column_offset = 0; | ||
2093 | ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); | ||
2094 | ordered_columns_t::const_iterator end = mColumnsIndexed.end(); | ||
2095 | for ( ; iter != end; ++iter) | ||
2096 | { | ||
2097 | if (index-- <= 0) | ||
2098 | { | ||
2099 | return column_offset; | ||
2100 | } | ||
2101 | column_offset += (*iter)->mWidth + mColumnPadding; | ||
2102 | } | ||
2103 | |||
2104 | // when running off the end, return the rightmost pixel | ||
2105 | return mItemListRect.mRight; | ||
2106 | } | ||
2107 | |||
2108 | S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index) | ||
2109 | { | ||
2110 | S32 row_bottom = ((mItemListRect.mTop - (index - mScrollLines)) * mLineHeight) | ||
2111 | - mLineHeight; | ||
2112 | return row_bottom; | ||
2113 | } | ||
2114 | |||
1850 | 2115 | ||
1851 | BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) | 2116 | BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) |
1852 | { | 2117 | { |
@@ -1860,7 +2125,8 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) | |||
1860 | mNeedsScroll = TRUE; | 2125 | mNeedsScroll = TRUE; |
1861 | } | 2126 | } |
1862 | } | 2127 | } |
1863 | else if (mCanSelect) | 2128 | else |
2129 | if (mCanSelect) | ||
1864 | { | 2130 | { |
1865 | LLScrollListItem* item = hitItem(x, y); | 2131 | LLScrollListItem* item = hitItem(x, y); |
1866 | if (item) | 2132 | if (item) |
@@ -1875,13 +2141,6 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) | |||
1875 | 2141 | ||
1876 | handled = LLUICtrl::handleHover( x, y, mask ); | 2142 | handled = LLUICtrl::handleHover( x, y, mask ); |
1877 | 2143 | ||
1878 | //if( !handled ) | ||
1879 | //{ | ||
1880 | // // Opaque | ||
1881 | // getWindow()->setCursor(UI_CURSOR_ARROW); | ||
1882 | // lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; | ||
1883 | // handled = TRUE; | ||
1884 | //} | ||
1885 | return handled; | 2144 | return handled; |
1886 | } | 2145 | } |
1887 | 2146 | ||
@@ -1998,7 +2257,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent | |||
1998 | } | 2257 | } |
1999 | } | 2258 | } |
2000 | } | 2259 | } |
2001 | else if (selectSimpleItemByPrefix(wstring_to_utf8str(mSearchString), FALSE)) | 2260 | else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), FALSE)) |
2002 | { | 2261 | { |
2003 | mNeedsScroll = TRUE; | 2262 | mNeedsScroll = TRUE; |
2004 | // update search string only on successful match | 2263 | // update search string only on successful match |
@@ -2037,7 +2296,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_ | |||
2037 | // type ahead search is case insensitive | 2296 | // type ahead search is case insensitive |
2038 | uni_char = LLStringOps::toLower((llwchar)uni_char); | 2297 | uni_char = LLStringOps::toLower((llwchar)uni_char); |
2039 | 2298 | ||
2040 | if (selectSimpleItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE)) | 2299 | if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE)) |
2041 | { | 2300 | { |
2042 | // update search string only on successful match | 2301 | // update search string only on successful match |
2043 | mNeedsScroll = TRUE; | 2302 | mNeedsScroll = TRUE; |
@@ -2082,7 +2341,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_ | |||
2082 | if (cellp) | 2341 | if (cellp) |
2083 | { | 2342 | { |
2084 | // Only select enabled items with matching first characters | 2343 | // Only select enabled items with matching first characters |
2085 | LLWString item_label = utf8str_to_wstring(cellp->getText()); | 2344 | LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); |
2086 | if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) | 2345 | if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) |
2087 | { | 2346 | { |
2088 | selectItem(item); | 2347 | selectItem(item); |
@@ -2176,7 +2435,7 @@ void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) | |||
2176 | LLScrollListCell* cellp = itemp->getColumn(mSearchColumn); | 2435 | LLScrollListCell* cellp = itemp->getColumn(mSearchColumn); |
2177 | if (cellp) | 2436 | if (cellp) |
2178 | { | 2437 | { |
2179 | cellp->highlightText(0, 0); | 2438 | cellp->highlightText(0, 0); |
2180 | } | 2439 | } |
2181 | mSelectionChanged = TRUE; | 2440 | mSelectionChanged = TRUE; |
2182 | } | 2441 | } |
@@ -2202,38 +2461,52 @@ BOOL LLScrollListCtrl::isSorted() | |||
2202 | return mSorted; | 2461 | return mSorted; |
2203 | } | 2462 | } |
2204 | 2463 | ||
2205 | // Called by scrollbar | 2464 | struct SameSortColumn |
2206 | //static | ||
2207 | void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) | ||
2208 | { | 2465 | { |
2209 | LLScrollListCtrl* self = (LLScrollListCtrl*) userdata; | 2466 | SameSortColumn(S32 column) : mColumn(column) {} |
2210 | self->mScrollLines = new_pos; | 2467 | S32 mColumn; |
2211 | } | ||
2212 | 2468 | ||
2469 | bool operator()(std::pair<S32, BOOL> sort_column) { return sort_column.first == mColumn; } | ||
2470 | }; | ||
2213 | 2471 | ||
2214 | // First column is column 0 | 2472 | BOOL LLScrollListCtrl::setSort(S32 column, BOOL ascending) |
2215 | void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) | ||
2216 | { | 2473 | { |
2217 | if (!mSorted || mSortColumn != column) | 2474 | sort_column_t new_sort_column(column, ascending); |
2475 | |||
2476 | if (mSortColumns.empty()) | ||
2218 | { | 2477 | { |
2219 | mSortColumn = column; | 2478 | mSortColumns.push_back(new_sort_column); |
2220 | std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); | 2479 | return TRUE; |
2221 | setSorted(TRUE); | ||
2222 | } | 2480 | } |
2481 | else | ||
2482 | { | ||
2483 | // grab current sort column | ||
2484 | sort_column_t cur_sort_column = mSortColumns.back(); | ||
2485 | |||
2486 | // remove any existing sort criterion referencing this column | ||
2487 | // and add the new one | ||
2488 | mSortColumns.erase(remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column)), mSortColumns.end()); | ||
2489 | mSortColumns.push_back(new_sort_column); | ||
2223 | 2490 | ||
2224 | // just reverse the list if changing sort order | 2491 | // did the sort criteria change? |
2225 | if(mSortAscending != ascending) | 2492 | return (cur_sort_column != new_sort_column); |
2226 | { | ||
2227 | std::reverse(mItemList.begin(), mItemList.end()); | ||
2228 | mSortAscending = ascending; | ||
2229 | } | 2493 | } |
2230 | } | 2494 | } |
2231 | 2495 | ||
2496 | // Called by scrollbar | ||
2497 | //static | ||
2498 | void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) | ||
2499 | { | ||
2500 | LLScrollListCtrl* self = (LLScrollListCtrl*) userdata; | ||
2501 | self->mScrollLines = new_pos; | ||
2502 | } | ||
2503 | |||
2504 | |||
2232 | void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) | 2505 | void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) |
2233 | { | 2506 | { |
2234 | if (name.empty()) | 2507 | if (name.empty()) |
2235 | { | 2508 | { |
2236 | sortByColumn(mSortColumn, mSortAscending); | 2509 | sortItems(); |
2237 | return; | 2510 | return; |
2238 | } | 2511 | } |
2239 | 2512 | ||
@@ -2244,6 +2517,26 @@ void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) | |||
2244 | } | 2517 | } |
2245 | } | 2518 | } |
2246 | 2519 | ||
2520 | // First column is column 0 | ||
2521 | void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) | ||
2522 | { | ||
2523 | if (setSort(column, ascending)) | ||
2524 | { | ||
2525 | sortItems(); | ||
2526 | } | ||
2527 | } | ||
2528 | |||
2529 | void LLScrollListCtrl::sortItems() | ||
2530 | { | ||
2531 | // do stable sort to preserve any previous sorts | ||
2532 | std::stable_sort( | ||
2533 | mItemList.begin(), | ||
2534 | mItemList.end(), | ||
2535 | SortScrollListItem(mSortColumns)); | ||
2536 | |||
2537 | setSorted(TRUE); | ||
2538 | } | ||
2539 | |||
2247 | S32 LLScrollListCtrl::getScrollPos() | 2540 | S32 LLScrollListCtrl::getScrollPos() |
2248 | { | 2541 | { |
2249 | return mScrollbar->getDocPos(); | 2542 | return mScrollbar->getDocPos(); |
@@ -2431,9 +2724,6 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2431 | BOOL draw_heading = FALSE; | 2724 | BOOL draw_heading = FALSE; |
2432 | node->getAttributeBOOL("draw_heading", draw_heading); | 2725 | node->getAttributeBOOL("draw_heading", draw_heading); |
2433 | 2726 | ||
2434 | BOOL collapse_empty_columns = FALSE; | ||
2435 | node->getAttributeBOOL("collapse_empty_columns", collapse_empty_columns); | ||
2436 | |||
2437 | S32 search_column = 0; | 2727 | S32 search_column = 0; |
2438 | node->getAttributeS32("search_column", search_column); | 2728 | node->getAttributeS32("search_column", search_column); |
2439 | 2729 | ||
@@ -2454,7 +2744,6 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2454 | node->getAttributeS32("heading_height", heading_height); | 2744 | node->getAttributeS32("heading_height", heading_height); |
2455 | scroll_list->setHeadingHeight(heading_height); | 2745 | scroll_list->setHeadingHeight(heading_height); |
2456 | } | 2746 | } |
2457 | scroll_list->setCollapseEmptyColumns(collapse_empty_columns); | ||
2458 | 2747 | ||
2459 | scroll_list->setScrollListParameters(node); | 2748 | scroll_list->setScrollListParameters(node); |
2460 | 2749 | ||
@@ -2465,7 +2754,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2465 | LLSD columns; | 2754 | LLSD columns; |
2466 | S32 index = 0; | 2755 | S32 index = 0; |
2467 | LLXMLNodePtr child; | 2756 | LLXMLNodePtr child; |
2468 | S32 total_static = 0, num_dynamic = 0; | 2757 | S32 total_static = 0; |
2469 | for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) | 2758 | for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) |
2470 | { | 2759 | { |
2471 | if (child->hasName("column")) | 2760 | if (child->hasName("column")) |
@@ -2491,8 +2780,10 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2491 | S32 columnwidth = -1; | 2780 | S32 columnwidth = -1; |
2492 | child->getAttributeS32("width", columnwidth); | 2781 | child->getAttributeS32("width", columnwidth); |
2493 | 2782 | ||
2494 | if(!columndynamicwidth) total_static += columnwidth; | 2783 | LLString tooltip; |
2495 | else ++num_dynamic; | 2784 | child->getAttributeString("tool_tip", tooltip); |
2785 | |||
2786 | if(!columndynamicwidth) total_static += llmax(0, columnwidth); | ||
2496 | 2787 | ||
2497 | F32 columnrelwidth = 0.f; | 2788 | F32 columnrelwidth = 0.f; |
2498 | child->getAttributeF32("relwidth", columnrelwidth); | 2789 | child->getAttributeF32("relwidth", columnrelwidth); |
@@ -2509,10 +2800,11 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2509 | columns[index]["relwidth"] = columnrelwidth; | 2800 | columns[index]["relwidth"] = columnrelwidth; |
2510 | columns[index]["dynamicwidth"] = columndynamicwidth; | 2801 | columns[index]["dynamicwidth"] = columndynamicwidth; |
2511 | columns[index]["halign"] = (S32)h_align; | 2802 | columns[index]["halign"] = (S32)h_align; |
2803 | columns[index]["tool_tip"] = tooltip; | ||
2804 | |||
2512 | index++; | 2805 | index++; |
2513 | } | 2806 | } |
2514 | } | 2807 | } |
2515 | scroll_list->setNumDynamicColumns(num_dynamic); | ||
2516 | scroll_list->setTotalStaticColumnWidth(total_static); | 2808 | scroll_list->setTotalStaticColumnWidth(total_static); |
2517 | scroll_list->setColumnHeadings(columns); | 2809 | scroll_list->setColumnHeadings(columns); |
2518 | 2810 | ||
@@ -2566,7 +2858,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2566 | while(token_iter != tokens.end()) | 2858 | while(token_iter != tokens.end()) |
2567 | { | 2859 | { |
2568 | const char* line = token_iter->c_str(); | 2860 | const char* line = token_iter->c_str(); |
2569 | scroll_list->addSimpleItem(line); | 2861 | scroll_list->addSimpleElement(line); |
2570 | ++token_iter; | 2862 | ++token_iter; |
2571 | } | 2863 | } |
2572 | } | 2864 | } |
@@ -2663,10 +2955,6 @@ BOOL LLScrollListCtrl::canDeselect() | |||
2663 | void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) | 2955 | void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) |
2664 | { | 2956 | { |
2665 | LLString name = column["name"].asString(); | 2957 | LLString name = column["name"].asString(); |
2666 | if (mColumns.empty()) | ||
2667 | { | ||
2668 | mDefaultColumn = 0; | ||
2669 | } | ||
2670 | // if no column name provided, just use ordinal as name | 2958 | // if no column name provided, just use ordinal as name |
2671 | if (name.empty()) | 2959 | if (name.empty()) |
2672 | { | 2960 | { |
@@ -2691,6 +2979,7 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) | |||
2691 | } | 2979 | } |
2692 | else if(new_column->mDynamicWidth) | 2980 | else if(new_column->mDynamicWidth) |
2693 | { | 2981 | { |
2982 | mNumDynamicWidthColumns++; | ||
2694 | new_column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; | 2983 | new_column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; |
2695 | } | 2984 | } |
2696 | S32 top = mItemListRect.mTop; | 2985 | S32 top = mItemListRect.mTop; |
@@ -2724,19 +3013,19 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) | |||
2724 | new_column->mHeader->setLabel(new_column->mLabel); | 3013 | new_column->mHeader->setLabel(new_column->mLabel); |
2725 | //new_column->mHeader->setLabel(new_column->mLabel); | 3014 | //new_column->mHeader->setLabel(new_column->mLabel); |
2726 | } | 3015 | } |
3016 | |||
3017 | new_column->mHeader->setToolTip(column["tool_tip"].asString()); | ||
3018 | |||
2727 | //RN: although it might be useful to change sort order with the keyboard, | 3019 | //RN: although it might be useful to change sort order with the keyboard, |
2728 | // mixing tab stops on child items along with the parent item is not supported yet | 3020 | // mixing tab stops on child items along with the parent item is not supported yet |
2729 | new_column->mHeader->setTabStop(FALSE); | 3021 | new_column->mHeader->setTabStop(FALSE); |
2730 | addChild(new_column->mHeader); | 3022 | addChild(new_column->mHeader); |
2731 | new_column->mHeader->setVisible(mDisplayColumnHeaders); | 3023 | new_column->mHeader->setVisible(mDisplayColumnHeaders); |
2732 | 3024 | ||
2733 | 3025 | sendChildToFront(mScrollbar); | |
2734 | // Move scroll to front | ||
2735 | removeChild(mScrollbar); | ||
2736 | addChild(mScrollbar); | ||
2737 | |||
2738 | } | 3026 | } |
2739 | } | 3027 | } |
3028 | |||
2740 | updateColumns(); | 3029 | updateColumns(); |
2741 | } | 3030 | } |
2742 | 3031 | ||
@@ -2753,18 +3042,18 @@ void LLScrollListCtrl::onClickColumn(void *userdata) | |||
2753 | 3042 | ||
2754 | LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; | 3043 | LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; |
2755 | bool ascending = column->mSortAscending; | 3044 | bool ascending = column->mSortAscending; |
2756 | if (column->mSortingColumn != column->mName) | 3045 | if (column->mSortingColumn != column->mName |
3046 | && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) | ||
2757 | { | 3047 | { |
2758 | if (parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) | 3048 | LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; |
2759 | { | 3049 | column_index = info_redir.mIndex; |
2760 | LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; | ||
2761 | column_index = info_redir.mIndex; | ||
2762 | } | ||
2763 | } | 3050 | } |
2764 | 3051 | ||
2765 | if (column_index == parent->mSortColumn) | 3052 | // if this column is the primary sort key, reverse the direction |
3053 | sort_column_t cur_sort_column; | ||
3054 | if (!parent->mSortColumns.empty() && parent->mSortColumns.back().first == column_index) | ||
2766 | { | 3055 | { |
2767 | ascending = !parent->mSortAscending; | 3056 | ascending = !parent->mSortColumns.back().second; |
2768 | } | 3057 | } |
2769 | 3058 | ||
2770 | parent->sortByColumn(column_index, ascending); | 3059 | parent->sortByColumn(column_index, ascending); |
@@ -2777,12 +3066,17 @@ void LLScrollListCtrl::onClickColumn(void *userdata) | |||
2777 | 3066 | ||
2778 | std::string LLScrollListCtrl::getSortColumnName() | 3067 | std::string LLScrollListCtrl::getSortColumnName() |
2779 | { | 3068 | { |
2780 | LLScrollListColumn* column = mSortColumn >= 0 ? mColumnsIndexed[mSortColumn] : NULL; | 3069 | LLScrollListColumn* column = mSortColumns.empty() ? NULL : mColumnsIndexed[mSortColumns.back().first]; |
2781 | 3070 | ||
2782 | if (column) return column->mName; | 3071 | if (column) return column->mName; |
2783 | else return ""; | 3072 | else return ""; |
2784 | } | 3073 | } |
2785 | 3074 | ||
3075 | BOOL LLScrollListCtrl::needsSorting() | ||
3076 | { | ||
3077 | return !mSortColumns.empty(); | ||
3078 | } | ||
3079 | |||
2786 | void LLScrollListCtrl::clearColumns() | 3080 | void LLScrollListCtrl::clearColumns() |
2787 | { | 3081 | { |
2788 | std::map<LLString, LLScrollListColumn>::iterator itor; | 3082 | std::map<LLString, LLScrollListColumn>::iterator itor; |
@@ -2796,6 +3090,7 @@ void LLScrollListCtrl::clearColumns() | |||
2796 | } | 3090 | } |
2797 | } | 3091 | } |
2798 | mColumns.clear(); | 3092 | mColumns.clear(); |
3093 | mSortColumns.clear(); | ||
2799 | } | 3094 | } |
2800 | 3095 | ||
2801 | void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& label) | 3096 | void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& label) |
@@ -2849,12 +3144,12 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p | |||
2849 | S32 col_index = 0 ; | 3144 | S32 col_index = 0 ; |
2850 | for (itor = columns.beginArray(); itor != columns.endArray(); ++itor) | 3145 | for (itor = columns.beginArray(); itor != columns.endArray(); ++itor) |
2851 | { | 3146 | { |
2852 | LLString column = (*itor)["column"].asString(); | 3147 | if (itor->isUndefined()) |
2853 | |||
2854 | if (mColumns.size() == 0) | ||
2855 | { | 3148 | { |
2856 | mDefaultColumn = 0; | 3149 | // skip unused columns in item passed in |
3150 | continue; | ||
2857 | } | 3151 | } |
3152 | LLString column = (*itor)["column"].asString(); | ||
2858 | 3153 | ||
2859 | LLScrollListColumn* columnp = NULL; | 3154 | LLScrollListColumn* columnp = NULL; |
2860 | 3155 | ||
@@ -2879,9 +3174,18 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p | |||
2879 | LLSD new_column; | 3174 | LLSD new_column; |
2880 | new_column["name"] = column; | 3175 | new_column["name"] = column; |
2881 | new_column["label"] = column; | 3176 | new_column["label"] = column; |
2882 | new_column["width"] = (*itor)["width"]; | 3177 | // if width supplied for column, use it, otherwise |
3178 | // use adaptive width | ||
3179 | if (itor->has("width")) | ||
3180 | { | ||
3181 | new_column["width"] = (*itor)["width"]; | ||
3182 | } | ||
3183 | else | ||
3184 | { | ||
3185 | new_column["dynamicwidth"] = true; | ||
3186 | } | ||
2883 | addColumn(new_column); | 3187 | addColumn(new_column); |
2884 | columnp = &mColumns.find(column)->second; | 3188 | columnp = &mColumns[column]; |
2885 | new_item->setNumColumns(mColumns.size()); | 3189 | new_item->setNumColumns(mColumns.size()); |
2886 | } | 3190 | } |
2887 | 3191 | ||
@@ -2895,6 +3199,7 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p | |||
2895 | LLString type = (*itor)["type"].asString(); | 3199 | LLString type = (*itor)["type"].asString(); |
2896 | BOOL has_color = (*itor).has("color"); | 3200 | BOOL has_color = (*itor).has("color"); |
2897 | LLColor4 color = ((*itor)["color"]); | 3201 | LLColor4 color = ((*itor)["color"]); |
3202 | BOOL enabled = !(*itor).has("enabled") || (*itor)["enabled"].asBoolean() == true; | ||
2898 | 3203 | ||
2899 | const LLFontGL *font = gResMgr->getRes(fontname); | 3204 | const LLFontGL *font = gResMgr->getRes(fontname); |
2900 | if (!font) | 3205 | if (!font) |
@@ -2906,8 +3211,7 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p | |||
2906 | if (type == "icon") | 3211 | if (type == "icon") |
2907 | { | 3212 | { |
2908 | LLUUID image_id = value.asUUID(); | 3213 | LLUUID image_id = value.asUUID(); |
2909 | LLImageGL* icon = LLUI::sImageProvider->getUIImageByID(image_id); | 3214 | LLScrollListIcon* cell = new LLScrollListIcon(value.asUUID(), width); |
2910 | LLScrollListIcon* cell = new LLScrollListIcon(icon, width, image_id); | ||
2911 | if (has_color) | 3215 | if (has_color) |
2912 | { | 3216 | { |
2913 | cell->setColor(color); | 3217 | cell->setColor(color); |
@@ -2916,8 +3220,10 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p | |||
2916 | } | 3220 | } |
2917 | else if (type == "checkbox") | 3221 | else if (type == "checkbox") |
2918 | { | 3222 | { |
2919 | LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl(value.asString(), | 3223 | LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl("check", |
2920 | LLRect(0, 0, width, width), "label"); | 3224 | LLRect(0, width, width, 0), " "); |
3225 | ctrl->setEnabled(enabled); | ||
3226 | ctrl->setValue(value); | ||
2921 | LLScrollListCheck* cell = new LLScrollListCheck(ctrl,width); | 3227 | LLScrollListCheck* cell = new LLScrollListCheck(ctrl,width); |
2922 | if (has_color) | 3228 | if (has_color) |
2923 | { | 3229 | { |
@@ -3070,18 +3376,12 @@ void LLScrollListCtrl::onFocusReceived() | |||
3070 | { | 3376 | { |
3071 | // forget latent selection changes when getting focus | 3377 | // forget latent selection changes when getting focus |
3072 | mSelectionChanged = FALSE; | 3378 | mSelectionChanged = FALSE; |
3379 | LLUICtrl::onFocusReceived(); | ||
3073 | } | 3380 | } |
3074 | 3381 | ||
3075 | //virtual | 3382 | //virtual |
3076 | void LLScrollListCtrl::onFocusLost() | 3383 | void LLScrollListCtrl::onFocusLost() |
3077 | { | 3384 | { |
3078 | if (mIsPopup) | ||
3079 | { | ||
3080 | if (getParent()) | ||
3081 | { | ||
3082 | getParent()->onFocusLost(); | ||
3083 | } | ||
3084 | } | ||
3085 | if (hasMouseCapture()) | 3385 | if (hasMouseCapture()) |
3086 | { | 3386 | { |
3087 | gFocusMgr.setMouseCapture(NULL); | 3387 | gFocusMgr.setMouseCapture(NULL); |
@@ -3133,11 +3433,11 @@ void LLColumnHeader::draw() | |||
3133 | { | 3433 | { |
3134 | if( getVisible() ) | 3434 | if( getVisible() ) |
3135 | { | 3435 | { |
3136 | mDrawArrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn; | 3436 | BOOL draw_arrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn; |
3137 | 3437 | ||
3138 | BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); | 3438 | BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); |
3139 | mArrowImage = is_ascending ? LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("up_arrow.tga"))) | 3439 | mButton->setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, draw_arrow ? LLColor4::white : LLColor4::transparent); |
3140 | : LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("down_arrow.tga"))); | 3440 | mArrowImage = mButton->getImageOverlay()->getImage(); |
3141 | 3441 | ||
3142 | //BOOL clip = mRect.mRight > mColumn->mParentCtrl->getItemListRect().getWidth(); | 3442 | //BOOL clip = mRect.mRight > mColumn->mParentCtrl->getItemListRect().getWidth(); |
3143 | //LLGLEnable scissor_test(clip ? GL_SCISSOR_TEST : GL_FALSE); | 3443 | //LLGLEnable scissor_test(clip ? GL_SCISSOR_TEST : GL_FALSE); |
@@ -3237,11 +3537,11 @@ void LLColumnHeader::showList() | |||
3237 | { | 3537 | { |
3238 | if (mColumn->mParentCtrl->getSortAscending()) | 3538 | if (mColumn->mParentCtrl->getSortAscending()) |
3239 | { | 3539 | { |
3240 | low_item_text = cell->getText(); | 3540 | low_item_text = cell->getValue().asString(); |
3241 | } | 3541 | } |
3242 | else | 3542 | else |
3243 | { | 3543 | { |
3244 | high_item_text = cell->getText(); | 3544 | high_item_text = cell->getValue().asString(); |
3245 | } | 3545 | } |
3246 | } | 3546 | } |
3247 | } | 3547 | } |
@@ -3254,11 +3554,11 @@ void LLColumnHeader::showList() | |||
3254 | { | 3554 | { |
3255 | if (mColumn->mParentCtrl->getSortAscending()) | 3555 | if (mColumn->mParentCtrl->getSortAscending()) |
3256 | { | 3556 | { |
3257 | high_item_text = cell->getText(); | 3557 | high_item_text = cell->getValue().asString(); |
3258 | } | 3558 | } |
3259 | else | 3559 | else |
3260 | { | 3560 | { |
3261 | low_item_text = cell->getText(); | 3561 | low_item_text = cell->getValue().asString(); |
3262 | } | 3562 | } |
3263 | } | 3563 | } |
3264 | } | 3564 | } |