diff options
Diffstat (limited to 'linden/indra/llui/llscrolllistctrl.cpp')
-rw-r--r-- | linden/indra/llui/llscrolllistctrl.cpp | 972 |
1 files changed, 790 insertions, 182 deletions
diff --git a/linden/indra/llui/llscrolllistctrl.cpp b/linden/indra/llui/llscrolllistctrl.cpp index 4d5c49f..22987dc 100644 --- a/linden/indra/llui/llscrolllistctrl.cpp +++ b/linden/indra/llui/llscrolllistctrl.cpp | |||
@@ -4,6 +4,7 @@ | |||
4 | * | 4 | * |
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | 5 | * Copyright (c) 2001-2007, Linden Research, Inc. |
6 | * | 6 | * |
7 | * Second Life Viewer Source Code | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | 8 | * The source code in this file ("Source Code") is provided by Linden Lab |
8 | * to you under the terms of the GNU General Public License, version 2.0 | 9 | * to you under the terms of the GNU General Public License, version 2.0 |
9 | * ("GPL"), unless you have obtained a separate licensing agreement | 10 | * ("GPL"), unless you have obtained a separate licensing agreement |
@@ -48,8 +49,11 @@ | |||
48 | #include "llwindow.h" | 49 | #include "llwindow.h" |
49 | #include "llcontrol.h" | 50 | #include "llcontrol.h" |
50 | #include "llkeyboard.h" | 51 | #include "llkeyboard.h" |
52 | #include "llresizebar.h" | ||
51 | 53 | ||
52 | const S32 LIST_BORDER_PAD = 2; // white space inside the border and to the left of the scrollbar | 54 | const S32 LIST_BORDER_PAD = 2; // white space inside the border and to the left of the scrollbar |
55 | const S32 MIN_COLUMN_WIDTH = 20; | ||
56 | const S32 LIST_SNAP_PADDING = 5; | ||
53 | 57 | ||
54 | // local structures & classes. | 58 | // local structures & classes. |
55 | struct SortScrollListItem | 59 | struct SortScrollListItem |
@@ -152,6 +156,19 @@ BOOL LLScrollListCheck::handleClick() | |||
152 | } | 156 | } |
153 | 157 | ||
154 | // | 158 | // |
159 | // LLScrollListSeparator | ||
160 | // | ||
161 | LLScrollListSeparator::LLScrollListSeparator(S32 width) : mWidth(width) | ||
162 | { | ||
163 | } | ||
164 | |||
165 | void LLScrollListSeparator::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const | ||
166 | { | ||
167 | //*FIXME: use dynamic item heights and make separators narrow, and inactive | ||
168 | gl_line_2d(5, 8, llmax(5, width - 5), 8, color); | ||
169 | } | ||
170 | |||
171 | // | ||
155 | // LLScrollListText | 172 | // LLScrollListText |
156 | // | 173 | // |
157 | U32 LLScrollListText::sCount = 0; | 174 | U32 LLScrollListText::sCount = 0; |
@@ -273,7 +290,7 @@ LLScrollListItem::~LLScrollListItem() | |||
273 | std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); | 290 | std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); |
274 | } | 291 | } |
275 | 292 | ||
276 | BOOL LLScrollListItem::handleMouseDown(S32 x, S32 y, MASK mask) | 293 | BOOL LLScrollListItem::handleClick(S32 x, S32 y, MASK mask) |
277 | { | 294 | { |
278 | BOOL handled = FALSE; | 295 | BOOL handled = FALSE; |
279 | 296 | ||
@@ -374,14 +391,13 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, | |||
374 | mPageLines(0), | 391 | mPageLines(0), |
375 | mHeadingHeight(20), | 392 | mHeadingHeight(20), |
376 | mMaxSelectable(0), | 393 | mMaxSelectable(0), |
377 | mHeadingFont(NULL), | ||
378 | mAllowMultipleSelection( allow_multiple_selection ), | 394 | mAllowMultipleSelection( allow_multiple_selection ), |
379 | mAllowKeyboardMovement(TRUE), | 395 | mAllowKeyboardMovement(TRUE), |
380 | mCommitOnKeyboardMovement(TRUE), | 396 | mCommitOnKeyboardMovement(TRUE), |
381 | mCommitOnSelectionChange(FALSE), | 397 | mCommitOnSelectionChange(FALSE), |
382 | mSelectionChanged(FALSE), | 398 | mSelectionChanged(FALSE), |
383 | mCanSelect(TRUE), | 399 | mCanSelect(TRUE), |
384 | mDisplayColumnButtons(FALSE), | 400 | mDisplayColumnHeaders(FALSE), |
385 | mCollapseEmptyColumns(FALSE), | 401 | mCollapseEmptyColumns(FALSE), |
386 | mIsPopup(FALSE), | 402 | mIsPopup(FALSE), |
387 | mMaxItemCount(INT_MAX), | 403 | mMaxItemCount(INT_MAX), |
@@ -396,21 +412,21 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, | |||
396 | mFgUnselectedColor( LLUI::sColorsGroup->getColor("ScrollUnselectedColor") ), | 412 | mFgUnselectedColor( LLUI::sColorsGroup->getColor("ScrollUnselectedColor") ), |
397 | mFgDisabledColor( LLUI::sColorsGroup->getColor("ScrollDisabledColor") ), | 413 | mFgDisabledColor( LLUI::sColorsGroup->getColor("ScrollDisabledColor") ), |
398 | mHighlightedColor( LLUI::sColorsGroup->getColor("ScrollHighlightedColor") ), | 414 | mHighlightedColor( LLUI::sColorsGroup->getColor("ScrollHighlightedColor") ), |
415 | mHighlightedItem(-1), | ||
399 | mBorderThickness( 2 ), | 416 | mBorderThickness( 2 ), |
400 | mOnDoubleClickCallback( NULL ), | 417 | mOnDoubleClickCallback( NULL ), |
401 | mOnMaximumSelectCallback( NULL ), | 418 | mOnMaximumSelectCallback( NULL ), |
402 | mOnSortChangedCallback( NULL ), | 419 | mOnSortChangedCallback( NULL ), |
403 | mHighlightedItem(-1), | 420 | mDrewSelected(FALSE), |
404 | mBorder(NULL), | 421 | mBorder(NULL), |
405 | mDefaultColumn("SIMPLE"), | ||
406 | mSearchColumn(0), | 422 | mSearchColumn(0), |
423 | mDefaultColumn("SIMPLE"), | ||
407 | 424 | ||
408 | mNumDynamicWidthColumns(0), | 425 | mNumDynamicWidthColumns(0), |
409 | mTotalStaticColumnWidth(0), | 426 | mTotalStaticColumnWidth(0), |
410 | mSortColumn(0), | 427 | mSortColumn(-1), |
411 | mSortAscending(TRUE), | 428 | mSorted(TRUE), |
412 | 429 | mSortAscending(TRUE) | |
413 | mDrewSelected(FALSE) | ||
414 | { | 430 | { |
415 | mItemListRect.setOriginAndSize( | 431 | mItemListRect.setOriginAndSize( |
416 | mBorderThickness + LIST_BORDER_PAD, | 432 | mBorderThickness + LIST_BORDER_PAD, |
@@ -497,6 +513,7 @@ void LLScrollListCtrl::clearRows() | |||
497 | 513 | ||
498 | mScrollLines = 0; | 514 | mScrollLines = 0; |
499 | mLastSelected = NULL; | 515 | mLastSelected = NULL; |
516 | updateMaxContentWidth(NULL); | ||
500 | } | 517 | } |
501 | 518 | ||
502 | 519 | ||
@@ -546,7 +563,6 @@ S32 LLScrollListCtrl::getFirstSelectedIndex() | |||
546 | return -1; | 563 | return -1; |
547 | } | 564 | } |
548 | 565 | ||
549 | |||
550 | LLScrollListItem* LLScrollListCtrl::getFirstData() const | 566 | LLScrollListItem* LLScrollListCtrl::getFirstData() const |
551 | { | 567 | { |
552 | if (mItemList.size() == 0) | 568 | if (mItemList.size() == 0) |
@@ -556,6 +572,15 @@ LLScrollListItem* LLScrollListCtrl::getFirstData() const | |||
556 | return mItemList[0]; | 572 | return mItemList[0]; |
557 | } | 573 | } |
558 | 574 | ||
575 | LLScrollListItem* LLScrollListCtrl::getLastData() const | ||
576 | { | ||
577 | if (mItemList.size() == 0) | ||
578 | { | ||
579 | return NULL; | ||
580 | } | ||
581 | return mItemList[mItemList.size() - 1]; | ||
582 | } | ||
583 | |||
559 | std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const | 584 | std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const |
560 | { | 585 | { |
561 | std::vector<LLScrollListItem*> ret; | 586 | std::vector<LLScrollListItem*> ret; |
@@ -573,7 +598,7 @@ void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) | |||
573 | { | 598 | { |
574 | LLUICtrl::reshape( width, height, called_from_parent ); | 599 | LLUICtrl::reshape( width, height, called_from_parent ); |
575 | 600 | ||
576 | S32 heading_size = (mDisplayColumnButtons ? mHeadingHeight : 0); | 601 | S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); |
577 | 602 | ||
578 | mItemListRect.setOriginAndSize( | 603 | mItemListRect.setOriginAndSize( |
579 | mBorderThickness + LIST_BORDER_PAD, | 604 | mBorderThickness + LIST_BORDER_PAD, |
@@ -586,10 +611,8 @@ void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) | |||
586 | mScrollbar->setPageSize( mPageLines ); | 611 | mScrollbar->setPageSize( mPageLines ); |
587 | 612 | ||
588 | updateColumns(); | 613 | updateColumns(); |
589 | updateColumnButtons(); | ||
590 | } | 614 | } |
591 | 615 | ||
592 | |||
593 | // Attempt to size the control to show all items. | 616 | // Attempt to size the control to show all items. |
594 | // Do not make larger than width or height. | 617 | // Do not make larger than width or height. |
595 | void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) | 618 | void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) |
@@ -621,35 +644,83 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) | |||
621 | { | 644 | { |
622 | case ADD_TOP: | 645 | case ADD_TOP: |
623 | mItemList.push_front(item); | 646 | mItemList.push_front(item); |
647 | setSorted(FALSE); | ||
624 | break; | 648 | break; |
625 | 649 | ||
626 | case ADD_SORTED: | 650 | case ADD_SORTED: |
627 | mSortColumn = 0; | 651 | if (mSortColumn == -1) |
628 | mSortAscending = TRUE; | 652 | { |
653 | mSortColumn = 0; | ||
654 | mSortAscending = TRUE; | ||
655 | } | ||
629 | mItemList.push_back(item); | 656 | mItemList.push_back(item); |
630 | std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); | 657 | std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); |
631 | break; | 658 | break; |
632 | 659 | ||
633 | case ADD_BOTTOM: | 660 | case ADD_BOTTOM: |
634 | mItemList.push_back(item); | 661 | mItemList.push_back(item); |
662 | setSorted(FALSE); | ||
635 | break; | 663 | break; |
636 | 664 | ||
637 | default: | 665 | default: |
638 | llassert(0); | 666 | llassert(0); |
639 | mItemList.push_back(item); | 667 | mItemList.push_back(item); |
668 | setSorted(FALSE); | ||
640 | break; | 669 | break; |
641 | } | 670 | } |
642 | 671 | ||
643 | updateLineHeight(); | 672 | updateLineHeight(); |
644 | mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0; | 673 | mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0; |
645 | mScrollbar->setVisible(mPageLines < getItemCount()); | 674 | BOOL scrollbar_visible = mPageLines < getItemCount(); |
675 | |||
676 | if (scrollbar_visible != mScrollbar->getVisible()) | ||
677 | { | ||
678 | mScrollbar->setVisible(mPageLines < getItemCount()); | ||
679 | updateColumns(); | ||
680 | } | ||
646 | mScrollbar->setPageSize( mPageLines ); | 681 | mScrollbar->setPageSize( mPageLines ); |
647 | 682 | ||
648 | mScrollbar->setDocSize( getItemCount() ); | 683 | mScrollbar->setDocSize( getItemCount() ); |
684 | |||
685 | updateMaxContentWidth(item); | ||
649 | } | 686 | } |
687 | |||
650 | return not_too_big; | 688 | return not_too_big; |
651 | } | 689 | } |
652 | 690 | ||
691 | void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) | ||
692 | { | ||
693 | const S32 HEADING_TEXT_PADDING = 30; | ||
694 | const S32 COLUMN_TEXT_PADDING = 20; | ||
695 | |||
696 | std::map<LLString, LLScrollListColumn>::iterator column_itor; | ||
697 | for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) | ||
698 | { | ||
699 | LLScrollListColumn* column = &column_itor->second; | ||
700 | |||
701 | if (!added_item) | ||
702 | { | ||
703 | // update on all items | ||
704 | column->mMaxContentWidth = column->mHeader ? LLFontGL::sSansSerifSmall->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0; | ||
705 | item_list::iterator iter; | ||
706 | for (iter = mItemList.begin(); iter != mItemList.end(); iter++) | ||
707 | { | ||
708 | LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); | ||
709 | if (!cellp) continue; | ||
710 | |||
711 | column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); | ||
712 | } | ||
713 | } | ||
714 | else | ||
715 | { | ||
716 | LLScrollListCell* cellp = added_item->getColumn(column->mIndex); | ||
717 | if (!cellp) continue; | ||
718 | |||
719 | column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); | ||
720 | } | ||
721 | } | ||
722 | } | ||
723 | |||
653 | 724 | ||
654 | // Line height is the max height of all the cells in all the items. | 725 | // Line height is the max height of all the cells in all the items. |
655 | void LLScrollListCtrl::updateLineHeight() | 726 | void LLScrollListCtrl::updateLineHeight() |
@@ -678,60 +749,82 @@ void LLScrollListCtrl::updateColumns() | |||
678 | for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) | 749 | for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) |
679 | { | 750 | { |
680 | LLScrollListColumn *column = &column_itor->second; | 751 | LLScrollListColumn *column = &column_itor->second; |
752 | S32 new_width = column->mWidth; | ||
681 | if (column->mRelWidth >= 0) | 753 | if (column->mRelWidth >= 0) |
682 | { | 754 | { |
683 | column->mWidth = (S32)llround(column->mRelWidth*mItemListRect.getWidth()); | 755 | new_width = (S32)llround(column->mRelWidth*mItemListRect.getWidth()); |
684 | } | 756 | } |
685 | else if (column->mDynamicWidth) | 757 | else if (column->mDynamicWidth) |
686 | { | 758 | { |
687 | column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; | 759 | new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; |
688 | 760 | } | |
761 | |||
762 | if (new_width != column->mWidth) | ||
763 | { | ||
764 | column->mWidth = new_width; | ||
689 | } | 765 | } |
690 | mColumnsIndexed[column_itor->second.mIndex] = column; | 766 | mColumnsIndexed[column_itor->second.mIndex] = column; |
691 | } | 767 | } |
692 | } | ||
693 | 768 | ||
694 | void LLScrollListCtrl::updateColumnButtons() | 769 | item_list::iterator iter; |
695 | { | 770 | for (iter = mItemList.begin(); iter != mItemList.end(); iter++) |
696 | std::map<LLString, LLScrollListColumn>::iterator column_itor; | ||
697 | for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) | ||
698 | { | 771 | { |
699 | LLScrollListColumn* column = &column_itor->second; | 772 | LLScrollListItem *itemp = *iter; |
700 | LLButton *button = column->mButton; | 773 | S32 num_cols = itemp->getNumColumns(); |
701 | 774 | S32 i = 0; | |
702 | if (button) | 775 | for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) |
703 | { | 776 | { |
704 | mColumnsIndexed[column->mIndex] = column; | 777 | if (i >= (S32)mColumnsIndexed.size()) break; |
778 | |||
779 | cell->setWidth(mColumnsIndexed[i]->mWidth); | ||
780 | } | ||
781 | } | ||
705 | 782 | ||
783 | // update headers | ||
784 | std::vector<LLScrollListColumn*>::iterator column_ordered_it; | ||
785 | S32 left = mItemListRect.mLeft; | ||
786 | LLColumnHeader* last_header = NULL; | ||
787 | for (column_ordered_it = mColumnsIndexed.begin(); column_ordered_it != mColumnsIndexed.end(); ++column_ordered_it) | ||
788 | { | ||
789 | if ((*column_ordered_it)->mWidth <= 0) | ||
790 | { | ||
791 | // skip hidden columns | ||
792 | } | ||
793 | LLScrollListColumn* column = *column_ordered_it; | ||
794 | |||
795 | if (column->mHeader) | ||
796 | { | ||
797 | last_header = column->mHeader; | ||
706 | S32 top = mItemListRect.mTop; | 798 | S32 top = mItemListRect.mTop; |
707 | S32 left = mItemListRect.mLeft; | 799 | S32 right = left + column->mWidth; |
708 | { | 800 | |
709 | std::map<LLString, LLScrollListColumn>::iterator itor; | 801 | if (column->mIndex != (S32)mColumnsIndexed.size()-1) |
710 | for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) | ||
711 | { | ||
712 | if (itor->second.mIndex < column->mIndex && | ||
713 | itor->second.mWidth > 0) | ||
714 | { | ||
715 | left += itor->second.mWidth + mColumnPadding; | ||
716 | } | ||
717 | } | ||
718 | } | ||
719 | S32 right = left+column->mWidth; | ||
720 | if (column->mIndex != (S32)mColumns.size()-1) | ||
721 | { | 802 | { |
722 | right += mColumnPadding; | 803 | right += mColumnPadding; |
723 | } | 804 | } |
724 | LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top); | 805 | right = llmax(left, llmin(mItemListRect.getWidth(), right)); |
725 | button->setRect(temp_rect); | 806 | |
726 | button->setFont(mHeadingFont); | 807 | S32 header_width = right - left; |
727 | button->setVisible(mDisplayColumnButtons); | 808 | |
809 | last_header->reshape(header_width, mHeadingHeight); | ||
810 | last_header->translate(left - last_header->getRect().mLeft, top - last_header->getRect().mBottom); | ||
811 | last_header->setVisible(mDisplayColumnHeaders && header_width > 0); | ||
812 | left = right; | ||
728 | } | 813 | } |
729 | } | 814 | } |
815 | |||
816 | // expand last column header we encountered to full list width | ||
817 | if (last_header) | ||
818 | { | ||
819 | S32 header_strip_width = mItemListRect.getWidth() + (mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE); | ||
820 | S32 new_width = llmax(0, mItemListRect.mLeft + header_strip_width - last_header->getRect().mLeft); | ||
821 | last_header->reshape(new_width, last_header->getRect().getHeight()); | ||
822 | } | ||
730 | } | 823 | } |
731 | 824 | ||
732 | void LLScrollListCtrl::setDisplayHeading(BOOL display) | 825 | void LLScrollListCtrl::setDisplayHeading(BOOL display) |
733 | { | 826 | { |
734 | mDisplayColumnButtons = display; | 827 | mDisplayColumnHeaders = display; |
735 | 828 | ||
736 | updateColumns(); | 829 | updateColumns(); |
737 | 830 | ||
@@ -745,15 +838,7 @@ void LLScrollListCtrl::setHeadingHeight(S32 heading_height) | |||
745 | reshape(mRect.getWidth(), mRect.getHeight()); | 838 | reshape(mRect.getWidth(), mRect.getHeight()); |
746 | 839 | ||
747 | // Resize | 840 | // Resize |
748 | mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight()); | 841 | mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); |
749 | |||
750 | updateColumnButtons(); | ||
751 | } | ||
752 | |||
753 | void LLScrollListCtrl::setHeadingFont(const LLFontGL* heading_font) | ||
754 | { | ||
755 | mHeadingFont = heading_font; | ||
756 | updateColumnButtons(); | ||
757 | } | 842 | } |
758 | 843 | ||
759 | void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse) | 844 | void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse) |
@@ -873,6 +958,7 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index) | |||
873 | } | 958 | } |
874 | delete itemp; | 959 | delete itemp; |
875 | mItemList.erase(mItemList.begin() + target_index); | 960 | mItemList.erase(mItemList.begin() + target_index); |
961 | updateMaxContentWidth(NULL); | ||
876 | } | 962 | } |
877 | 963 | ||
878 | void LLScrollListCtrl::deleteSelectedItems() | 964 | void LLScrollListCtrl::deleteSelectedItems() |
@@ -892,6 +978,7 @@ void LLScrollListCtrl::deleteSelectedItems() | |||
892 | } | 978 | } |
893 | } | 979 | } |
894 | mLastSelected = NULL; | 980 | mLastSelected = NULL; |
981 | updateMaxContentWidth(NULL); | ||
895 | } | 982 | } |
896 | 983 | ||
897 | void LLScrollListCtrl::highlightNthItem(S32 target_index) | 984 | void LLScrollListCtrl::highlightNthItem(S32 target_index) |
@@ -1033,7 +1120,7 @@ void LLScrollListCtrl::selectNextItem( BOOL extend_selection) | |||
1033 | } | 1120 | } |
1034 | } | 1121 | } |
1035 | 1122 | ||
1036 | if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement)) | 1123 | if (mCommitOnKeyboardMovement) |
1037 | { | 1124 | { |
1038 | onCommit(); | 1125 | onCommit(); |
1039 | } | 1126 | } |
@@ -1486,109 +1573,153 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) | |||
1486 | return handled; | 1573 | return handled; |
1487 | } | 1574 | } |
1488 | 1575 | ||
1489 | 1576 | BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) | |
1490 | BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) | ||
1491 | { | 1577 | { |
1492 | BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; | 1578 | if (!mCanSelect) return FALSE; |
1493 | 1579 | ||
1494 | // set keyboard focus first, in case click action wants to move focus elsewhere | 1580 | BOOL selection_changed = FALSE; |
1495 | setFocus(TRUE); | ||
1496 | 1581 | ||
1497 | if( !handled && mCanSelect) | 1582 | LLScrollListItem* hit_item = hitItem(x, y); |
1583 | if( hit_item ) | ||
1498 | { | 1584 | { |
1499 | LLScrollListItem* hit_item = hitItem(x, y); | 1585 | if( mAllowMultipleSelection ) |
1500 | if( hit_item ) | ||
1501 | { | 1586 | { |
1502 | if( mAllowMultipleSelection ) | 1587 | if (mask & MASK_SHIFT) |
1503 | { | 1588 | { |
1504 | if (mask & MASK_SHIFT) | 1589 | if (mLastSelected == NULL) |
1505 | { | 1590 | { |
1506 | if (mLastSelected == NULL) | 1591 | selectItem(hit_item); |
1507 | { | 1592 | } |
1508 | selectItem(hit_item); | 1593 | else |
1509 | } | 1594 | { |
1510 | else | 1595 | // Select everthing between mLastSelected and hit_item |
1596 | bool selecting = false; | ||
1597 | item_list::iterator itor; | ||
1598 | // If we multiselect backwards, we'll stomp on mLastSelected, | ||
1599 | // meaning that we never stop selecting until hitting max or | ||
1600 | // the end of the list. | ||
1601 | LLScrollListItem* lastSelected = mLastSelected; | ||
1602 | for (itor = mItemList.begin(); itor != mItemList.end(); ++itor) | ||
1511 | { | 1603 | { |
1512 | // Select everthing between mLastSelected and hit_item | 1604 | if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable) |
1513 | bool selecting = false; | ||
1514 | item_list::iterator itor; | ||
1515 | // If we multiselect backwards, we'll stomp on mLastSelected, | ||
1516 | // meaning that we never stop selecting until hitting max or | ||
1517 | // the end of the list. | ||
1518 | LLScrollListItem* lastSelected = mLastSelected; | ||
1519 | for (itor = mItemList.begin(); itor != mItemList.end(); ++itor) | ||
1520 | { | 1605 | { |
1521 | if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable) | 1606 | if(mOnMaximumSelectCallback) |
1522 | { | ||
1523 | if(mOnMaximumSelectCallback) | ||
1524 | { | ||
1525 | mOnMaximumSelectCallback(mCallbackUserData); | ||
1526 | } | ||
1527 | break; | ||
1528 | } | ||
1529 | LLScrollListItem *item = *itor; | ||
1530 | if (item == hit_item || item == lastSelected) | ||
1531 | { | ||
1532 | selectItem(item, FALSE); | ||
1533 | selecting = !selecting; | ||
1534 | } | ||
1535 | if (selecting) | ||
1536 | { | 1607 | { |
1537 | selectItem(item, FALSE); | 1608 | mOnMaximumSelectCallback(mCallbackUserData); |
1538 | } | 1609 | } |
1610 | break; | ||
1611 | } | ||
1612 | LLScrollListItem *item = *itor; | ||
1613 | if (item == hit_item || item == lastSelected) | ||
1614 | { | ||
1615 | selectItem(item, FALSE); | ||
1616 | selecting = !selecting; | ||
1617 | } | ||
1618 | if (selecting) | ||
1619 | { | ||
1620 | selectItem(item, FALSE); | ||
1539 | } | 1621 | } |
1540 | } | 1622 | } |
1541 | } | 1623 | } |
1542 | else if (mask & MASK_CONTROL) | 1624 | } |
1625 | else if (mask & MASK_CONTROL) | ||
1626 | { | ||
1627 | if (hit_item->getSelected()) | ||
1628 | { | ||
1629 | deselectItem(hit_item); | ||
1630 | } | ||
1631 | else | ||
1543 | { | 1632 | { |
1544 | if (hit_item->getSelected()) | 1633 | if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) |
1545 | { | 1634 | { |
1546 | deselectItem(hit_item); | 1635 | selectItem(hit_item, FALSE); |
1547 | } | 1636 | } |
1548 | else | 1637 | else |
1549 | { | 1638 | { |
1550 | if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) | 1639 | if(mOnMaximumSelectCallback) |
1551 | { | 1640 | { |
1552 | selectItem(hit_item, FALSE); | 1641 | mOnMaximumSelectCallback(mCallbackUserData); |
1553 | } | ||
1554 | else | ||
1555 | { | ||
1556 | if(mOnMaximumSelectCallback) | ||
1557 | { | ||
1558 | mOnMaximumSelectCallback(mCallbackUserData); | ||
1559 | } | ||
1560 | } | 1642 | } |
1561 | } | 1643 | } |
1562 | } | 1644 | } |
1563 | else | ||
1564 | { | ||
1565 | deselectAllItems(TRUE); | ||
1566 | selectItem(hit_item); | ||
1567 | } | ||
1568 | } | 1645 | } |
1569 | else | 1646 | else |
1570 | { | 1647 | { |
1648 | deselectAllItems(TRUE); | ||
1571 | selectItem(hit_item); | 1649 | selectItem(hit_item); |
1572 | } | 1650 | } |
1573 | |||
1574 | hit_item->handleMouseDown(x - mBorderThickness - LIST_BORDER_PAD, | ||
1575 | 1, mask); | ||
1576 | // always commit on mousedown | ||
1577 | onCommit(); | ||
1578 | mSelectionChanged = FALSE; | ||
1579 | |||
1580 | // clear search string on mouse operations | ||
1581 | mSearchString.clear(); | ||
1582 | } | 1651 | } |
1583 | else | 1652 | else |
1584 | { | 1653 | { |
1585 | mLastSelected = NULL; | 1654 | selectItem(hit_item); |
1586 | } | 1655 | } |
1656 | |||
1657 | hit_item->handleClick(x - mBorderThickness - LIST_BORDER_PAD, | ||
1658 | 1, mask); | ||
1659 | |||
1660 | selection_changed = mSelectionChanged; | ||
1661 | if (mCommitOnSelectionChange) | ||
1662 | { | ||
1663 | commitIfChanged(); | ||
1664 | } | ||
1665 | |||
1666 | // clear search string on mouse operations | ||
1667 | mSearchString.clear(); | ||
1668 | } | ||
1669 | else | ||
1670 | { | ||
1671 | //mLastSelected = NULL; | ||
1672 | //deselectAllItems(TRUE); | ||
1673 | } | ||
1674 | |||
1675 | return selection_changed; | ||
1676 | } | ||
1677 | |||
1678 | |||
1679 | BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) | ||
1680 | { | ||
1681 | BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; | ||
1682 | |||
1683 | if( !handled ) | ||
1684 | { | ||
1685 | // set keyboard focus first, in case click action wants to move focus elsewhere | ||
1686 | setFocus(TRUE); | ||
1687 | |||
1688 | // clear selection changed flag so because user is starting a selection operation | ||
1689 | mSelectionChanged = FALSE; | ||
1690 | |||
1691 | gFocusMgr.setMouseCapture(this); | ||
1692 | selectItemAt(x, y, mask); | ||
1587 | } | 1693 | } |
1588 | 1694 | ||
1589 | return TRUE; | 1695 | return TRUE; |
1590 | } | 1696 | } |
1591 | 1697 | ||
1698 | BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) | ||
1699 | { | ||
1700 | if (hasMouseCapture()) | ||
1701 | { | ||
1702 | if(mask == MASK_NONE) | ||
1703 | { | ||
1704 | selectItemAt(x, y, mask); | ||
1705 | } | ||
1706 | } | ||
1707 | |||
1708 | if (hasMouseCapture()) | ||
1709 | { | ||
1710 | gFocusMgr.setMouseCapture(NULL); | ||
1711 | } | ||
1712 | |||
1713 | // always commit when mouse operation is completed inside list | ||
1714 | if (mItemListRect.pointInRect(x,y)) | ||
1715 | { | ||
1716 | mSelectionChanged = FALSE; | ||
1717 | onCommit(); | ||
1718 | } | ||
1719 | |||
1720 | return LLUICtrl::handleMouseUp(x, y, mask); | ||
1721 | } | ||
1722 | |||
1592 | BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) | 1723 | BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) |
1593 | { | 1724 | { |
1594 | //BOOL handled = FALSE; | 1725 | //BOOL handled = FALSE; |
@@ -1647,31 +1778,35 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) | |||
1647 | { | 1778 | { |
1648 | BOOL handled = FALSE; | 1779 | BOOL handled = FALSE; |
1649 | 1780 | ||
1650 | if(getVisible()) | 1781 | if (hasMouseCapture()) |
1651 | { | 1782 | { |
1652 | if (mCanSelect) | 1783 | if(mask == MASK_NONE) |
1653 | { | 1784 | { |
1654 | LLScrollListItem* item = hitItem(x, y); | 1785 | selectItemAt(x, y, mask); |
1655 | if (item) | ||
1656 | { | ||
1657 | highlightNthItem(getItemIndex(item)); | ||
1658 | } | ||
1659 | else | ||
1660 | { | ||
1661 | highlightNthItem(-1); | ||
1662 | } | ||
1663 | } | 1786 | } |
1664 | 1787 | } | |
1665 | handled = LLView::handleHover( x, y, mask ); | 1788 | else if (mCanSelect) |
1666 | 1789 | { | |
1667 | if( !handled ) | 1790 | LLScrollListItem* item = hitItem(x, y); |
1791 | if (item) | ||
1792 | { | ||
1793 | highlightNthItem(getItemIndex(item)); | ||
1794 | } | ||
1795 | else | ||
1668 | { | 1796 | { |
1669 | // Opaque | 1797 | highlightNthItem(-1); |
1670 | getWindow()->setCursor(UI_CURSOR_ARROW); | ||
1671 | lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; | ||
1672 | handled = TRUE; | ||
1673 | } | 1798 | } |
1674 | } | 1799 | } |
1800 | |||
1801 | handled = LLUICtrl::handleHover( x, y, mask ); | ||
1802 | |||
1803 | //if( !handled ) | ||
1804 | //{ | ||
1805 | // // Opaque | ||
1806 | // getWindow()->setCursor(UI_CURSOR_ARROW); | ||
1807 | // lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; | ||
1808 | // handled = TRUE; | ||
1809 | //} | ||
1675 | return handled; | 1810 | return handled; |
1676 | } | 1811 | } |
1677 | 1812 | ||
@@ -1980,6 +2115,16 @@ void LLScrollListCtrl::commitIfChanged() | |||
1980 | } | 2115 | } |
1981 | } | 2116 | } |
1982 | 2117 | ||
2118 | void LLScrollListCtrl::setSorted(BOOL sorted) | ||
2119 | { | ||
2120 | mSorted = sorted; | ||
2121 | } | ||
2122 | |||
2123 | BOOL LLScrollListCtrl::isSorted() | ||
2124 | { | ||
2125 | return mSorted; | ||
2126 | } | ||
2127 | |||
1983 | // Called by scrollbar | 2128 | // Called by scrollbar |
1984 | //static | 2129 | //static |
1985 | void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) | 2130 | void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) |
@@ -1992,9 +2137,19 @@ void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void | |||
1992 | // First column is column 0 | 2137 | // First column is column 0 |
1993 | void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) | 2138 | void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) |
1994 | { | 2139 | { |
1995 | mSortColumn = column; | 2140 | if (!mSorted || mSortColumn != column) |
1996 | mSortAscending = ascending; | 2141 | { |
1997 | std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); | 2142 | mSortColumn = column; |
2143 | std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); | ||
2144 | setSorted(TRUE); | ||
2145 | } | ||
2146 | |||
2147 | // just reverse the list if changing sort order | ||
2148 | if(mSortAscending != ascending) | ||
2149 | { | ||
2150 | std::reverse(mItemList.begin(), mItemList.end()); | ||
2151 | mSortAscending = ascending; | ||
2152 | } | ||
1998 | } | 2153 | } |
1999 | 2154 | ||
2000 | void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) | 2155 | void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) |
@@ -2066,7 +2221,7 @@ LLXMLNodePtr LLScrollListCtrl::getXML(bool save_children) const | |||
2066 | 2221 | ||
2067 | node->createChild("draw_border", TRUE)->setBoolValue((mBorder != NULL)); | 2222 | node->createChild("draw_border", TRUE)->setBoolValue((mBorder != NULL)); |
2068 | 2223 | ||
2069 | node->createChild("draw_heading", TRUE)->setBoolValue(mDisplayColumnButtons); | 2224 | node->createChild("draw_heading", TRUE)->setBoolValue(mDisplayColumnHeaders); |
2070 | 2225 | ||
2071 | node->createChild("background_visible", TRUE)->setBoolValue(mBackgroundVisible); | 2226 | node->createChild("background_visible", TRUE)->setBoolValue(mBackgroundVisible); |
2072 | 2227 | ||
@@ -2215,13 +2370,6 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2215 | node->getAttributeS32("heading_height", heading_height); | 2370 | node->getAttributeS32("heading_height", heading_height); |
2216 | scroll_list->setHeadingHeight(heading_height); | 2371 | scroll_list->setHeadingHeight(heading_height); |
2217 | } | 2372 | } |
2218 | if (node->hasAttribute("heading_font")) | ||
2219 | { | ||
2220 | LLString heading_font(""); | ||
2221 | node->getAttributeString("heading_font", heading_font); | ||
2222 | LLFontGL* gl_font = LLFontGL::fontFromName(heading_font.c_str()); | ||
2223 | scroll_list->setHeadingFont(gl_font); | ||
2224 | } | ||
2225 | scroll_list->setCollapseEmptyColumns(collapse_empty_columns); | 2373 | scroll_list->setCollapseEmptyColumns(collapse_empty_columns); |
2226 | 2374 | ||
2227 | scroll_list->setScrollListParameters(node); | 2375 | scroll_list->setScrollListParameters(node); |
@@ -2246,6 +2394,9 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2246 | 2394 | ||
2247 | LLString sortname(columnname); | 2395 | LLString sortname(columnname); |
2248 | child->getAttributeString("sort", sortname); | 2396 | child->getAttributeString("sort", sortname); |
2397 | |||
2398 | BOOL sort_ascending = TRUE; | ||
2399 | child->getAttributeBOOL("sort_ascending", sort_ascending); | ||
2249 | 2400 | ||
2250 | LLString imagename; | 2401 | LLString imagename; |
2251 | child->getAttributeString("image", imagename); | 2402 | child->getAttributeString("image", imagename); |
@@ -2267,6 +2418,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac | |||
2267 | 2418 | ||
2268 | columns[index]["name"] = columnname; | 2419 | columns[index]["name"] = columnname; |
2269 | columns[index]["sort"] = sortname; | 2420 | columns[index]["sort"] = sortname; |
2421 | columns[index]["sort_ascending"] = sort_ascending; | ||
2270 | columns[index]["image"] = imagename; | 2422 | columns[index]["image"] = imagename; |
2271 | columns[index]["label"] = labelname; | 2423 | columns[index]["label"] = labelname; |
2272 | columns[index]["width"] = columnwidth; | 2424 | columns[index]["width"] = columnwidth; |
@@ -2427,10 +2579,17 @@ BOOL LLScrollListCtrl::canDeselect() | |||
2427 | void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) | 2579 | void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) |
2428 | { | 2580 | { |
2429 | LLString name = column["name"].asString(); | 2581 | LLString name = column["name"].asString(); |
2430 | if (mColumns.size() == 0) | 2582 | if (mColumns.empty()) |
2431 | { | 2583 | { |
2432 | mDefaultColumn = 0; | 2584 | mDefaultColumn = 0; |
2433 | } | 2585 | } |
2586 | // if no column name provided, just use ordinal as name | ||
2587 | if (name.empty()) | ||
2588 | { | ||
2589 | std::ostringstream new_name; | ||
2590 | new_name << mColumnsIndexed.size(); | ||
2591 | name = new_name.str(); | ||
2592 | } | ||
2434 | if (mColumns.find(name) == mColumns.end()) | 2593 | if (mColumns.find(name) == mColumns.end()) |
2435 | { | 2594 | { |
2436 | // Add column | 2595 | // Add column |
@@ -2470,30 +2629,28 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) | |||
2470 | right += mColumnPadding; | 2629 | right += mColumnPadding; |
2471 | } | 2630 | } |
2472 | LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top); | 2631 | LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top); |
2473 | new_column->mButton = new LLSquareButton(button_name, temp_rect, "", mHeadingFont, "", onClickColumn, NULL); | 2632 | new_column->mHeader = new LLColumnHeader(button_name, temp_rect, new_column); |
2474 | if(column["image"].asString() != "") | 2633 | if(column["image"].asString() != "") |
2475 | { | 2634 | { |
2476 | //new_column->mButton->setScaleImage(false); | 2635 | //new_column->mHeader->setScaleImage(false); |
2477 | new_column->mButton->setImageSelected(column["image"].asString()); | 2636 | new_column->mHeader->setImage(column["image"].asString()); |
2478 | new_column->mButton->setImageUnselected(column["image"].asString()); | ||
2479 | } | 2637 | } |
2480 | else | 2638 | else |
2481 | { | 2639 | { |
2482 | new_column->mButton->setLabelSelected(new_column->mLabel); | 2640 | new_column->mHeader->setLabel(new_column->mLabel); |
2483 | new_column->mButton->setLabelUnselected(new_column->mLabel); | 2641 | //new_column->mHeader->setLabel(new_column->mLabel); |
2484 | } | 2642 | } |
2485 | //RN: although it might be useful to change sort order with the keyboard, | 2643 | //RN: although it might be useful to change sort order with the keyboard, |
2486 | // mixing tab stops on child items along with the parent item is not supported yet | 2644 | // mixing tab stops on child items along with the parent item is not supported yet |
2487 | new_column->mButton->setTabStop(FALSE); | 2645 | new_column->mHeader->setTabStop(FALSE); |
2488 | addChild(new_column->mButton); | 2646 | addChild(new_column->mHeader); |
2489 | new_column->mButton->setVisible(mDisplayColumnButtons); | 2647 | new_column->mHeader->setVisible(mDisplayColumnHeaders); |
2490 | 2648 | ||
2491 | 2649 | ||
2492 | // Move scroll to front | 2650 | // Move scroll to front |
2493 | removeChild(mScrollbar); | 2651 | removeChild(mScrollbar); |
2494 | addChild(mScrollbar); | 2652 | addChild(mScrollbar); |
2495 | 2653 | ||
2496 | new_column->mButton->setCallbackUserData(new_column); | ||
2497 | } | 2654 | } |
2498 | } | 2655 | } |
2499 | updateColumns(); | 2656 | updateColumns(); |
@@ -2511,6 +2668,7 @@ void LLScrollListCtrl::onClickColumn(void *userdata) | |||
2511 | U32 column_index = info->mIndex; | 2668 | U32 column_index = info->mIndex; |
2512 | 2669 | ||
2513 | LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; | 2670 | LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; |
2671 | bool ascending = column->mSortAscending; | ||
2514 | if (column->mSortingColumn != column->mName) | 2672 | if (column->mSortingColumn != column->mName) |
2515 | { | 2673 | { |
2516 | if (parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) | 2674 | if (parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) |
@@ -2520,7 +2678,6 @@ void LLScrollListCtrl::onClickColumn(void *userdata) | |||
2520 | } | 2678 | } |
2521 | } | 2679 | } |
2522 | 2680 | ||
2523 | bool ascending = true; | ||
2524 | if (column_index == parent->mSortColumn) | 2681 | if (column_index == parent->mSortColumn) |
2525 | { | 2682 | { |
2526 | ascending = !parent->mSortAscending; | 2683 | ascending = !parent->mSortAscending; |
@@ -2536,7 +2693,7 @@ void LLScrollListCtrl::onClickColumn(void *userdata) | |||
2536 | 2693 | ||
2537 | std::string LLScrollListCtrl::getSortColumnName() | 2694 | std::string LLScrollListCtrl::getSortColumnName() |
2538 | { | 2695 | { |
2539 | LLScrollListColumn* column = mColumnsIndexed[mSortColumn]; | 2696 | LLScrollListColumn* column = mSortColumn >= 0 ? mColumnsIndexed[mSortColumn] : NULL; |
2540 | 2697 | ||
2541 | if (column) return column->mName; | 2698 | if (column) return column->mName; |
2542 | else return ""; | 2699 | else return ""; |
@@ -2547,11 +2704,11 @@ void LLScrollListCtrl::clearColumns() | |||
2547 | std::map<LLString, LLScrollListColumn>::iterator itor; | 2704 | std::map<LLString, LLScrollListColumn>::iterator itor; |
2548 | for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) | 2705 | for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) |
2549 | { | 2706 | { |
2550 | LLButton *button = itor->second.mButton; | 2707 | LLColumnHeader *header = itor->second.mHeader; |
2551 | if (button) | 2708 | if (header) |
2552 | { | 2709 | { |
2553 | removeChild(button); | 2710 | removeChild(header); |
2554 | delete button; | 2711 | delete header; |
2555 | } | 2712 | } |
2556 | } | 2713 | } |
2557 | mColumns.clear(); | 2714 | mColumns.clear(); |
@@ -2563,14 +2720,22 @@ void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& la | |||
2563 | if (itor != mColumns.end()) | 2720 | if (itor != mColumns.end()) |
2564 | { | 2721 | { |
2565 | itor->second.mLabel = label; | 2722 | itor->second.mLabel = label; |
2566 | if (itor->second.mButton) | 2723 | if (itor->second.mHeader) |
2567 | { | 2724 | { |
2568 | itor->second.mButton->setLabelSelected(label); | 2725 | itor->second.mHeader->setLabel(label); |
2569 | itor->second.mButton->setLabelUnselected(label); | ||
2570 | } | 2726 | } |
2571 | } | 2727 | } |
2572 | } | 2728 | } |
2573 | 2729 | ||
2730 | LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index) | ||
2731 | { | ||
2732 | if (index < 0 || index >= (S32)mColumnsIndexed.size()) | ||
2733 | { | ||
2734 | return NULL; | ||
2735 | } | ||
2736 | return mColumnsIndexed[index]; | ||
2737 | } | ||
2738 | |||
2574 | void LLScrollListCtrl::setColumnHeadings(LLSD headings) | 2739 | void LLScrollListCtrl::setColumnHeadings(LLSD headings) |
2575 | { | 2740 | { |
2576 | mColumns.clear(); | 2741 | mColumns.clear(); |
@@ -2597,6 +2762,7 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p | |||
2597 | // Add any columns we don't already have | 2762 | // Add any columns we don't already have |
2598 | LLSD columns = value["columns"]; | 2763 | LLSD columns = value["columns"]; |
2599 | LLSD::array_const_iterator itor; | 2764 | LLSD::array_const_iterator itor; |
2765 | S32 col_index = 0 ; | ||
2600 | for (itor = columns.beginArray(); itor != columns.endArray(); ++itor) | 2766 | for (itor = columns.beginArray(); itor != columns.endArray(); ++itor) |
2601 | { | 2767 | { |
2602 | LLString column = (*itor)["column"].asString(); | 2768 | LLString column = (*itor)["column"].asString(); |
@@ -2605,21 +2771,39 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p | |||
2605 | { | 2771 | { |
2606 | mDefaultColumn = 0; | 2772 | mDefaultColumn = 0; |
2607 | } | 2773 | } |
2608 | std::map<LLString, LLScrollListColumn>::iterator column_itor = mColumns.find(column); | 2774 | |
2609 | if (column_itor == mColumns.end()) | 2775 | LLScrollListColumn* columnp = NULL; |
2776 | |||
2777 | // empty columns strings index by ordinal | ||
2778 | if (column.empty()) | ||
2779 | { | ||
2780 | std::ostringstream new_name; | ||
2781 | new_name << col_index; | ||
2782 | column = new_name.str(); | ||
2783 | } | ||
2784 | |||
2785 | std::map<LLString, LLScrollListColumn>::iterator column_itor; | ||
2786 | column_itor = mColumns.find(column); | ||
2787 | if (column_itor != mColumns.end()) | ||
2788 | { | ||
2789 | columnp = &column_itor->second; | ||
2790 | } | ||
2791 | |||
2792 | // create new column on demand | ||
2793 | if (!columnp) | ||
2610 | { | 2794 | { |
2611 | LLSD new_column; | 2795 | LLSD new_column; |
2612 | new_column["name"] = column; | 2796 | new_column["name"] = column; |
2613 | new_column["label"] = column; | 2797 | new_column["label"] = column; |
2614 | new_column["width"] = 0; | 2798 | new_column["width"] = (*itor)["width"]; |
2615 | addColumn(new_column); | 2799 | addColumn(new_column); |
2616 | column_itor = mColumns.find(column); | 2800 | columnp = &mColumns.find(column)->second; |
2617 | new_item->setNumColumns(mColumns.size()); | 2801 | new_item->setNumColumns(mColumns.size()); |
2618 | } | 2802 | } |
2619 | 2803 | ||
2620 | S32 index = column_itor->second.mIndex; | 2804 | S32 index = columnp->mIndex; |
2621 | S32 width = column_itor->second.mWidth; | 2805 | S32 width = columnp->mWidth; |
2622 | LLFontGL::HAlign font_alignment = column_itor->second.mFontAlignment; | 2806 | LLFontGL::HAlign font_alignment = columnp->mFontAlignment; |
2623 | 2807 | ||
2624 | LLSD value = (*itor)["value"]; | 2808 | LLSD value = (*itor)["value"]; |
2625 | LLString fontname = (*itor)["font"].asString(); | 2809 | LLString fontname = (*itor)["font"].asString(); |
@@ -2645,10 +2829,20 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p | |||
2645 | LLRect(0, 0, width, width), "label"); | 2829 | LLRect(0, 0, width, width), "label"); |
2646 | new_item->setColumn(index, new LLScrollListCheck(ctrl,width)); | 2830 | new_item->setColumn(index, new LLScrollListCheck(ctrl,width)); |
2647 | } | 2831 | } |
2832 | else if (type == "separator") | ||
2833 | { | ||
2834 | new_item->setColumn(index, new LLScrollListSeparator(width)); | ||
2835 | } | ||
2648 | else | 2836 | else |
2649 | { | 2837 | { |
2650 | new_item->setColumn(index, new LLScrollListText(value.asString(), font, width, font_style, font_alignment)); | 2838 | new_item->setColumn(index, new LLScrollListText(value.asString(), font, width, font_style, font_alignment)); |
2839 | if (columnp->mHeader && !value.asString().empty()) | ||
2840 | { | ||
2841 | columnp->mHeader->setHasResizableElement(TRUE); | ||
2842 | } | ||
2651 | } | 2843 | } |
2844 | |||
2845 | col_index++; | ||
2652 | } | 2846 | } |
2653 | 2847 | ||
2654 | S32 num_columns = mColumns.size(); | 2848 | S32 num_columns = mColumns.size(); |
@@ -2744,6 +2938,14 @@ void LLScrollListCtrl::setFocus(BOOL b) | |||
2744 | } | 2938 | } |
2745 | LLUICtrl::setFocus(b); | 2939 | LLUICtrl::setFocus(b); |
2746 | } | 2940 | } |
2941 | |||
2942 | //virtual | ||
2943 | void LLScrollListCtrl::onFocusReceived() | ||
2944 | { | ||
2945 | // forget latent selection changes when getting focus | ||
2946 | mSelectionChanged = FALSE; | ||
2947 | } | ||
2948 | |||
2747 | //virtual | 2949 | //virtual |
2748 | void LLScrollListCtrl::onFocusLost() | 2950 | void LLScrollListCtrl::onFocusLost() |
2749 | { | 2951 | { |
@@ -2754,5 +2956,411 @@ void LLScrollListCtrl::onFocusLost() | |||
2754 | getParent()->onFocusLost(); | 2956 | getParent()->onFocusLost(); |
2755 | } | 2957 | } |
2756 | } | 2958 | } |
2959 | if (hasMouseCapture()) | ||
2960 | { | ||
2961 | gFocusMgr.setMouseCapture(NULL); | ||
2962 | } | ||
2963 | LLUICtrl::onFocusLost(); | ||
2757 | } | 2964 | } |
2758 | 2965 | ||
2966 | LLColumnHeader::LLColumnHeader(const LLString& label, const LLRect &rect, LLScrollListColumn* column, const LLFontGL* fontp) : | ||
2967 | LLComboBox(label, rect, label, NULL, NULL), | ||
2968 | mColumn(column), | ||
2969 | mOrigLabel(label), | ||
2970 | mShowSortOptions(FALSE), | ||
2971 | mHasResizableElement(FALSE) | ||
2972 | { | ||
2973 | mListPosition = LLComboBox::ABOVE; | ||
2974 | setCommitCallback(onSelectSort); | ||
2975 | setCallbackUserData(this); | ||
2976 | mButton->setTabStop(FALSE); | ||
2977 | // require at least two frames between mouse down and mouse up event to capture intentional "hold" not just bad framerate | ||
2978 | mButton->setHeldDownDelay(LLUI::sConfigGroup->getF32("ColumnHeaderDropDownDelay"), 2); | ||
2979 | mButton->setHeldDownCallback(onHeldDown); | ||
2980 | mButton->setClickedCallback(onClick); | ||
2981 | mButton->setMouseDownCallback(onMouseDown); | ||
2982 | |||
2983 | mButton->setCallbackUserData(this); | ||
2984 | |||
2985 | mAscendingText = "[LOW]...[HIGH](Ascending)"; | ||
2986 | mDescendingText = "[HIGH]...[LOW](Descending)"; | ||
2987 | |||
2988 | mList->reshape(llmax(mList->getRect().getWidth(), 110, mRect.getWidth()), mList->getRect().getHeight()); | ||
2989 | |||
2990 | // resize handles on left and right | ||
2991 | const S32 RESIZE_BAR_THICKNESS = 3; | ||
2992 | mResizeBar = new LLResizeBar( | ||
2993 | "resizebar", | ||
2994 | LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0), | ||
2995 | MIN_COLUMN_WIDTH, mRect.getHeight(), LLResizeBar::RIGHT ); | ||
2996 | addChild(mResizeBar); | ||
2997 | |||
2998 | mResizeBar->setEnabled(FALSE); | ||
2999 | } | ||
3000 | |||
3001 | LLColumnHeader::~LLColumnHeader() | ||
3002 | { | ||
3003 | } | ||
3004 | |||
3005 | void LLColumnHeader::draw() | ||
3006 | { | ||
3007 | if( getVisible() ) | ||
3008 | { | ||
3009 | mDrawArrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn; | ||
3010 | |||
3011 | BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); | ||
3012 | mArrowImage = is_ascending ? LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("up_arrow.tga"))) | ||
3013 | : LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("down_arrow.tga"))); | ||
3014 | |||
3015 | //BOOL clip = mRect.mRight > mColumn->mParentCtrl->getItemListRect().getWidth(); | ||
3016 | //LLGLEnable scissor_test(clip ? GL_SCISSOR_TEST : GL_FALSE); | ||
3017 | |||
3018 | //LLRect column_header_local_rect(-mRect.mLeft, mRect.getHeight(), mColumn->mParentCtrl->getItemListRect().getWidth() - mRect.mLeft, 0); | ||
3019 | //LLUI::setScissorRegionLocal(column_header_local_rect); | ||
3020 | |||
3021 | // Draw children | ||
3022 | LLComboBox::draw(); | ||
3023 | |||
3024 | if (mList->getVisible()) | ||
3025 | { | ||
3026 | // sync sort order with list selection every frame | ||
3027 | mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn, getCurrentIndex() == 0); | ||
3028 | } | ||
3029 | |||
3030 | } | ||
3031 | } | ||
3032 | |||
3033 | BOOL LLColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) | ||
3034 | { | ||
3035 | if (canResize() && mResizeBar->getRect().pointInRect(x, y)) | ||
3036 | { | ||
3037 | // reshape column to max content width | ||
3038 | LLRect column_rect = getRect(); | ||
3039 | column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth; | ||
3040 | userSetShape(column_rect); | ||
3041 | } | ||
3042 | else | ||
3043 | { | ||
3044 | onClick(this); | ||
3045 | } | ||
3046 | return TRUE; | ||
3047 | } | ||
3048 | |||
3049 | void LLColumnHeader::setImage(const LLString &image_name) | ||
3050 | { | ||
3051 | if (mButton) | ||
3052 | { | ||
3053 | mButton->setImageSelected(image_name); | ||
3054 | mButton->setImageUnselected(image_name); | ||
3055 | } | ||
3056 | } | ||
3057 | |||
3058 | //static | ||
3059 | void LLColumnHeader::onClick(void* user_data) | ||
3060 | { | ||
3061 | LLColumnHeader* headerp = (LLColumnHeader*)user_data; | ||
3062 | if (!headerp) return; | ||
3063 | |||
3064 | LLScrollListColumn* column = headerp->mColumn; | ||
3065 | if (!column) return; | ||
3066 | |||
3067 | if (headerp->mList->getVisible()) | ||
3068 | { | ||
3069 | headerp->hideList(); | ||
3070 | } | ||
3071 | |||
3072 | LLScrollListCtrl::onClickColumn(column); | ||
3073 | |||
3074 | // propage new sort order to sort order list | ||
3075 | headerp->mList->selectNthItem(column->mParentCtrl->getSortAscending() ? 0 : 1); | ||
3076 | } | ||
3077 | |||
3078 | //static | ||
3079 | void LLColumnHeader::onMouseDown(void* user_data) | ||
3080 | { | ||
3081 | // for now, do nothing but block the normal showList() behavior | ||
3082 | return; | ||
3083 | } | ||
3084 | |||
3085 | //static | ||
3086 | void LLColumnHeader::onHeldDown(void* user_data) | ||
3087 | { | ||
3088 | LLColumnHeader* headerp = (LLColumnHeader*)user_data; | ||
3089 | headerp->showList(); | ||
3090 | } | ||
3091 | |||
3092 | void LLColumnHeader::showList() | ||
3093 | { | ||
3094 | if (mShowSortOptions) | ||
3095 | { | ||
3096 | //LLSD item_val = mColumn->mParentCtrl->getFirstData()->getValue(); | ||
3097 | mOrigLabel = mButton->getLabelSelected(); | ||
3098 | |||
3099 | // move sort column over to this column and do initial sort | ||
3100 | mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn, mColumn->mParentCtrl->getSortAscending()); | ||
3101 | |||
3102 | LLString low_item_text; | ||
3103 | LLString high_item_text; | ||
3104 | |||
3105 | LLScrollListItem* itemp = mColumn->mParentCtrl->getFirstData(); | ||
3106 | if (itemp) | ||
3107 | { | ||
3108 | LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex); | ||
3109 | if (cell && cell->isText()) | ||
3110 | { | ||
3111 | if (mColumn->mParentCtrl->getSortAscending()) | ||
3112 | { | ||
3113 | low_item_text = cell->getText(); | ||
3114 | } | ||
3115 | else | ||
3116 | { | ||
3117 | high_item_text = cell->getText(); | ||
3118 | } | ||
3119 | } | ||
3120 | } | ||
3121 | |||
3122 | itemp = mColumn->mParentCtrl->getLastData(); | ||
3123 | if (itemp) | ||
3124 | { | ||
3125 | LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex); | ||
3126 | if (cell && cell->isText()) | ||
3127 | { | ||
3128 | if (mColumn->mParentCtrl->getSortAscending()) | ||
3129 | { | ||
3130 | high_item_text = cell->getText(); | ||
3131 | } | ||
3132 | else | ||
3133 | { | ||
3134 | low_item_text = cell->getText(); | ||
3135 | } | ||
3136 | } | ||
3137 | } | ||
3138 | |||
3139 | LLString::truncate(low_item_text, 3); | ||
3140 | LLString::truncate(high_item_text, 3); | ||
3141 | |||
3142 | LLString ascending_string; | ||
3143 | LLString descending_string; | ||
3144 | |||
3145 | if (low_item_text.empty() || high_item_text.empty()) | ||
3146 | { | ||
3147 | ascending_string = "Ascending"; | ||
3148 | descending_string = "Descending"; | ||
3149 | } | ||
3150 | else | ||
3151 | { | ||
3152 | mAscendingText.setArg("[LOW]", low_item_text); | ||
3153 | mAscendingText.setArg("[HIGH]", high_item_text); | ||
3154 | mDescendingText.setArg("[LOW]", low_item_text); | ||
3155 | mDescendingText.setArg("[HIGH]", high_item_text); | ||
3156 | ascending_string = mAscendingText.getString(); | ||
3157 | descending_string = mDescendingText.getString(); | ||
3158 | } | ||
3159 | |||
3160 | S32 text_width = LLFontGL::sSansSerifSmall->getWidth(ascending_string); | ||
3161 | text_width = llmax(text_width, LLFontGL::sSansSerifSmall->getWidth(descending_string)) + 10; | ||
3162 | text_width = llmax(text_width, mRect.getWidth() - 30); | ||
3163 | |||
3164 | mList->getColumn(0)->mWidth = text_width; | ||
3165 | ((LLScrollListText*)mList->getFirstData()->getColumn(0))->setText(ascending_string); | ||
3166 | ((LLScrollListText*)mList->getLastData()->getColumn(0))->setText(descending_string); | ||
3167 | |||
3168 | mList->reshape(llmax(text_width + 30, 110, mRect.getWidth()), mList->getRect().getHeight()); | ||
3169 | |||
3170 | LLComboBox::showList(); | ||
3171 | } | ||
3172 | } | ||
3173 | |||
3174 | //static | ||
3175 | void LLColumnHeader::onSelectSort(LLUICtrl* ctrl, void* user_data) | ||
3176 | { | ||
3177 | LLColumnHeader* headerp = (LLColumnHeader*)user_data; | ||
3178 | if (!headerp) return; | ||
3179 | |||
3180 | LLScrollListColumn* column = headerp->mColumn; | ||
3181 | if (!column) return; | ||
3182 | LLScrollListCtrl *parent = column->mParentCtrl; | ||
3183 | if (!parent) return; | ||
3184 | |||
3185 | if (headerp->getCurrentIndex() == 0) | ||
3186 | { | ||
3187 | // ascending | ||
3188 | parent->sortByColumn(column->mSortingColumn, TRUE); | ||
3189 | } | ||
3190 | else | ||
3191 | { | ||
3192 | // descending | ||
3193 | parent->sortByColumn(column->mSortingColumn, FALSE); | ||
3194 | } | ||
3195 | |||
3196 | // restore original column header | ||
3197 | headerp->setLabel(headerp->mOrigLabel); | ||
3198 | } | ||
3199 | |||
3200 | LLView* LLColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding) | ||
3201 | { | ||
3202 | // this logic assumes dragging on right | ||
3203 | llassert(snap_edge == SNAP_RIGHT); | ||
3204 | |||
3205 | // use higher snap threshold for column headers | ||
3206 | threshold = llmin(threshold, 15); | ||
3207 | |||
3208 | LLRect snap_rect = getSnapRect(); | ||
3209 | |||
3210 | S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth(); | ||
3211 | |||
3212 | // x coord growing means column growing, so same signs mean we're going in right direction | ||
3213 | if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) | ||
3214 | { | ||
3215 | new_edge_val = snap_rect.mRight + snap_delta; | ||
3216 | } | ||
3217 | else | ||
3218 | { | ||
3219 | LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1); | ||
3220 | while (next_column) | ||
3221 | { | ||
3222 | if (next_column->mHeader) | ||
3223 | { | ||
3224 | snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight; | ||
3225 | if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) | ||
3226 | { | ||
3227 | new_edge_val = snap_rect.mRight + snap_delta; | ||
3228 | } | ||
3229 | break; | ||
3230 | } | ||
3231 | next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1); | ||
3232 | } | ||
3233 | } | ||
3234 | |||
3235 | return this; | ||
3236 | } | ||
3237 | |||
3238 | void LLColumnHeader::userSetShape(const LLRect& new_rect) | ||
3239 | { | ||
3240 | S32 new_width = new_rect.getWidth(); | ||
3241 | S32 delta_width = new_width - (mRect.getWidth() + mColumn->mParentCtrl->getColumnPadding()); | ||
3242 | |||
3243 | if (delta_width != 0) | ||
3244 | { | ||
3245 | S32 remaining_width = delta_width; | ||
3246 | S32 col; | ||
3247 | for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++) | ||
3248 | { | ||
3249 | LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); | ||
3250 | if (!columnp) break; | ||
3251 | |||
3252 | if (columnp->mHeader && columnp->mHeader->canResize()) | ||
3253 | { | ||
3254 | // how many pixels in width can this column afford to give up? | ||
3255 | S32 resize_buffer_amt = llmax(0, columnp->mWidth - MIN_COLUMN_WIDTH); | ||
3256 | |||
3257 | // user shrinking column, need to add width to other columns | ||
3258 | if (delta_width < 0) | ||
3259 | { | ||
3260 | if (!columnp->mDynamicWidth && columnp->mWidth > 0) | ||
3261 | { | ||
3262 | // statically sized column, give all remaining width to this column | ||
3263 | columnp->mWidth -= remaining_width; | ||
3264 | if (columnp->mRelWidth > 0.f) | ||
3265 | { | ||
3266 | columnp->mRelWidth = (F32)columnp->mWidth / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); | ||
3267 | } | ||
3268 | } | ||
3269 | break; | ||
3270 | } | ||
3271 | else | ||
3272 | { | ||
3273 | // user growing column, need to take width from other columns | ||
3274 | remaining_width -= resize_buffer_amt; | ||
3275 | |||
3276 | if (!columnp->mDynamicWidth && columnp->mWidth > 0) | ||
3277 | { | ||
3278 | columnp->mWidth -= llmin(columnp->mWidth - MIN_COLUMN_WIDTH, delta_width); | ||
3279 | if (columnp->mRelWidth > 0.f) | ||
3280 | { | ||
3281 | columnp->mRelWidth = (F32)columnp->mWidth / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); | ||
3282 | } | ||
3283 | } | ||
3284 | |||
3285 | if (remaining_width <= 0) | ||
3286 | { | ||
3287 | // width sucked up from neighboring columns, done | ||
3288 | break; | ||
3289 | } | ||
3290 | } | ||
3291 | } | ||
3292 | } | ||
3293 | |||
3294 | // clamp resize amount to maximum that can be absorbed by other columns | ||
3295 | if (delta_width > 0) | ||
3296 | { | ||
3297 | delta_width -= llmax(remaining_width, 0); | ||
3298 | } | ||
3299 | |||
3300 | // propagate constrained delta_width to new width for this column | ||
3301 | new_width = mRect.getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding(); | ||
3302 | |||
3303 | // use requested width | ||
3304 | mColumn->mWidth = new_width; | ||
3305 | |||
3306 | // update proportional spacing | ||
3307 | if (mColumn->mRelWidth > 0.f) | ||
3308 | { | ||
3309 | mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); | ||
3310 | } | ||
3311 | |||
3312 | // tell scroll list to layout columns again | ||
3313 | mColumn->mParentCtrl->updateColumns(); | ||
3314 | } | ||
3315 | } | ||
3316 | |||
3317 | void LLColumnHeader::setHasResizableElement(BOOL resizable) | ||
3318 | { | ||
3319 | // for now, dynamically spaced columns can't be resized | ||
3320 | if (mColumn->mDynamicWidth) return; | ||
3321 | |||
3322 | if (resizable != mHasResizableElement) | ||
3323 | { | ||
3324 | mHasResizableElement = resizable; | ||
3325 | |||
3326 | S32 num_resizable_columns = 0; | ||
3327 | S32 col; | ||
3328 | for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) | ||
3329 | { | ||
3330 | LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); | ||
3331 | if (columnp->mHeader && columnp->mHeader->canResize()) | ||
3332 | { | ||
3333 | num_resizable_columns++; | ||
3334 | } | ||
3335 | } | ||
3336 | |||
3337 | S32 num_resizers_enabled = 0; | ||
3338 | |||
3339 | // now enable/disable resize handles on resizable columns if we have at least two | ||
3340 | for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) | ||
3341 | { | ||
3342 | LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); | ||
3343 | if (!columnp->mHeader) continue; | ||
3344 | BOOL enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize(); | ||
3345 | columnp->mHeader->enableResizeBar(enable); | ||
3346 | if (enable) | ||
3347 | { | ||
3348 | num_resizers_enabled++; | ||
3349 | } | ||
3350 | } | ||
3351 | } | ||
3352 | } | ||
3353 | |||
3354 | void LLColumnHeader::enableResizeBar(BOOL enable) | ||
3355 | { | ||
3356 | // for now, dynamically spaced columns can't be resized | ||
3357 | if (!mColumn->mDynamicWidth) | ||
3358 | { | ||
3359 | mResizeBar->setEnabled(enable); | ||
3360 | } | ||
3361 | } | ||
3362 | |||
3363 | BOOL LLColumnHeader::canResize() | ||
3364 | { | ||
3365 | return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth); | ||
3366 | } | ||