aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llui/llscrolllistctrl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llui/llscrolllistctrl.cpp')
-rw-r--r--linden/indra/llui/llscrolllistctrl.cpp972
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
52const S32 LIST_BORDER_PAD = 2; // white space inside the border and to the left of the scrollbar 54const S32 LIST_BORDER_PAD = 2; // white space inside the border and to the left of the scrollbar
55const S32 MIN_COLUMN_WIDTH = 20;
56const S32 LIST_SNAP_PADDING = 5;
53 57
54// local structures & classes. 58// local structures & classes.
55struct SortScrollListItem 59struct SortScrollListItem
@@ -152,6 +156,19 @@ BOOL LLScrollListCheck::handleClick()
152} 156}
153 157
154// 158//
159// LLScrollListSeparator
160//
161LLScrollListSeparator::LLScrollListSeparator(S32 width) : mWidth(width)
162{
163}
164
165void 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//
157U32 LLScrollListText::sCount = 0; 174U32 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
276BOOL LLScrollListItem::handleMouseDown(S32 x, S32 y, MASK mask) 293BOOL 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
550LLScrollListItem* LLScrollListCtrl::getFirstData() const 566LLScrollListItem* 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
575LLScrollListItem* LLScrollListCtrl::getLastData() const
576{
577 if (mItemList.size() == 0)
578 {
579 return NULL;
580 }
581 return mItemList[mItemList.size() - 1];
582}
583
559std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const 584std::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.
595void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) 618void 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
691void 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.
655void LLScrollListCtrl::updateLineHeight() 726void 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
694void 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
732void LLScrollListCtrl::setDisplayHeading(BOOL display) 825void 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
753void LLScrollListCtrl::setHeadingFont(const LLFontGL* heading_font)
754{
755 mHeadingFont = heading_font;
756 updateColumnButtons();
757} 842}
758 843
759void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse) 844void 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
878void LLScrollListCtrl::deleteSelectedItems() 964void 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
897void LLScrollListCtrl::highlightNthItem(S32 target_index) 984void 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 1576BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask)
1490BOOL 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
1679BOOL 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
1698BOOL 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
1592BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) 1723BOOL 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
2118void LLScrollListCtrl::setSorted(BOOL sorted)
2119{
2120 mSorted = sorted;
2121}
2122
2123BOOL LLScrollListCtrl::isSorted()
2124{
2125 return mSorted;
2126}
2127
1983// Called by scrollbar 2128// Called by scrollbar
1984//static 2129//static
1985void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) 2130void 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
1993void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) 2138void 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
2000void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) 2155void 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()
2427void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) 2579void 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
2537std::string LLScrollListCtrl::getSortColumnName() 2694std::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
2730LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index)
2731{
2732 if (index < 0 || index >= (S32)mColumnsIndexed.size())
2733 {
2734 return NULL;
2735 }
2736 return mColumnsIndexed[index];
2737}
2738
2574void LLScrollListCtrl::setColumnHeadings(LLSD headings) 2739void 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
2943void LLScrollListCtrl::onFocusReceived()
2944{
2945 // forget latent selection changes when getting focus
2946 mSelectionChanged = FALSE;
2947}
2948
2747//virtual 2949//virtual
2748void LLScrollListCtrl::onFocusLost() 2950void 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
2966LLColumnHeader::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
3001LLColumnHeader::~LLColumnHeader()
3002{
3003}
3004
3005void 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
3033BOOL 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
3049void 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
3059void 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
3079void LLColumnHeader::onMouseDown(void* user_data)
3080{
3081 // for now, do nothing but block the normal showList() behavior
3082 return;
3083}
3084
3085//static
3086void LLColumnHeader::onHeldDown(void* user_data)
3087{
3088 LLColumnHeader* headerp = (LLColumnHeader*)user_data;
3089 headerp->showList();
3090}
3091
3092void 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
3175void 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
3200LLView* 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
3238void 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
3317void 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
3354void 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
3363BOOL LLColumnHeader::canResize()
3364{
3365 return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth);
3366}