diff options
Diffstat (limited to 'indra/llui/llscrolllistctrl.cpp')
-rw-r--r-- | indra/llui/llscrolllistctrl.cpp | 2271 |
1 files changed, 720 insertions, 1551 deletions
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index c923f6b116..84e438cfb7 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -1,75 +1,77 @@ /** * @file llscrolllistctrl.cpp - * @brief LLScrollListCtrl base class + * @brief Scroll lists are composed of rows (items), each of which + * contains columns (cells). * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -#include <algorithm> - #include "linden_common.h" -#include "llstl.h" -#include "llboost.h" #include "llscrolllistctrl.h" -#include "indra_constants.h" +#include <algorithm> + +#include "llstl.h" +#include "llboost.h" +//#include "indra_constants.h" #include "llcheckboxctrl.h" #include "llclipboard.h" #include "llfocusmgr.h" -#include "llglimmediate.h" +#include "llgl.h" // LLGLSUIDefault() +#include "lllocalcliprect.h" +//#include "llrender.h" #include "llresmgr.h" #include "llscrollbar.h" +#include "llscrolllistcell.h" #include "llstring.h" #include "llui.h" #include "lluictrlfactory.h" #include "llwindow.h" #include "llcontrol.h" #include "llkeyboard.h" -#include "llresizebar.h" +#include "llviewborder.h" +#include "lltextbox.h" +#include "llsdparam.h" +#include "llcachename.h" +#include "llmenugl.h" +#include "llurlaction.h" +#include "lltooltip.h" -const S32 MIN_COLUMN_WIDTH = 20; -const S32 LIST_SNAP_PADDING = 5; +#include <boost/bind.hpp> -static LLRegisterWidget<LLScrollListCtrl> r("scroll_list"); +static LLDefaultChildRegistry::Register<LLScrollListCtrl> r("scroll_list"); // local structures & classes. struct SortScrollListItem { - SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders) + SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal) : mSortOrders(sort_orders) + , mSortSignal(sort_signal) {} bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2) { - if ( mSortOrders.empty() ) - return i1 < i2; - // sort over all columns in order specified by mSortOrders S32 sort_result = 0; for (sort_order_t::const_reverse_iterator it = mSortOrders.rbegin(); @@ -78,12 +80,20 @@ struct SortScrollListItem S32 col_idx = it->first; BOOL sort_ascending = it->second; + S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column? + const LLScrollListCell *cell1 = i1->getColumn(col_idx); const LLScrollListCell *cell2 = i2->getColumn(col_idx); - S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column? if (cell1 && cell2) { - sort_result = order * LLString::compareDict(cell1->getValue().asString(), cell2->getValue().asString()); + if(mSortSignal) + { + sort_result = order * (*mSortSignal)(col_idx,i1, i2); + } + else + { + sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString()); + } if (sort_result != 0) { break; // we have a sort order! @@ -93,498 +103,105 @@ struct SortScrollListItem return sort_result < 0; } + typedef std::vector<std::pair<S32, BOOL> > sort_order_t; + const LLScrollListCtrl::sort_signal_t* mSortSignal; const sort_order_t& mSortOrders; }; - -// -// LLScrollListIcon -// -LLScrollListIcon::LLScrollListIcon(LLUIImagePtr icon, S32 width) - : LLScrollListCell(width), - mIcon(icon), - mColor(LLColor4::white) -{ -} - -LLScrollListIcon::LLScrollListIcon(const LLSD& value, S32 width) - : LLScrollListCell(width), - mColor(LLColor4::white) -{ - setValue(value); -} - - -LLScrollListIcon::~LLScrollListIcon() -{ -} - -void LLScrollListIcon::setValue(const LLSD& value) -{ - if (value.isUUID()) - { - // don't use default image specified by LLUUID::null, use no image in that case - LLUUID image_id = value.asUUID(); - mIcon = image_id.notNull() ? LLUI::sImageProvider->getUIImageByID(image_id) : LLUIImagePtr(NULL); - } - else - { - LLString value_string = value.asString(); - if (LLUUID::validate(value_string)) - { - setValue(LLUUID(value_string)); - } - else if (!value_string.empty()) - { - mIcon = LLUI::getUIImage(value.asString()); - } - else - { - mIcon = NULL; - } - } -} - - -void LLScrollListIcon::setColor(const LLColor4& color) -{ - mColor = color; -} - -S32 LLScrollListIcon::getWidth() const -{ - // if no specified fix width, use width of icon - if (LLScrollListCell::getWidth() == 0 && mIcon.notNull()) - { - return mIcon->getWidth(); - } - return LLScrollListCell::getWidth(); -} - - -void LLScrollListIcon::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - if (mIcon) - { - mIcon->draw(0, 0, mColor); - } -} - -// -// LLScrollListCheck -// -LLScrollListCheck::LLScrollListCheck(LLCheckBoxCtrl* check_box, S32 width) -{ - mCheckBox = check_box; - LLRect rect(mCheckBox->getRect()); - if (width) - { - - rect.mRight = rect.mLeft + width; - mCheckBox->setRect(rect); - setWidth(width); - } - else - { - setWidth(rect.getWidth()); //check_box->getWidth(); - } -} - -LLScrollListCheck::~LLScrollListCheck() -{ - delete mCheckBox; -} - -void LLScrollListCheck::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - mCheckBox->draw(); -} - -BOOL LLScrollListCheck::handleClick() -{ - if (mCheckBox->getEnabled()) - { - mCheckBox->toggle(); - } - // don't change selection when clicking on embedded checkbox - return TRUE; -} - -// -// LLScrollListSeparator -// -LLScrollListSeparator::LLScrollListSeparator(S32 width) : LLScrollListCell(width) -{ -} - -//virtual -S32 LLScrollListSeparator::getHeight() const -{ - return 5; -} - - -void LLScrollListSeparator::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - //*FIXME: use dynamic item heights and make separators narrow, and inactive - gl_line_2d(5, 8, llmax(5, getWidth() - 5), 8, color); -} - -// -// LLScrollListText -// -U32 LLScrollListText::sCount = 0; - -LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, S32 width, U8 font_style, LLFontGL::HAlign font_alignment, LLColor4& color, BOOL use_color, BOOL visible) -: LLScrollListCell(width), - mText( text ), - mFont( font ), - mColor(color), - mUseColor(use_color), - mFontStyle( font_style ), - mFontAlignment( font_alignment ), - mVisible( visible ), - mHighlightCount( 0 ), - mHighlightOffset( 0 ) -{ - sCount++; - - // initialize rounded rect image - if (!mRoundedRectImage) - { - mRoundedRectImage = LLUI::sImageProvider->getUIImage("rounded_square.tga"); - } -} -//virtual -void LLScrollListText::highlightText(S32 offset, S32 num_chars) -{ - mHighlightOffset = offset; - mHighlightCount = num_chars; -} - -//virtual -BOOL LLScrollListText::isText() const -{ - return TRUE; -} - -//virtual -BOOL LLScrollListText::getVisible() const -{ - return mVisible; -} - -//virtual -S32 LLScrollListText::getHeight() const -{ - return llround(mFont->getLineHeight()); -} - - -LLScrollListText::~LLScrollListText() -{ - sCount--; -} - -S32 LLScrollListText::getContentWidth() const -{ - return mFont->getWidth(mText.getString()); -} - - -void LLScrollListText::setColor(const LLColor4& color) -{ - mColor = color; - mUseColor = TRUE; -} - -void LLScrollListText::setText(const LLStringExplicit& text) -{ - mText = text; -} - -//virtual -void LLScrollListText::setValue(const LLSD& text) -{ - setText(text.asString()); -} - -//virtual -const LLSD LLScrollListText::getValue() const -{ - return LLSD(mText.getString()); -} - - -void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_color) const -{ - LLColor4 display_color; - if (mUseColor) - { - display_color = mColor; - } - else - { - display_color = color; - } - - if (mHighlightCount > 0) - { - S32 left = 0; - switch(mFontAlignment) - { - case LLFontGL::LEFT: - left = mFont->getWidth(mText.getString(), 0, mHighlightOffset); - break; - case LLFontGL::RIGHT: - left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX); - break; - case LLFontGL::HCENTER: - left = (getWidth() - mFont->getWidth(mText.getString())) / 2; - break; - } - LLRect highlight_rect(left - 2, - llround(mFont->getLineHeight()) + 1, - left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, - 1); - mRoundedRectImage->draw(highlight_rect, highlight_color); - } - - // Try to draw the entire string - F32 right_x; - U32 string_chars = mText.length(); - F32 start_x = 0.f; - switch(mFontAlignment) - { - case LLFontGL::LEFT: - start_x = 0.f; - break; - case LLFontGL::RIGHT: - start_x = (F32)getWidth(); - break; - case LLFontGL::HCENTER: - start_x = (F32)getWidth() * 0.5f; - break; - } - mFont->render(mText.getWString(), 0, - start_x, 2.f, - display_color, - mFontAlignment, - LLFontGL::BOTTOM, - mFontStyle, - string_chars, - getWidth(), - &right_x, - FALSE, - TRUE); -} - - -LLScrollListItem::~LLScrollListItem() -{ - std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); -} - -void LLScrollListItem::setNumColumns(S32 columns) -{ - S32 prev_columns = mColumns.size(); - if (columns < prev_columns) - { - std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer()); - } - - mColumns.resize(columns); - - for (S32 col = prev_columns; col < columns; ++col) - { - mColumns[col] = NULL; - } -} - -void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell ) -{ - if (column < (S32)mColumns.size()) - { - delete mColumns[column]; - mColumns[column] = cell; - } - else - { - llerrs << "LLScrollListItem::setColumn: bad column: " << column << llendl; - } -} - -LLString LLScrollListItem::getContentsCSV() const -{ - LLString ret; - - S32 count = getNumColumns(); - for (S32 i=0; i<count; ++i) - { - ret += getColumn(i)->getValue().asString(); - if (i < count-1) - { - ret += ", "; - } - } - - return ret; -} - -void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) -{ - // draw background rect - LLRect bg_rect = rect; - { - LLGLSNoTexture no_texture; - gGL.color4fv(bg_color.mV); - gl_rect_2d( bg_rect ); - } - - S32 cur_x = rect.mLeft; - S32 num_cols = getNumColumns(); - S32 cur_col = 0; - - for (LLScrollListCell* cell = getColumn(0); cur_col < num_cols; cell = getColumn(++cur_col)) - { - // Two ways a cell could be hidden - if (cell->getWidth() < 0 - || !cell->getVisible()) continue; - - LLUI::pushMatrix(); - { - LLUI::translate((F32) cur_x, (F32) rect.mBottom, 0.0f); - - cell->draw( fg_color, highlight_color ); - } - LLUI::popMatrix(); - - cur_x += cell->getWidth() + column_padding; - } -} - - -void LLScrollListItem::setEnabled(BOOL b) -{ - mEnabled = b; -} - -//--------------------------------------------------------------------------- -// LLScrollListItemComment -//--------------------------------------------------------------------------- -LLScrollListItemComment::LLScrollListItemComment(const LLString& comment_string, const LLColor4& color) -: LLScrollListItem(FALSE), - mColor(color) -{ - addColumn( comment_string, LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL ) ); -} - -void LLScrollListItemComment::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) -{ - LLScrollListCell* cell = getColumn(0); - if (cell) - { - // Two ways a cell could be hidden - if (cell->getWidth() < 0 - || !cell->getVisible()) return; - - LLUI::pushMatrix(); - { - LLUI::translate((F32)rect.mLeft, (F32)rect.mBottom, 0.0f); - - // force first cell to be width of entire item - cell->setWidth(rect.getWidth()); - cell->draw( mColor, highlight_color ); - } - LLUI::popMatrix(); - } -} - -//--------------------------------------------------------------------------- -// LLScrollListItemSeparator -//--------------------------------------------------------------------------- -LLScrollListItemSeparator::LLScrollListItemSeparator() -: LLScrollListItem(FALSE) -{ - LLScrollListSeparator* cell = new LLScrollListSeparator(0); - setNumColumns(1); - setColumn(0, cell); -} - -void LLScrollListItemSeparator::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) -{ - //TODO* move LLScrollListSeparator::draw into here and get rid of it - LLScrollListCell* cell = getColumn(0); - if (cell) - { - // Two ways a cell could be hidden - if (cell->getWidth() < 0 - || !cell->getVisible()) return; - - LLUI::pushMatrix(); - { - LLUI::translate((F32)rect.mLeft, (F32)rect.mBottom, 0.0f); - - // force first cell to be width of entire item - cell->setWidth(rect.getWidth()); - cell->draw( fg_color, highlight_color ); - } - LLUI::popMatrix(); - } -} - //--------------------------------------------------------------------------- // LLScrollListCtrl //--------------------------------------------------------------------------- -LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, - void (*commit_callback)(LLUICtrl* ctrl, void* userdata), - void* callback_user_data, - BOOL allow_multiple_selection, - BOOL show_border - ) - : LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data), +LLScrollListCtrl::Contents::Contents() +: columns("column"), + rows("row") +{ + addSynonym(columns, "columns"); + addSynonym(rows, "rows"); +} + +LLScrollListCtrl::Params::Params() +: multi_select("multi_select", false), + has_border("draw_border"), + draw_heading("draw_heading"), + search_column("search_column", 0), + sort_column("sort_column", -1), + sort_ascending("sort_ascending", true), + mouse_wheel_opaque("mouse_wheel_opaque", false), + commit_on_keyboard_movement("commit_on_keyboard_movement", true), + heading_height("heading_height"), + page_lines("page_lines", 0), + background_visible("background_visible"), + draw_stripes("draw_stripes"), + column_padding("column_padding"), + fg_unselected_color("fg_unselected_color"), + fg_selected_color("fg_selected_color"), + bg_selected_color("bg_selected_color"), + fg_disable_color("fg_disable_color"), + bg_writeable_color("bg_writeable_color"), + bg_readonly_color("bg_readonly_color"), + bg_stripe_color("bg_stripe_color"), + hovered_color("hovered_color"), + highlighted_color("highlighted_color"), + contents(""), + scroll_bar_bg_visible("scroll_bar_bg_visible"), + scroll_bar_bg_color("scroll_bar_bg_color") + , border("border") +{ + name = "scroll_list"; + mouse_opaque = true; +} + +LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) +: LLUICtrl(p), mLineHeight(0), mScrollLines(0), - mPageLines(0), - mHeadingHeight(20), + mMouseWheelOpaque(p.mouse_wheel_opaque), + mPageLines(p.page_lines), mMaxSelectable(0), - mAllowMultipleSelection( allow_multiple_selection ), mAllowKeyboardMovement(TRUE), - mCommitOnKeyboardMovement(TRUE), + mCommitOnKeyboardMovement(p.commit_on_keyboard_movement), mCommitOnSelectionChange(FALSE), mSelectionChanged(FALSE), mNeedsScroll(FALSE), mCanSelect(TRUE), - mDisplayColumnHeaders(FALSE), mColumnsDirty(FALSE), mMaxItemCount(INT_MAX), mMaxContentWidth(0), - mBackgroundVisible( TRUE ), - mDrawStripes(TRUE), - mBgWriteableColor( LLUI::sColorsGroup->getColor( "ScrollBgWriteableColor" ) ), - mBgReadOnlyColor( LLUI::sColorsGroup->getColor( "ScrollBgReadOnlyColor" ) ), - mBgSelectedColor( LLUI::sColorsGroup->getColor("ScrollSelectedBGColor") ), - mBgStripeColor( LLUI::sColorsGroup->getColor("ScrollBGStripeColor") ), - mFgSelectedColor( LLUI::sColorsGroup->getColor("ScrollSelectedFGColor") ), - mFgUnselectedColor( LLUI::sColorsGroup->getColor("ScrollUnselectedColor") ), - mFgDisabledColor( LLUI::sColorsGroup->getColor("ScrollDisabledColor") ), - mHighlightedColor( LLUI::sColorsGroup->getColor("ScrollHighlightedColor") ), mBorderThickness( 2 ), mOnDoubleClickCallback( NULL ), mOnMaximumSelectCallback( NULL ), mOnSortChangedCallback( NULL ), mHighlightedItem(-1), mBorder(NULL), - mSearchColumn(0), + mSortCallback(NULL), + mPopupMenu(NULL), mNumDynamicWidthColumns(0), mTotalStaticColumnWidth(0), - mSorted(TRUE), + mTotalColumnPadding(0), + mSorted(false), mDirty(FALSE), mOriginalSelection(-1), - mDrewSelected(FALSE) + mLastSelected(NULL), + mHeadingHeight(p.heading_height), + mAllowMultipleSelection(p.multi_select), + mDisplayColumnHeaders(p.draw_heading), + mBackgroundVisible(p.background_visible), + mDrawStripes(p.draw_stripes), + mBgWriteableColor(p.bg_writeable_color()), + mBgReadOnlyColor(p.bg_readonly_color()), + mBgSelectedColor(p.bg_selected_color()), + mBgStripeColor(p.bg_stripe_color()), + mFgSelectedColor(p.fg_selected_color()), + mFgUnselectedColor(p.fg_unselected_color()), + mFgDisabledColor(p.fg_disable_color()), + mHighlightedColor(p.highlighted_color()), + mHoveredColor(p.hovered_color()), + mSearchColumn(p.search_column), + mColumnPadding(p.column_padding), + mContextMenuType(MENU_NONE) { mItemListRect.setOriginAndSize( mBorderThickness, @@ -594,50 +211,117 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, updateLineHeight(); - mPageLines = mLineHeight? (mItemListRect.getHeight()) / mLineHeight : 0; - // Init the scrollbar + static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); + LLRect scroll_rect; scroll_rect.setOriginAndSize( - getRect().getWidth() - mBorderThickness - SCROLLBAR_SIZE, + getRect().getWidth() - mBorderThickness - scrollbar_size, mItemListRect.mBottom, - SCROLLBAR_SIZE, + scrollbar_size, mItemListRect.getHeight()); - mScrollbar = new LLScrollbar( "Scrollbar", scroll_rect, - LLScrollbar::VERTICAL, - getItemCount(), - mScrollLines, - mPageLines, - &LLScrollListCtrl::onScrollChange, this ); - mScrollbar->setFollowsRight(); - mScrollbar->setFollowsTop(); - mScrollbar->setFollowsBottom(); - mScrollbar->setEnabled( TRUE ); - // scrollbar is visible only when needed - mScrollbar->setVisible(FALSE); + + LLScrollbar::Params sbparams; + sbparams.name("Scrollbar"); + sbparams.rect(scroll_rect); + sbparams.orientation(LLScrollbar::VERTICAL); + sbparams.doc_size(getItemCount()); + sbparams.doc_pos(mScrollLines); + sbparams.page_size( getLinesPerPage() ); + sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2)); + sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + sbparams.visible(false); + sbparams.bg_visible(p.scroll_bar_bg_visible); + sbparams.bg_color(p.scroll_bar_bg_color); + mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams); addChild(mScrollbar); // Border - if (show_border) + if (p.has_border) { - LLRect border_rect( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - mBorder = new LLViewBorder( "dlg border", border_rect, LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, 1 ); + LLRect border_rect = getLocalRect(); + LLViewBorder::Params params = p.border; + params.rect(border_rect); + mBorder = LLUICtrlFactory::create<LLViewBorder> (params); addChild(mBorder); } - mColumnPadding = 5; + // set border *after* rect is fully initialized + if (mBorder) + { + mBorder->setRect(getLocalRect()); + mBorder->reshape(getRect().getWidth(), getRect().getHeight()); + } - mLastSelected = NULL; + if (p.sort_column >= 0) + { + sortByColumnIndex(p.sort_column, p.sort_ascending); + } + + + for (LLInitParam::ParamIterator<LLScrollListColumn::Params>::const_iterator row_it = p.contents.columns().begin(); + row_it != p.contents.columns().end(); + ++row_it) + { + addColumn(*row_it); + } + + for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = p.contents.rows().begin(); + row_it != p.contents.rows().end(); + ++row_it) + { + addRow(*row_it); + } + + LLTextBox::Params text_p; + text_p.name("comment_text"); + text_p.border_visible(false); + text_p.rect(mItemListRect); + text_p.follows.flags(FOLLOWS_ALL); + // word wrap was added accroding to the EXT-6841 + text_p.wrap(true); + addChild(LLUICtrlFactory::create<LLTextBox>(text_p)); } -LLScrollListCtrl::~LLScrollListCtrl() +S32 LLScrollListCtrl::getSearchColumn() { - std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); - - if( gEditMenuHandler == this ) + // search for proper search column + if (mSearchColumn < 0) { - gEditMenuHandler = NULL; + LLScrollListItem* itemp = getFirstData(); + if (itemp) + { + for(S32 column = 0; column < getNumColumns(); column++) + { + LLScrollListCell* cell = itemp->getColumn(column); + if (cell && cell->isText()) + { + mSearchColumn = column; + break; + } + } + } } + return llclamp(mSearchColumn, 0, getNumColumns()); +} +/*virtual*/ +bool LLScrollListCtrl::preProcessChildNode(LLXMLNodePtr child) +{ + if (child->hasName("column") || child->hasName("row")) + { + return true; // skip + } + else + { + return false; + } +} + +LLScrollListCtrl::~LLScrollListCtrl() +{ + delete mSortCallback; + + std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); } @@ -709,6 +393,10 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const S32 LLScrollListCtrl::getFirstSelectedIndex() const { S32 CurSelectedIndex = 0; + + // make sure sort is up to date before returning an index + updateSort(); + item_list::const_iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { @@ -756,7 +444,7 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const // returns first matching item LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const { - LLString string_val = sd.asString(); + std::string string_val = sd.asString(); item_list::const_iterator iter; for(iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -781,6 +469,7 @@ void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) void LLScrollListCtrl::updateLayout() { + static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); // reserve room for column headers, if needed S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); mItemListRect.setOriginAndSize( @@ -789,17 +478,21 @@ void LLScrollListCtrl::updateLayout() getRect().getWidth() - 2 * mBorderThickness, getRect().getHeight() - (2 * mBorderThickness ) - heading_size ); + getChildView("comment_text")->setShape(mItemListRect); + // how many lines of content in a single "page" - mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0; - BOOL scrollbar_visible = getItemCount() > mPageLines; + S32 page_lines = getLinesPerPage(); + + BOOL scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight(); if (scrollbar_visible) { // provide space on the right for scrollbar - mItemListRect.mRight = getRect().getWidth() - mBorderThickness - SCROLLBAR_SIZE; + mItemListRect.mRight = getRect().getWidth() - mBorderThickness - scrollbar_size; } - mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); - mScrollbar->setPageSize( mPageLines ); + mScrollbar->setOrigin(getRect().getWidth() - mBorderThickness - scrollbar_size, mItemListRect.mBottom); + mScrollbar->reshape(scrollbar_size, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); + mScrollbar->setPageSize(page_lines); mScrollbar->setDocSize( getItemCount() ); mScrollbar->setVisible(scrollbar_visible); @@ -811,6 +504,9 @@ void LLScrollListCtrl::updateLayout() void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height) { S32 height = llmin( getRequiredRect().getHeight(), max_height ); + if(mPageLines) + height = llmin( mPageLines * mLineHeight + 2*mBorderThickness + (mDisplayColumnHeaders ? mHeadingHeight : 0), height ); + S32 width = getRect().getWidth(); reshape( width, height ); @@ -838,7 +534,7 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r { case ADD_TOP: mItemList.push_front(item); - setSorted(FALSE); + setNeedsSort(); break; case ADD_SORTED: @@ -851,33 +547,33 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r std::stable_sort( mItemList.begin(), mItemList.end(), - SortScrollListItem(single_sort_column)); + SortScrollListItem(single_sort_column,mSortCallback)); // ADD_SORTED just sorts by first column... // this might not match user sort criteria, so flag list as being in unsorted state - setSorted(FALSE); + setNeedsSort(); break; } case ADD_BOTTOM: mItemList.push_back(item); - setSorted(FALSE); + setNeedsSort(); break; default: llassert(0); mItemList.push_back(item); - setSorted(FALSE); + setNeedsSort(); break; } // create new column on demand if (mColumns.empty() && requires_column) { - LLSD new_column; - new_column["name"] = "default_column"; - new_column["label"] = ""; - new_column["dynamicwidth"] = TRUE; - addColumn(new_column); + LLScrollListColumn::Params col_params; + col_params.name = "default_column"; + col_params.header.label = ""; + col_params.width.dynamic_width = true; + addColumn(col_params); } updateLineHeightInsert(item); @@ -893,8 +589,8 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r // *TODO: Use bookkeeping to make this an incramental cost with item additions void LLScrollListCtrl::calcColumnWidths() { - const S32 HEADING_TEXT_PADDING = 30; - const S32 COLUMN_TEXT_PADDING = 20; + const S32 HEADING_TEXT_PADDING = 25; + const S32 COLUMN_TEXT_PADDING = 10; mMaxContentWidth = 0; @@ -907,30 +603,27 @@ void LLScrollListCtrl::calcColumnWidths() if (!column) continue; // update column width - S32 new_width = column->mWidth; + S32 new_width = column->getWidth(); if (column->mRelWidth >= 0) { new_width = (S32)llround(column->mRelWidth*mItemListRect.getWidth()); } else if (column->mDynamicWidth) { - new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; + new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns; } - if (new_width != column->mWidth) - { - column->mWidth = new_width; - } + column->setWidth(new_width); // update max content width for this column, by looking at all items - column->mMaxContentWidth = column->mHeader ? LLFontGL::sSansSerifSmall->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0; + column->mMaxContentWidth = column->mHeader ? LLFontGL::getFontSansSerifSmall()->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0; item_list::iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); if (!cellp) continue; - column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + column->mMaxContentWidth = llmax(LLFontGL::getFontSansSerifSmall()->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); } max_item_width += column->mMaxContentWidth; @@ -974,28 +667,13 @@ void LLScrollListCtrl::updateColumns() { calcColumnWidths(); - // propagate column widths to individual cells - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) - { - LLScrollListItem *itemp = *iter; - S32 num_cols = itemp->getNumColumns(); - S32 i = 0; - for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) - { - if (i >= (S32)mColumnsIndexed.size()) break; - - cell->setWidth(mColumnsIndexed[i]->mWidth); - } - } - // update column headers std::vector<LLScrollListColumn*>::iterator column_ordered_it; S32 left = mItemListRect.mLeft; - LLColumnHeader* last_header = NULL; + LLScrollColumnHeader* last_header = NULL; for (column_ordered_it = mColumnsIndexed.begin(); column_ordered_it != mColumnsIndexed.end(); ++column_ordered_it) { - if ((*column_ordered_it)->mWidth < 0) + if ((*column_ordered_it)->getWidth() < 0) { // skip hidden columns continue; @@ -1004,9 +682,11 @@ void LLScrollListCtrl::updateColumns() if (column->mHeader) { + column->mHeader->updateResizeBars(); + last_header = column->mHeader; S32 top = mItemListRect.mTop; - S32 right = left + column->mWidth; + S32 right = left + column->getWidth(); if (column->mIndex != (S32)mColumnsIndexed.size()-1) { @@ -1024,21 +704,30 @@ void LLScrollListCtrl::updateColumns() } } - //FIXME: stretch the entire last column if it is resizable (gestures windows shows truncated text in last column) // expand last column header we encountered to full list width if (last_header) { S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft); last_header->reshape(new_width, last_header->getRect().getHeight()); last_header->setVisible(mDisplayColumnHeaders && new_width > 0); + last_header->getColumn()->setWidth(new_width); } -} -void LLScrollListCtrl::setDisplayHeading(BOOL display) -{ - mDisplayColumnHeaders = display; + // propagate column widths to individual cells + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + S32 num_cols = itemp->getNumColumns(); + S32 i = 0; + for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) + { + if (i >= (S32)mColumnsIndexed.size()) break; + + cell->setWidth(mColumnsIndexed[i]->getWidth()); + } + } - updateLayout(); } void LLScrollListCtrl::setHeadingHeight(S32 heading_height) @@ -1048,6 +737,12 @@ void LLScrollListCtrl::setHeadingHeight(S32 heading_height) updateLayout(); } +void LLScrollListCtrl::setPageLines(S32 new_page_lines) +{ + mPageLines = new_page_lines; + + updateLayout(); +} BOOL LLScrollListCtrl::selectFirstItem() { @@ -1097,6 +792,9 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) return FALSE; } + // make sure sort is up to date + updateSort(); + S32 listlen = (S32)mItemList.size(); first_index = llclamp(first_index, 0, listlen-1); @@ -1107,9 +805,15 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) BOOL success = FALSE; S32 index = 0; - for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ) { LLScrollListItem *itemp = *iter; + if(!itemp) + { + iter = mItemList.erase(iter); + continue ; + } + if( index >= first_index && index <= last_index ) { if( itemp->getEnabled() ) @@ -1123,6 +827,7 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) deselectItem(itemp); } index++; + iter++ ; } if (mCommitOnSelectionChange) @@ -1143,6 +848,7 @@ void LLScrollListCtrl::swapWithNext(S32 index) // At end of list, doesn't do anything return; } + updateSort(); LLScrollListItem *cur_itemp = mItemList[index]; mItemList[index] = mItemList[index + 1]; mItemList[index + 1] = cur_itemp; @@ -1156,6 +862,7 @@ void LLScrollListCtrl::swapWithPrevious(S32 index) // At beginning of list, don't do anything } + updateSort(); LLScrollListItem *cur_itemp = mItemList[index]; mItemList[index] = mItemList[index - 1]; mItemList[index - 1] = cur_itemp; @@ -1169,6 +876,8 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index) return; } + updateSort(); + LLScrollListItem *itemp; itemp = mItemList[target_index]; if (itemp == mLastSelected) @@ -1225,7 +934,15 @@ void LLScrollListCtrl::deleteSelectedItems() dirtyColumns(); } -void LLScrollListCtrl::highlightNthItem(S32 target_index) +void LLScrollListCtrl::clearHighlightedItems() +{ + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter) + { + (*iter)->setHighlighted(false); + } +} + +void LLScrollListCtrl::mouseOverHighlightNthItem(S32 target_index) { if (mHighlightedItem != target_index) { @@ -1233,14 +950,14 @@ void LLScrollListCtrl::highlightNthItem(S32 target_index) } } -S32 LLScrollListCtrl::selectMultiple( LLDynamicArray<LLUUID> ids ) +S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids ) { item_list::iterator iter; S32 count = 0; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { LLScrollListItem* item = *iter; - LLDynamicArray<LLUUID>::iterator iditr; + uuid_vec_t::iterator iditr; for(iditr = ids.begin(); iditr != ids.end(); ++iditr) { if (item->getEnabled() && (item->getUUID() == (*iditr))) @@ -1262,6 +979,8 @@ S32 LLScrollListCtrl::selectMultiple( LLDynamicArray<LLUUID> ids ) S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const { + updateSort(); + S32 index = 0; item_list::const_iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -1278,6 +997,8 @@ S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const { + updateSort(); + S32 index = 0; item_list::const_iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -1303,6 +1024,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) } else { + updateSort(); + item_list::iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { @@ -1345,6 +1068,8 @@ void LLScrollListCtrl::selectNextItem( BOOL extend_selection) } else { + updateSort(); + item_list::reverse_iterator iter; for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++) { @@ -1396,75 +1121,78 @@ void LLScrollListCtrl::deselectAllItems(BOOL no_commit_on_change) /////////////////////////////////////////////////////////////////////////////////////////////////// // Use this to add comment text such as "Searching", which ignores column settings of list -LLScrollListItem* LLScrollListCtrl::addCommentText(const LLString& comment_text, EAddPosition pos) +void LLScrollListCtrl::setCommentText(const std::string& comment_text) { - LLScrollListItem* item = NULL; - if (getItemCount() < mMaxItemCount) - { - // always draw comment text with "enabled" color - item = new LLScrollListItemComment( comment_text, mFgUnselectedColor ); - addItem( item, pos, FALSE ); - } - return item; + getChild<LLTextBox>("comment_text")->setValue(comment_text); } LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos) { - LLScrollListItem* item = new LLScrollListItemSeparator(); - addItem(item, pos, FALSE); - return item; + LLScrollListItem::Params separator_params; + separator_params.enabled(false); + LLScrollListCell::Params column_params; + column_params.type = "icon"; + column_params.value = "menu_separator"; + column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f); + column_params.font_halign = LLFontGL::HCENTER; + separator_params.columns.add(column_params); + return addRow( separator_params, pos ); } // Selects first enabled item of the given name. // Returns false if item not found. -BOOL LLScrollListCtrl::selectItemByLabel(const LLString& label, BOOL case_sensitive) +// Calls getItemByLabel in order to combine functionality +BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sensitive) { - // ensure that no stale items are selected, even if we don't find a match - deselectAllItems(TRUE); - //RN: assume no empty items - if (label.empty()) + deselectAllItems(TRUE); // ensure that no stale items are selected, even if we don't find a match + LLScrollListItem* item = getItemByLabel(label, case_sensitive); + + bool found = NULL != item; + if(found) { - return FALSE; + selectItem(item); } - LLString target_text = label; - if (!case_sensitive) + if (mCommitOnSelectionChange) { - LLString::toLower(target_text); + commitIfChanged(); } - BOOL found = FALSE; + return found; +} + +LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, BOOL case_sensitive, S32 column) +{ + if (label.empty()) //RN: assume no empty items + { + return NULL; + } + + std::string target_text = label; + if (!case_sensitive) + { + LLStringUtil::toLower(target_text); + } item_list::iterator iter; - S32 index = 0; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { LLScrollListItem* item = *iter; - // Only select enabled items with matching names - LLString item_text = item->getColumn(0)->getValue().asString(); + std::string item_text = item->getColumn(column)->getValue().asString(); // Only select enabled items with matching names if (!case_sensitive) { - LLString::toLower(item_text); + LLStringUtil::toLower(item_text); } - BOOL select = !found && item->getEnabled() && item_text == target_text; - if (select) + if(item_text == target_text) { - selectItem(item); + return item; } - found = found || select; - index++; - } - - if (mCommitOnSelectionChange) - { - commitIfChanged(); } - - return found; + return NULL; } -BOOL LLScrollListCtrl::selectItemByPrefix(const LLString& target, BOOL case_sensitive) +BOOL LLScrollListCtrl::selectItemByPrefix(const std::string& target, BOOL case_sensitive) { return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive); } @@ -1486,7 +1214,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen { LLScrollListItem* item = *iter; // Only select enabled items with matching names - LLScrollListCell* cellp = item->getColumn(mSearchColumn); + LLScrollListCell* cellp = item->getColumn(getSearchColumn()); BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE; if (select) { @@ -1501,7 +1229,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen if (!case_sensitive) { // do comparisons in lower case - LLWString::toLower(target_trimmed); + LLWStringUtil::toLower(target_trimmed); } for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -1509,7 +1237,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen LLScrollListItem* item = *iter; // Only select enabled items with matching names - LLScrollListCell* cellp = item->getColumn(mSearchColumn); + LLScrollListCell* cellp = item->getColumn(getSearchColumn()); if (!cellp) { continue; @@ -1517,11 +1245,11 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); if (!case_sensitive) { - LLWString::toLower(item_label); + LLWStringUtil::toLower(item_label); } // remove extraneous whitespace from searchable label LLWString trimmed_label = item_label; - LLWString::trim(trimmed_label); + LLWStringUtil::trim(trimmed_label); BOOL select = item->getEnabled() && trimmed_label.compare(0, target_trimmed.size(), target_trimmed) == 0; @@ -1545,7 +1273,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen return found; } -const LLString LLScrollListCtrl::getSelectedItemLabel(S32 column) const +const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const { LLScrollListItem* item; @@ -1555,23 +1283,25 @@ const LLString LLScrollListCtrl::getSelectedItemLabel(S32 column) const return item->getColumn(column)->getValue().asString(); } - return LLString::null; + return LLStringUtil::null; } /////////////////////////////////////////////////////////////////////////////////////////////////// // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which // has an associated, unique UUID, and only one of which can be selected at a time. -LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const LLString& item_text, const LLUUID& id, EAddPosition pos, BOOL enabled, S32 column_width) +LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, BOOL enabled) { - LLScrollListItem* item = NULL; if (getItemCount() < mMaxItemCount) { - item = new LLScrollListItem( enabled, NULL, id ); - item->addColumn(item_text, LLResMgr::getInstance()->getRes(LLFONT_SANSSERIF_SMALL), column_width); - addItem( item, pos ); + LLScrollListItem::Params item_p; + item_p.enabled(enabled); + item_p.value(id); + item_p.columns.add().value(item_text).type("text"); + + return addRow( item_p, pos ); } - return item; + return NULL; } // Select the line or lines that match this UUID @@ -1659,24 +1389,24 @@ void LLScrollListCtrl::drawItems() S32 y = mItemListRect.mTop - mLineHeight; // allow for partial line at bottom - S32 num_page_lines = mPageLines + 1; + S32 num_page_lines = getLinesPerPage(); LLRect item_rect; LLGLSUIDefault gls_ui; + F32 alpha = getDrawContext().mAlpha; + { LLLocalClipRect clip(mItemListRect); S32 cur_y = y; - mDrewSelected = FALSE; - S32 line = 0; S32 max_columns = 0; LLColor4 highlight_color = LLColor4::white; - F32 type_ahead_timeout = LLUI::sConfigGroup->getF32("TypeAheadTimeout"); + static LLUICachedControl<F32> type_ahead_timeout ("TypeAheadTimeout", 0); highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f); item_list::iterator iter; @@ -1689,14 +1419,10 @@ void LLScrollListCtrl::drawItems() cur_y, mItemListRect.getWidth(), mLineHeight ); + item->setRect(item_rect); //llinfos << item_rect.getWidth() << llendl; - if (item->getSelected()) - { - mDrewSelected = TRUE; - } - max_columns = llmax(max_columns, item->getNumColumns()); LLColor4 fg_color; @@ -1704,30 +1430,49 @@ void LLScrollListCtrl::drawItems() if( mScrollLines <= line && line < mScrollLines + num_page_lines ) { - fg_color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); + fg_color = (item->getEnabled() ? mFgUnselectedColor.get() : mFgDisabledColor.get()); if( item->getSelected() && mCanSelect) { - bg_color = mBgSelectedColor; - fg_color = (item->getEnabled() ? mFgSelectedColor : mFgDisabledColor); + if(item->getHighlighted()) // if it's highlighted, average the colors + { + bg_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f); + } + else // otherwise just select-highlight it + { + bg_color = mBgSelectedColor.get(); + } + + fg_color = (item->getEnabled() ? mFgSelectedColor.get() : mFgDisabledColor.get()); } else if (mHighlightedItem == line && mCanSelect) { - bg_color = mHighlightedColor; + if(item->getHighlighted()) // if it's highlighted, average the colors + { + bg_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f); + } + else // otherwise just hover-highlight it + { + bg_color = mHoveredColor.get(); + } + } + else if (item->getHighlighted()) + { + bg_color = mHighlightedColor.get(); } else { if (mDrawStripes && (line % 2 == 0) && (max_columns > 1)) { - bg_color = mBgStripeColor; + bg_color = mBgStripeColor.get(); } } if (!item->getEnabled()) { - bg_color = mBgReadOnlyColor; + bg_color = mBgReadOnlyColor.get(); } - item->draw(item_rect, fg_color, bg_color, highlight_color, mColumnPadding); + item->draw(item_rect, fg_color % alpha, bg_color% alpha, highlight_color % alpha, mColumnPadding); cur_y -= mLineHeight; } @@ -1739,11 +1484,10 @@ void LLScrollListCtrl::drawItems() void LLScrollListCtrl::draw() { + LLLocalClipRect clip(getLocalRect()); + // if user specifies sort, make sure it is maintained - if (needsSorting() && !isSorted()) - { - sortItems(); - } + updateSort(); if (mNeedsScroll) { @@ -1754,9 +1498,8 @@ void LLScrollListCtrl::draw() // Draw background if (mBackgroundVisible) { - LLGLSNoTexture no_texture; - gGL.color4fv( getEnabled() ? mBgWriteableColor.mV : mBgReadOnlyColor.mV ); - gl_rect_2d(background); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() : mBgReadOnlyColor.get() ); } if (mColumnsDirty) @@ -1765,11 +1508,13 @@ void LLScrollListCtrl::draw() mColumnsDirty = FALSE; } + getChildView("comment_text")->setVisible(mItemList.empty()); + drawItems(); if (mBorder) { - mBorder->setKeyboardFocusHighlight(gFocusMgr.getKeyboardFocus() == this); + mBorder->setKeyboardFocusHighlight(hasFocus()); } LLUICtrl::draw(); @@ -1787,10 +1532,28 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) BOOL handled = FALSE; // Pretend the mouse is over the scrollbar handled = mScrollbar->handleScrollWheel( 0, 0, clicks ); + + if (mMouseWheelOpaque) + { + return TRUE; + } + return handled; } -BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) +// *NOTE: Requires a valid row_index and column_index +LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index) +{ + LLRect cell_rect; + S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; + S32 rect_bottom = getRowOffsetFromIndex(row_index); + LLScrollListColumn* columnp = getColumn(column_index); + cell_rect.setOriginAndSize(rect_left, rect_bottom, + /*rect_left + */columnp->getWidth(), mLineHeight); + return cell_rect; +} + +BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask) { S32 column_index = getColumnIndexFromOffset(x); LLScrollListColumn* columnp = getColumn(column_index); @@ -1804,34 +1567,32 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky { LLScrollListCell* hit_cell = hit_item->getColumn(column_index); if (!hit_cell) return FALSE; - //S32 cell_required_width = hit_cell->getContentWidth(); if (hit_cell - && hit_cell->isText()) + && hit_cell->isText() + && hit_cell->needsToolTip()) { - - S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; - S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item)); - LLRect cell_rect; - cell_rect.setOriginAndSize(rect_left, rect_bottom, rect_left + columnp->mWidth, mLineHeight); + S32 row_index = getItemIndex(hit_item); + LLRect cell_rect = getCellRect(row_index, column_index); // Convert rect local to screen coordinates - localPointToScreen( - cell_rect.mLeft, cell_rect.mBottom, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - localPointToScreen( - cell_rect.mRight, cell_rect.mTop, - &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); + LLRect sticky_rect; + localRectToScreen(cell_rect, &sticky_rect); - msg = hit_cell->getValue().asString(); + // display tooltip exactly over original cell, in same font + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(hit_cell->getToolTip()) + .font(LLFontGL::getFontSansSerifSmall()) + .pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 6)) + .delay_time(0.2f) + .sticky_rect(sticky_rect)); } handled = TRUE; } // otherwise, look for a tooltip associated with this column - LLColumnHeader* headerp = columnp->mHeader; + LLScrollColumnHeader* headerp = columnp->mHeader; if (headerp && !handled) { - headerp->handleToolTip(x, y, msg, sticky_rect_screen); - handled = !msg.empty(); + handled = headerp->handleToolTip(x, y, mask); } return handled; @@ -1870,7 +1631,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if(mOnMaximumSelectCallback) { - mOnMaximumSelectCallback(mCallbackUserData); + mOnMaximumSelectCallback(); } break; } @@ -1908,7 +1669,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if(mOnMaximumSelectCallback) { - mOnMaximumSelectCallback(mCallbackUserData); + mOnMaximumSelectCallback(); } } } @@ -1962,7 +1723,7 @@ BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) } BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) -{ +{ if (hasMouseCapture()) { // release mouse capture immediately so @@ -1986,6 +1747,72 @@ BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) return LLUICtrl::handleMouseUp(x, y, mask); } +// virtual +BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + LLScrollListItem *item = hitItem(x, y); + if (item) + { + // check to see if we have a UUID for this row + std::string id = item->getValue().asString(); + LLUUID uuid(id); + if (! uuid.isNull() && mContextMenuType != MENU_NONE) + { + // set up the callbacks for all of the avatar/group menu items + // (N.B. callbacks don't take const refs as id is local scope) + bool is_group = (mContextMenuType == MENU_GROUP); + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group)); + registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group)); + registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group)); + + // create the context menu from the XUI file and display it + std::string menu_name = is_group ? "menu_url_group.xml" : "menu_url_agent.xml"; + delete mPopupMenu; + mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>( + menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + if (mPopupMenu) + { + mPopupMenu->show(x, y); + LLMenuGL::showPopup(this, mPopupMenu, x, y); + return TRUE; + } + } + } + return FALSE; +} + +void LLScrollListCtrl::showNameDetails(std::string id, bool is_group) +{ + // show the resident's profile or the group profile + std::string sltype = is_group ? "group" : "agent"; + std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; + LLUrlAction::clickAction(slurl); +} + +void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group) +{ + // copy the name of the avatar or group to the clipboard + std::string name; + if (is_group) + { + gCacheName->getGroupName(LLUUID(id), name); + } + else + { + gCacheName->getFullName(LLUUID(id), name); + } + LLUrlAction::copyURLToClipboard(name); +} + +void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group) +{ + // copy a SLURL for the avatar or group to the clipboard + std::string sltype = is_group ? "group" : "agent"; + std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; + LLUrlAction::copyURLToClipboard(slurl); +} + BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) { //BOOL handled = FALSE; @@ -1997,9 +1824,10 @@ BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) // so the scroll bars will work. if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) { - if( mCanSelect && mOnDoubleClickCallback ) + // Run the callback only if an item is being double-clicked. + if( mCanSelect && hitItem(x, y) && mOnDoubleClickCallback ) { - mOnDoubleClickCallback( mCallbackUserData ); + mOnDoubleClickCallback(); } } } @@ -2066,6 +1894,8 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) // Excludes disabled items. LLScrollListItem* hit_item = NULL; + updateSort(); + LLRect item_rect; item_rect.setLeftTopAndSize( mItemListRect.mLeft, @@ -2074,7 +1904,7 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) mLineHeight ); // allow for partial line at bottom - S32 num_page_lines = mPageLines + 1; + S32 num_page_lines = getLinesPerPage(); S32 line = 0; item_list::iterator iter; @@ -2109,7 +1939,7 @@ S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x) ordered_columns_t::const_iterator end = mColumnsIndexed.end(); for ( ; iter != end; ++iter) { - width = (*iter)->mWidth + mColumnPadding; + width = (*iter)->getWidth() + mColumnPadding; right += width; if (left <= x && x < right ) { @@ -2136,7 +1966,7 @@ S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index) { return column_offset; } - column_offset += (*iter)->mWidth + mColumnPadding; + column_offset += (*iter)->getWidth() + mColumnPadding; } // when running off the end, return the rightmost pixel @@ -2145,8 +1975,7 @@ S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index) S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index) { - S32 row_bottom = ((mItemListRect.mTop - (index - mScrollLines)) * mLineHeight) - - mLineHeight; + S32 row_bottom = (mItemListRect.mTop - ((index - mScrollLines + 1) * mLineHeight) ); return row_bottom; } @@ -2169,11 +1998,11 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) LLScrollListItem* item = hitItem(x, y); if (item) { - highlightNthItem(getItemIndex(item)); + mouseOverHighlightNthItem(getItemIndex(item)); } else { - highlightNthItem(-1); + mouseOverHighlightNthItem(-1); } } @@ -2182,6 +2011,11 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) return handled; } +void LLScrollListCtrl::onMouseLeave(S32 x, S32 y, MASK mask) +{ + // clear mouse highlight + mouseOverHighlightNthItem(-1); +} BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { @@ -2288,7 +2122,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) { if (getFirstSelected()) { - LLScrollListCell* cellp = getFirstSelected()->getColumn(mSearchColumn); + LLScrollListCell* cellp = getFirstSelected()->getColumn(getSearchColumn()); if (cellp) { cellp->highlightText(0, 0); @@ -2326,7 +2160,8 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) } // perform incremental search based on keyboard input - if (mSearchTimer.getElapsedTimeF32() > LLUI::sConfigGroup->getF32("TypeAheadTimeout")) + static LLUICachedControl<F32> type_ahead_timeout ("TypeAheadTimeout", 0); + if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout) { mSearchString.clear(); } @@ -2375,7 +2210,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) { LLScrollListItem* item = *iter; - LLScrollListCell* cellp = item->getColumn(mSearchColumn); + LLScrollListCell* cellp = item->getColumn(getSearchColumn()); if (cellp) { // Only select enabled items with matching first characters @@ -2442,7 +2277,7 @@ void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_it { if (mLastSelected) { - LLScrollListCell* cellp = mLastSelected->getColumn(mSearchColumn); + LLScrollListCell* cellp = mLastSelected->getColumn(getSearchColumn()); if (cellp) { cellp->highlightText(0, 0); @@ -2470,7 +2305,7 @@ void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) } itemp->setSelected(FALSE); - LLScrollListCell* cellp = itemp->getColumn(mSearchColumn); + LLScrollListCell* cellp = itemp->getColumn(getSearchColumn()); if (cellp) { cellp->highlightText(0, 0); @@ -2497,9 +2332,16 @@ struct SameSortColumn bool operator()(std::pair<S32, BOOL> sort_column) { return sort_column.first == mColumn; } }; -BOOL LLScrollListCtrl::setSort(S32 column, BOOL ascending) +BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending) { - sort_column_t new_sort_column(column, ascending); + LLScrollListColumn* sort_column = getColumn(column_idx); + if (!sort_column) return FALSE; + + sort_column->mSortDirection = ascending ? LLScrollListColumn::ASCENDING : LLScrollListColumn::DESCENDING; + + sort_column_t new_sort_column(column_idx, ascending); + + setNeedsSort(); if (mSortColumns.empty()) { @@ -2513,7 +2355,7 @@ BOOL LLScrollListCtrl::setSort(S32 column, BOOL ascending) // remove any existing sort criterion referencing this column // and add the new one - mSortColumns.erase(remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column)), mSortColumns.end()); + mSortColumns.erase(remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column_idx)), mSortColumns.end()); mSortColumns.push_back(new_sort_column); // did the sort criteria change? @@ -2521,48 +2363,68 @@ BOOL LLScrollListCtrl::setSort(S32 column, BOOL ascending) } } -// Called by scrollbar -//static -void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) +S32 LLScrollListCtrl::getLinesPerPage() { - LLScrollListCtrl* self = (LLScrollListCtrl*) userdata; - self->mScrollLines = new_pos; + //if mPageLines is NOT provided display all item + if(mPageLines) + { + return mPageLines; + } + else + { + return mLineHeight ? mItemListRect.getHeight() / mLineHeight : getItemCount(); + } } -void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) +// Called by scrollbar +void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar ) { - if (name.empty()) - { - sortItems(); - return; - } + mScrollLines = new_pos; +} + - std::map<LLString, LLScrollListColumn>::iterator itor = mColumns.find(name); +void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending) +{ + std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.find(name); if (itor != mColumns.end()) { - sortByColumn((*itor).second.mIndex, ascending); + sortByColumnIndex((*itor).second.mIndex, ascending); } } // First column is column 0 -void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) +void LLScrollListCtrl::sortByColumnIndex(U32 column, BOOL ascending) { - if (setSort(column, ascending)) + setSort(column, ascending); + updateSort(); +} + +void LLScrollListCtrl::updateSort() const +{ + if (hasSortOrder() && !isSorted()) { - sortItems(); + // do stable sort to preserve any previous sorts + std::stable_sort( + mItemList.begin(), + mItemList.end(), + SortScrollListItem(mSortColumns,mSortCallback)); + + mSorted = true; } } -void LLScrollListCtrl::sortItems() +// for one-shot sorts, does not save sort column/order +void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending) { + std::vector<std::pair<S32, BOOL> > sort_column; + sort_column.push_back(std::make_pair(column, ascending)); + // do stable sort to preserve any previous sorts std::stable_sort( mItemList.begin(), mItemList.end(), - SortScrollListItem(mSortColumns)); - - setSorted(TRUE); + SortScrollListItem(sort_column,mSortCallback)); } void LLScrollListCtrl::dirtyColumns() @@ -2573,7 +2435,7 @@ void LLScrollListCtrl::dirtyColumns() // just in case someone indexes into it immediately mColumnsIndexed.resize(mColumns.size()); - std::map<LLString, LLScrollListColumn>::iterator column_itor; + std::map<std::string, LLScrollListColumn>::iterator column_itor; for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) { LLScrollListColumn *column = &column_itor->second; @@ -2592,7 +2454,7 @@ void LLScrollListCtrl::setScrollPos( S32 pos ) { mScrollbar->setDocPos( pos ); - onScrollChange(mScrollbar->getDocPos(), mScrollbar, this); + onScrollChange(mScrollbar->getDocPos(), mScrollbar); } @@ -2605,6 +2467,8 @@ void LLScrollListCtrl::scrollToShowSelected() return; } + updateSort(); + S32 index = getFirstSelectedIndex(); if (index < 0) { @@ -2619,7 +2483,8 @@ void LLScrollListCtrl::scrollToShowSelected() } S32 lowest = mScrollLines; - S32 highest = mScrollLines + mPageLines; + S32 page_lines = getLinesPerPage(); + S32 highest = mScrollLines + page_lines; if (index < lowest) { @@ -2628,298 +2493,13 @@ void LLScrollListCtrl::scrollToShowSelected() } else if (highest <= index) { - setScrollPos(index - mPageLines + 1); + setScrollPos(index - page_lines + 1); } } -// virtual -LLXMLNodePtr LLScrollListCtrl::getXML(bool save_children) const +void LLScrollListCtrl::updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width) { - LLXMLNodePtr node = LLUICtrl::getXML(); - - // Attributes - - node->createChild("multi_select", TRUE)->setBoolValue(mAllowMultipleSelection); - - node->createChild("draw_border", TRUE)->setBoolValue((mBorder != NULL)); - - node->createChild("draw_heading", TRUE)->setBoolValue(mDisplayColumnHeaders); - - node->createChild("background_visible", TRUE)->setBoolValue(mBackgroundVisible); - - node->createChild("draw_stripes", TRUE)->setBoolValue(mDrawStripes); - - node->createChild("column_padding", TRUE)->setIntValue(mColumnPadding); - - addColorXML(node, mBgWriteableColor, "bg_writeable_color", "ScrollBgWriteableColor"); - addColorXML(node, mBgReadOnlyColor, "bg_read_only_color", "ScrollBgReadOnlyColor"); - addColorXML(node, mBgSelectedColor, "bg_selected_color", "ScrollSelectedBGColor"); - addColorXML(node, mBgStripeColor, "bg_stripe_color", "ScrollBGStripeColor"); - addColorXML(node, mFgSelectedColor, "fg_selected_color", "ScrollSelectedFGColor"); - addColorXML(node, mFgUnselectedColor, "fg_unselected_color", "ScrollUnselectedColor"); - addColorXML(node, mFgDisabledColor, "fg_disable_color", "ScrollDisabledColor"); - addColorXML(node, mHighlightedColor, "highlighted_color", "ScrollHighlightedColor"); - - // Contents - - std::map<LLString, LLScrollListColumn>::const_iterator itor; - std::vector<const LLScrollListColumn*> sorted_list; - sorted_list.resize(mColumns.size()); - for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) - { - sorted_list[itor->second.mIndex] = &itor->second; - } - - std::vector<const LLScrollListColumn*>::iterator itor2; - for (itor2 = sorted_list.begin(); itor2 != sorted_list.end(); ++itor2) - { - LLXMLNodePtr child_node = node->createChild("column", FALSE); - const LLScrollListColumn *column = *itor2; - - child_node->createChild("name", TRUE)->setStringValue(column->mName); - child_node->createChild("label", TRUE)->setStringValue(column->mLabel); - child_node->createChild("width", TRUE)->setIntValue(column->mWidth); - } - - return node; -} - -void LLScrollListCtrl::setScrollListParameters(LLXMLNodePtr node) -{ - // James: This is not a good way to do colors. We need a central "UI style" - // manager that sets the colors for ALL scroll lists, buttons, etc. - - LLColor4 color; - if(node->hasAttribute("fg_unselected_color")) - { - LLUICtrlFactory::getAttributeColor(node,"fg_unselected_color", color); - setFgUnselectedColor(color); - } - if(node->hasAttribute("fg_selected_color")) - { - LLUICtrlFactory::getAttributeColor(node,"fg_selected_color", color); - setFgSelectedColor(color); - } - if(node->hasAttribute("bg_selected_color")) - { - LLUICtrlFactory::getAttributeColor(node,"bg_selected_color", color); - setBgSelectedColor(color); - } - if(node->hasAttribute("fg_disable_color")) - { - LLUICtrlFactory::getAttributeColor(node,"fg_disable_color", color); - setFgDisableColor(color); - } - if(node->hasAttribute("bg_writeable_color")) - { - LLUICtrlFactory::getAttributeColor(node,"bg_writeable_color", color); - setBgWriteableColor(color); - } - if(node->hasAttribute("bg_read_only_color")) - { - LLUICtrlFactory::getAttributeColor(node,"bg_read_only_color", color); - setReadOnlyBgColor(color); - } - if (LLUICtrlFactory::getAttributeColor(node,"bg_stripe_color", color)) - { - setBgStripeColor(color); - } - if (LLUICtrlFactory::getAttributeColor(node,"highlighted_color", color)) - { - setHighlightedColor(color); - } - - if(node->hasAttribute("background_visible")) - { - BOOL background_visible; - node->getAttributeBOOL("background_visible", background_visible); - setBackgroundVisible(background_visible); - } - - if(node->hasAttribute("draw_stripes")) - { - BOOL draw_stripes; - node->getAttributeBOOL("draw_stripes", draw_stripes); - setDrawStripes(draw_stripes); - } - - if(node->hasAttribute("column_padding")) - { - S32 column_padding; - node->getAttributeS32("column_padding", column_padding); - setColumnPadding(column_padding); - } -} - -// static -LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) -{ - LLString name("scroll_list"); - node->getAttributeString("name", name); - - LLRect rect; - createRect(node, rect, parent, LLRect()); - - BOOL multi_select = FALSE; - node->getAttributeBOOL("multi_select", multi_select); - - BOOL draw_border = TRUE; - node->getAttributeBOOL("draw_border", draw_border); - - BOOL draw_heading = FALSE; - node->getAttributeBOOL("draw_heading", draw_heading); - - S32 search_column = 0; - node->getAttributeS32("search_column", search_column); - - S32 sort_column = -1; - node->getAttributeS32("sort_column", sort_column); - - BOOL sort_ascending = TRUE; - node->getAttributeBOOL("sort_ascending", sort_ascending); - - LLUICtrlCallback callback = NULL; - - LLScrollListCtrl* scroll_list = new LLScrollListCtrl( - name, - rect, - callback, - NULL, - multi_select, - draw_border); - - scroll_list->setDisplayHeading(draw_heading); - if (node->hasAttribute("heading_height")) - { - S32 heading_height; - node->getAttributeS32("heading_height", heading_height); - scroll_list->setHeadingHeight(heading_height); - } - - scroll_list->setScrollListParameters(node); - - scroll_list->initFromXML(node, parent); - - scroll_list->setSearchColumn(search_column); - - if (sort_column >= 0) - { - scroll_list->sortByColumn(sort_column, sort_ascending); - } - - LLSD columns; - S32 index = 0; - LLXMLNodePtr child; - S32 total_static = 0; - for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) - { - if (child->hasName("column")) - { - LLString labelname(""); - child->getAttributeString("label", labelname); - - LLString columnname(labelname); - child->getAttributeString("name", columnname); - - LLString sortname(columnname); - child->getAttributeString("sort", sortname); - - BOOL sort_ascending = TRUE; - child->getAttributeBOOL("sort_ascending", sort_ascending); - - LLString imagename; - child->getAttributeString("image", imagename); - - BOOL columndynamicwidth = FALSE; - child->getAttributeBOOL("dynamicwidth", columndynamicwidth); - - S32 columnwidth = -1; - child->getAttributeS32("width", columnwidth); - - LLString tooltip; - child->getAttributeString("tool_tip", tooltip); - - if(!columndynamicwidth) total_static += llmax(0, columnwidth); - - F32 columnrelwidth = 0.f; - child->getAttributeF32("relwidth", columnrelwidth); - - LLFontGL::HAlign h_align = LLFontGL::LEFT; - h_align = LLView::selectFontHAlign(child); - - columns[index]["name"] = columnname; - columns[index]["sort"] = sortname; - columns[index]["sort_ascending"] = sort_ascending; - columns[index]["image"] = imagename; - columns[index]["label"] = labelname; - columns[index]["width"] = columnwidth; - columns[index]["relwidth"] = columnrelwidth; - columns[index]["dynamicwidth"] = columndynamicwidth; - columns[index]["halign"] = (S32)h_align; - columns[index]["tool_tip"] = tooltip; - - index++; - } - } - scroll_list->setTotalStaticColumnWidth(total_static); - scroll_list->setColumnHeadings(columns); - - for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) - { - if (child->hasName("row")) - { - LLUUID id; - child->getAttributeUUID("id", id); - - LLSD row; - - row["id"] = id; - - S32 column_idx = 0; - LLXMLNodePtr row_child; - for (row_child = child->getFirstChild(); row_child.notNull(); row_child = row_child->getNextSibling()) - { - if (row_child->hasName("column")) - { - LLString value = row_child->getTextContents(); - - LLString columnname(""); - row_child->getAttributeString("name", columnname); - - LLString font(""); - row_child->getAttributeString("font", font); - - LLString font_style(""); - row_child->getAttributeString("font-style", font_style); - - row["columns"][column_idx]["column"] = columnname; - row["columns"][column_idx]["value"] = value; - row["columns"][column_idx]["font"] = font; - row["columns"][column_idx]["font-style"] = font_style; - column_idx++; - } - } - scroll_list->addElement(row); - } - } - - LLString contents = node->getTextContents(); - if (!contents.empty()) - { - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep("\t\n"); - tokenizer tokens(contents, sep); - tokenizer::iterator token_iter = tokens.begin(); - - while(token_iter != tokens.end()) - { - const char* line = token_iter->c_str(); - scroll_list->addSimpleElement(line); - ++token_iter; - } - } - - return scroll_list; + mTotalStaticColumnWidth += llmax(0, new_width) - llmax(0, col->getWidth()); } // LLEditMenuHandler functions @@ -2927,7 +2507,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac // virtual void LLScrollListCtrl::copy() { - LLString buffer; + std::string buffer; std::vector<LLScrollListItem*> items = getAllSelected(); std::vector<LLScrollListItem*>::iterator itor; @@ -2997,73 +2577,87 @@ BOOL LLScrollListCtrl::canDeselect() const void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) { - LLString name = column["name"].asString(); + LLScrollListColumn::Params p; + LLParamSDParser::instance().readSD(column, p); + addColumn(p, pos); +} + +void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params, EAddPosition pos) +{ + if (!column_params.validateBlock()) return; + + std::string name = column_params.name; // if no column name provided, just use ordinal as name if (name.empty()) { - std::ostringstream new_name; - new_name << mColumnsIndexed.size(); - name = new_name.str(); + name = llformat("%d", mColumnsIndexed.size()); } + if (mColumns.find(name) == mColumns.end()) { // Add column - mColumns[name] = LLScrollListColumn(column); + mColumns[name] = LLScrollListColumn(column_params, this); LLScrollListColumn* new_column = &mColumns[name]; - new_column->mParentCtrl = this; new_column->mIndex = mColumns.size()-1; // Add button - if (new_column->mWidth > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth) + if (new_column->getWidth() > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth) { + if (getNumColumns() > 0) + { + mTotalColumnPadding += mColumnPadding; + } if (new_column->mRelWidth >= 0) { - new_column->mWidth = (S32)llround(new_column->mRelWidth*mItemListRect.getWidth()); + new_column->setWidth((S32)llround(new_column->mRelWidth*mItemListRect.getWidth())); } else if(new_column->mDynamicWidth) { mNumDynamicWidthColumns++; - new_column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; + new_column->setWidth((mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns); } S32 top = mItemListRect.mTop; + S32 left = mItemListRect.mLeft; + for (std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.begin(); + itor != mColumns.end(); + ++itor) { - std::map<LLString, LLScrollListColumn>::iterator itor; - for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) + if (itor->second.mIndex < new_column->mIndex && + itor->second.getWidth() > 0) { - if (itor->second.mIndex < new_column->mIndex && - itor->second.mWidth > 0) - { - left += itor->second.mWidth + mColumnPadding; - } + left += itor->second.getWidth() + mColumnPadding; } } - LLString button_name = "btn_" + name; - S32 right = left+new_column->mWidth; + + S32 right = left+new_column->getWidth(); if (new_column->mIndex != (S32)mColumns.size()-1) { right += mColumnPadding; } + LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top); - new_column->mHeader = new LLColumnHeader(button_name, temp_rect, new_column); - if(column["image"].asString() != "") + + LLScrollColumnHeader::Params params(LLUICtrlFactory::getDefaultParams<LLScrollColumnHeader>()); + params.name = "btn_" + name; + params.rect = temp_rect; + params.column = new_column; + params.tool_tip = column_params.tool_tip; + params.tab_stop = false; + params.visible = mDisplayColumnHeaders; + + if(column_params.header.image.isProvided()) { - //new_column->mHeader->setScaleImage(false); - new_column->mHeader->setImage(column["image"].asString()); + params.image_selected = column_params.header.image; + params.image_unselected = column_params.header.image; } else { - new_column->mHeader->setLabel(new_column->mLabel); - //new_column->mHeader->setLabel(new_column->mLabel); + params.label = column_params.header.label; } - new_column->mHeader->setToolTip(column["tool_tip"].asString()); - - //RN: although it might be useful to change sort order with the keyboard, - // mixing tab stops on child items along with the parent item is not supported yet - new_column->mHeader->setTabStop(FALSE); + new_column->mHeader = LLUICtrlFactory::create<LLScrollColumnHeader>(params); addChild(new_column->mHeader); - new_column->mHeader->setVisible(mDisplayColumnHeaders); sendChildToFront(mScrollbar); } @@ -3084,7 +2678,7 @@ void LLScrollListCtrl::onClickColumn(void *userdata) S32 column_index = info->mIndex; LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; - bool ascending = column->mSortAscending; + bool ascending = column->mSortDirection == LLScrollListColumn::ASCENDING; if (column->mSortingColumn != column->mName && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) { @@ -3099,11 +2693,11 @@ void LLScrollListCtrl::onClickColumn(void *userdata) ascending = !parent->mSortColumns.back().second; } - parent->sortByColumn(column_index, ascending); + parent->sortByColumnIndex(column_index, ascending); if (parent->mOnSortChangedCallback) { - parent->mOnSortChangedCallback(parent->getCallbackUserData()); + parent->mOnSortChangedCallback(); } } @@ -3115,17 +2709,17 @@ std::string LLScrollListCtrl::getSortColumnName() else return ""; } -BOOL LLScrollListCtrl::needsSorting() +BOOL LLScrollListCtrl::hasSortOrder() const { return !mSortColumns.empty(); } void LLScrollListCtrl::clearColumns() { - std::map<LLString, LLScrollListColumn>::iterator itor; + std::map<std::string, LLScrollListColumn>::iterator itor; for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) { - LLColumnHeader *header = itor->second.mHeader; + LLScrollColumnHeader *header = itor->second.mHeader; if (header) { removeChild(header); @@ -3134,17 +2728,19 @@ void LLScrollListCtrl::clearColumns() } mColumns.clear(); mSortColumns.clear(); + mTotalStaticColumnWidth = 0; + mTotalColumnPadding = 0; } -void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& label) +void LLScrollListCtrl::setColumnLabel(const std::string& column, const std::string& label) { - std::map<LLString, LLScrollListColumn>::iterator itor = mColumns.find(column); - if (itor != mColumns.end()) + LLScrollListColumn* columnp = getColumn(column); + if (columnp) { - itor->second.mLabel = label; - if (itor->second.mHeader) + columnp->mLabel = label; + if (columnp->mHeader) { - itor->second.mHeader->setLabel(label); + columnp->mHeader->setLabel(label); } } } @@ -3158,74 +2754,66 @@ LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index) return mColumnsIndexed[index]; } -void LLScrollListCtrl::setColumnHeadings(LLSD headings) +LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name) { - mColumns.clear(); - LLSD::array_const_iterator itor; - for (itor = headings.beginArray(); itor != headings.endArray(); ++itor) + column_map_t::iterator column_itor = mColumns.find(name); + if (column_itor != mColumns.end()) { - addColumn(*itor); + return &column_itor->second; } + return NULL; } -LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition pos, void* userdata) + +LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata) { - // ID - LLSD id = value["id"]; + LLScrollListItem::Params item_params; + LLParamSDParser::instance().readSD(element, item_params); + item_params.userdata = userdata; + return addRow(item_params, pos); +} - LLScrollListItem *new_item = new LLScrollListItem(id, userdata); - if (value.has("enabled")) - { - new_item->setEnabled( value["enabled"].asBoolean() ); - } +LLScrollListItem* LLScrollListCtrl::addRow(const LLScrollListItem::Params& item_p, EAddPosition pos) +{ + LLScrollListItem *new_item = new LLScrollListItem(item_p); + return addRow(new_item, item_p, pos); +} +LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& item_p, EAddPosition pos) +{ + if (!item_p.validateBlock() || !new_item) return NULL; new_item->setNumColumns(mColumns.size()); // Add any columns we don't already have - LLSD columns = value["columns"]; - LLSD::array_const_iterator itor; - S32 col_index = 0 ; - for (itor = columns.beginArray(); itor != columns.endArray(); ++itor) - { - if (itor->isUndefined()) - { - // skip unused columns in item passed in - continue; - } - LLString column = (*itor)["column"].asString(); + S32 col_index = 0; - LLScrollListColumn* columnp = NULL; + for(LLInitParam::ParamIterator<LLScrollListCell::Params>::const_iterator itor = item_p.columns().begin(); + itor != item_p.columns().end(); + ++itor) + { + LLScrollListCell::Params cell_p = *itor; + std::string column = cell_p.column; // empty columns strings index by ordinal if (column.empty()) { - std::ostringstream new_name; - new_name << col_index; - column = new_name.str(); + column = llformat("%d", col_index); } - std::map<LLString, LLScrollListColumn>::iterator column_itor; - column_itor = mColumns.find(column); - if (column_itor != mColumns.end()) - { - columnp = &column_itor->second; - } + LLScrollListColumn* columnp = getColumn(column); // create new column on demand if (!columnp) { - LLSD new_column; - new_column["name"] = column; - new_column["label"] = column; + LLScrollListColumn::Params new_column; + new_column.name = column; + new_column.header.label = column; + // if width supplied for column, use it, otherwise // use adaptive width - if (itor->has("width")) + if (cell_p.width.isProvided()) { - new_column["width"] = (*itor)["width"]; - } - else - { - new_column["dynamicwidth"] = true; + new_column.width.pixel_width = cell_p.width; } addColumn(new_column); columnp = &mColumns[column]; @@ -3233,70 +2821,48 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p } S32 index = columnp->mIndex; - S32 width = columnp->mWidth; - LLFontGL::HAlign font_alignment = columnp->mFontAlignment; + cell_p.width.setIfNotProvided(columnp->getWidth()); - LLSD value = (*itor)["value"]; - LLString fontname = (*itor)["font"].asString(); - LLString fontstyle = (*itor)["font-style"].asString(); - LLString type = (*itor)["type"].asString(); - BOOL has_color = (*itor).has("color"); - LLColor4 color = ((*itor)["color"]); - BOOL enabled = !(*itor).has("enabled") || (*itor)["enabled"].asBoolean() == true; + LLScrollListCell* cell = LLScrollListCell::create(cell_p); - const LLFontGL *font = LLResMgr::getInstance()->getRes(fontname); - if (!font) + if (cell) { - font = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL ); - } - U8 font_style = LLFontGL::getStyleFromString(fontstyle); - - if (type == "icon") - { - LLScrollListIcon* cell = new LLScrollListIcon(value, width); - if (has_color) - { - cell->setColor(color); - } new_item->setColumn(index, cell); - } - else if (type == "checkbox") - { - LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl("check", - LLRect(0, width, width, 0), " "); - ctrl->setEnabled(enabled); - ctrl->setValue(value); - LLScrollListCheck* cell = new LLScrollListCheck(ctrl,width); - if (has_color) + if (columnp->mHeader + && cell->isText() + && !cell->getValue().asString().empty()) { - cell->setColor(color); + columnp->mHeader->setHasResizableElement(TRUE); } - new_item->setColumn(index, cell); } - else if (type == "separator") + + col_index++; + } + + if (item_p.columns().empty()) + { + if (mColumns.empty()) { - LLScrollListSeparator* cell = new LLScrollListSeparator(width); - if (has_color) - { - cell->setColor(color); - } - new_item->setColumn(index, cell); + LLScrollListColumn::Params new_column; + new_column.name = "0"; + + addColumn(new_column); + new_item->setNumColumns(mColumns.size()); } - else + + LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value)); + if (cell) { - LLScrollListText* cell = new LLScrollListText(value.asString(), font, width, font_style, font_alignment); - if (has_color) - { - cell->setColor(color); - } - new_item->setColumn(index, cell); - if (columnp->mHeader && !value.asString().empty()) + LLScrollListColumn* columnp = &(mColumns.begin()->second); + + new_item->setColumn(0, cell); + if (columnp->mHeader + && cell->isText() + && !cell->getValue().asString().empty()) { columnp->mHeader->setHasResizableElement(TRUE); } } - - col_index++; } // add dummy cells for missing columns @@ -3306,16 +2872,18 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p if (new_item->getColumn(column_idx) == NULL) { LLScrollListColumn* column_ptr = &column_it->second; - new_item->setColumn(column_idx, new LLScrollListText("", LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL ), column_ptr->mWidth, LLFontGL::NORMAL)); + LLScrollListCell::Params cell_p; + cell_p.width = column_ptr->getWidth(); + + new_item->setColumn(column_idx, new LLScrollListSpacer(cell_p)); } } addItem(new_item, pos); - return new_item; } -LLScrollListItem* LLScrollListCtrl::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id) +LLScrollListItem* LLScrollListCtrl::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) { LLSD entry_id = id; @@ -3324,14 +2892,13 @@ LLScrollListItem* LLScrollListCtrl::addSimpleElement(const LLString& value, EAdd entry_id = value; } - LLScrollListItem *new_item = new LLScrollListItem(entry_id); - - const LLFontGL *font = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL ); - - new_item->addColumn(value, font, getRect().getWidth()); - - addItem(new_item, pos); - return new_item; + LLScrollListItem::Params item_params; + item_params.value(entry_id); + item_params.columns.add() + .value(value) + .font(LLFontGL::getFontSansSerifSmall()); + + return addRow(item_params, pos); } void LLScrollListCtrl::setValue(const LLSD& value ) @@ -3384,7 +2951,6 @@ BOOL LLScrollListCtrl::operateOnAll(EOperation op) //virtual void LLScrollListCtrl::setFocus(BOOL b) { - mSearchString.clear(); // for tabbing into pristine scroll lists (Finder) if (!getFirstSelected()) { @@ -3429,406 +2995,9 @@ void LLScrollListCtrl::onFocusLost() { gFocusMgr.setMouseCapture(NULL); } - LLUICtrl::onFocusLost(); -} - -LLColumnHeader::LLColumnHeader(const LLString& label, const LLRect &rect, LLScrollListColumn* column, const LLFontGL* fontp) : - LLComboBox(label, rect, label, NULL, NULL), - mColumn(column), - mOrigLabel(label), - mShowSortOptions(FALSE), - mHasResizableElement(FALSE) -{ - mListPosition = LLComboBox::ABOVE; - setCommitCallback(onSelectSort); - setCallbackUserData(this); - mButton->setTabStop(FALSE); - // require at least two frames between mouse down and mouse up event to capture intentional "hold" not just bad framerate - mButton->setHeldDownDelay(LLUI::sConfigGroup->getF32("ColumnHeaderDropDownDelay"), 2); - mButton->setHeldDownCallback(onHeldDown); - mButton->setClickedCallback(onClick); - mButton->setMouseDownCallback(onMouseDown); - - mButton->setCallbackUserData(this); - - mAscendingText = "[LOW]...[HIGH](Ascending)"; - mDescendingText = "[HIGH]...[LOW](Descending)"; - - mList->reshape(llmax(mList->getRect().getWidth(), 110, getRect().getWidth()), mList->getRect().getHeight()); - - // resize handles on left and right - const S32 RESIZE_BAR_THICKNESS = 3; - mResizeBar = new LLResizeBar( - "resizebar", - this, - LLRect( getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0), - MIN_COLUMN_WIDTH, S32_MAX, LLResizeBar::RIGHT ); - addChild(mResizeBar); - - mResizeBar->setEnabled(FALSE); -} - -LLColumnHeader::~LLColumnHeader() -{ -} - -void LLColumnHeader::draw() -{ - BOOL draw_arrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn; - - BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); - mButton->setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, draw_arrow ? LLColor4::white : LLColor4::transparent); - mArrowImage = mButton->getImageOverlay(); - - //BOOL clip = getRect().mRight > mColumn->mParentCtrl->getItemListRect().getWidth(); - //LLGLEnable scissor_test(clip ? GL_SCISSOR_TEST : GL_FALSE); - - //LLRect column_header_local_rect(-getRect().mLeft, getRect().getHeight(), mColumn->mParentCtrl->getItemListRect().getWidth() - getRect().mLeft, 0); - //LLUI::setScissorRegionLocal(column_header_local_rect); - - // Draw children - LLComboBox::draw(); - - if (mList->getVisible()) - { - // sync sort order with list selection every frame - mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn, getCurrentIndex() == 0); - } -} -BOOL LLColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - if (canResize() && mResizeBar->getRect().pointInRect(x, y)) - { - // reshape column to max content width - LLRect column_rect = getRect(); - column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth; - userSetShape(column_rect); - } - else - { - onClick(this); - } - return TRUE; -} - -void LLColumnHeader::setImage(const LLString &image_name) -{ - if (mButton) - { - mButton->setImageSelected(image_name); - mButton->setImageUnselected(image_name); - } -} - -//static -void LLColumnHeader::onClick(void* user_data) -{ - LLColumnHeader* headerp = (LLColumnHeader*)user_data; - if (!headerp) return; - - LLScrollListColumn* column = headerp->mColumn; - if (!column) return; - - if (headerp->mList->getVisible()) - { - headerp->hideList(); - } - - LLScrollListCtrl::onClickColumn(column); - - // propage new sort order to sort order list - headerp->mList->selectNthItem(column->mParentCtrl->getSortAscending() ? 0 : 1); -} - -//static -void LLColumnHeader::onMouseDown(void* user_data) -{ - // for now, do nothing but block the normal showList() behavior - return; -} - -//static -void LLColumnHeader::onHeldDown(void* user_data) -{ - LLColumnHeader* headerp = (LLColumnHeader*)user_data; - headerp->showList(); -} - -void LLColumnHeader::showList() -{ - if (mShowSortOptions) - { - //LLSD item_val = mColumn->mParentCtrl->getFirstData()->getValue(); - mOrigLabel = mButton->getLabelSelected(); - - // move sort column over to this column and do initial sort - mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn, mColumn->mParentCtrl->getSortAscending()); - - LLString low_item_text; - LLString high_item_text; - - LLScrollListItem* itemp = mColumn->mParentCtrl->getFirstData(); - if (itemp) - { - LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex); - if (cell && cell->isText()) - { - if (mColumn->mParentCtrl->getSortAscending()) - { - low_item_text = cell->getValue().asString(); - } - else - { - high_item_text = cell->getValue().asString(); - } - } - } - - itemp = mColumn->mParentCtrl->getLastData(); - if (itemp) - { - LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex); - if (cell && cell->isText()) - { - if (mColumn->mParentCtrl->getSortAscending()) - { - high_item_text = cell->getValue().asString(); - } - else - { - low_item_text = cell->getValue().asString(); - } - } - } - - LLString::truncate(low_item_text, 3); - LLString::truncate(high_item_text, 3); - - LLString ascending_string; - LLString descending_string; - - if (low_item_text.empty() || high_item_text.empty()) - { - ascending_string = "Ascending"; - descending_string = "Descending"; - } - else - { - mAscendingText.setArg("[LOW]", low_item_text); - mAscendingText.setArg("[HIGH]", high_item_text); - mDescendingText.setArg("[LOW]", low_item_text); - mDescendingText.setArg("[HIGH]", high_item_text); - ascending_string = mAscendingText.getString(); - descending_string = mDescendingText.getString(); - } - - S32 text_width = LLFontGL::sSansSerifSmall->getWidth(ascending_string); - text_width = llmax(text_width, LLFontGL::sSansSerifSmall->getWidth(descending_string)) + 10; - text_width = llmax(text_width, getRect().getWidth() - 30); - - mList->getColumn(0)->mWidth = text_width; - ((LLScrollListText*)mList->getFirstData()->getColumn(0))->setText(ascending_string); - ((LLScrollListText*)mList->getLastData()->getColumn(0))->setText(descending_string); - - mList->reshape(llmax(text_width + 30, 110, getRect().getWidth()), mList->getRect().getHeight()); - - LLComboBox::showList(); - } -} - -//static -void LLColumnHeader::onSelectSort(LLUICtrl* ctrl, void* user_data) -{ - LLColumnHeader* headerp = (LLColumnHeader*)user_data; - if (!headerp) return; - - LLScrollListColumn* column = headerp->mColumn; - if (!column) return; - LLScrollListCtrl *parent = column->mParentCtrl; - if (!parent) return; - - if (headerp->getCurrentIndex() == 0) - { - // ascending - parent->sortByColumn(column->mSortingColumn, TRUE); - } - else - { - // descending - parent->sortByColumn(column->mSortingColumn, FALSE); - } - - // restore original column header - headerp->setLabel(headerp->mOrigLabel); -} - -LLView* LLColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding) -{ - // this logic assumes dragging on right - llassert(snap_edge == SNAP_RIGHT); - - // use higher snap threshold for column headers - threshold = llmin(threshold, 15); - - LLRect snap_rect = getSnapRect(); - - S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth(); - - // x coord growing means column growing, so same signs mean we're going in right direction - if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) - { - new_edge_val = snap_rect.mRight + snap_delta; - } - else - { - LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1); - while (next_column) - { - if (next_column->mHeader) - { - snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight; - if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) - { - new_edge_val = snap_rect.mRight + snap_delta; - } - break; - } - next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1); - } - } - - return this; -} - -void LLColumnHeader::userSetShape(const LLRect& new_rect) -{ - S32 new_width = new_rect.getWidth(); - S32 delta_width = new_width - (getRect().getWidth() /*+ mColumn->mParentCtrl->getColumnPadding()*/); - - if (delta_width != 0) - { - S32 remaining_width = delta_width; - S32 col; - for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++) - { - LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); - if (!columnp) break; - - if (columnp->mHeader && columnp->mHeader->canResize()) - { - // how many pixels in width can this column afford to give up? - S32 resize_buffer_amt = llmax(0, columnp->mWidth - MIN_COLUMN_WIDTH); - - // user shrinking column, need to add width to other columns - if (delta_width < 0) - { - if (!columnp->mDynamicWidth && columnp->mWidth > 0) - { - // statically sized column, give all remaining width to this column - columnp->mWidth -= remaining_width; - if (columnp->mRelWidth > 0.f) - { - columnp->mRelWidth = (F32)columnp->mWidth / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); - } - } - break; - } - else - { - // user growing column, need to take width from other columns - remaining_width -= resize_buffer_amt; - - if (!columnp->mDynamicWidth && columnp->mWidth > 0) - { - columnp->mWidth -= llmin(columnp->mWidth - MIN_COLUMN_WIDTH, delta_width); - if (columnp->mRelWidth > 0.f) - { - columnp->mRelWidth = (F32)columnp->mWidth / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); - } - } - - if (remaining_width <= 0) - { - // width sucked up from neighboring columns, done - break; - } - } - } - } - - // clamp resize amount to maximum that can be absorbed by other columns - if (delta_width > 0) - { - delta_width -= llmax(remaining_width, 0); - } - - // propagate constrained delta_width to new width for this column - new_width = getRect().getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding(); - - // use requested width - mColumn->mWidth = new_width; - - // update proportional spacing - if (mColumn->mRelWidth > 0.f) - { - mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); - } - - // tell scroll list to layout columns again - // do immediate update to get proper feedback to resize handle - // which needs to know how far the resize actually went - mColumn->mParentCtrl->updateColumns(); - } -} - -void LLColumnHeader::setHasResizableElement(BOOL resizable) -{ - // for now, dynamically spaced columns can't be resized - if (mColumn->mDynamicWidth) return; - - if (resizable != mHasResizableElement) - { - mHasResizableElement = resizable; - - S32 num_resizable_columns = 0; - S32 col; - for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) - { - LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); - if (columnp->mHeader && columnp->mHeader->canResize()) - { - num_resizable_columns++; - } - } - - S32 num_resizers_enabled = 0; - - // now enable/disable resize handles on resizable columns if we have at least two - for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) - { - LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); - if (!columnp->mHeader) continue; - BOOL enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize(); - columnp->mHeader->enableResizeBar(enable); - if (enable) - { - num_resizers_enabled++; - } - } - } -} + mSearchString.clear(); -void LLColumnHeader::enableResizeBar(BOOL enable) -{ - // for now, dynamically spaced columns can't be resized - if (!mColumn->mDynamicWidth) - { - mResizeBar->setEnabled(enable); - } + LLUICtrl::onFocusLost(); } -BOOL LLColumnHeader::canResize() -{ - return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth); -} |