diff options
Diffstat (limited to 'indra/llui/llflatlistview.cpp')
-rw-r--r-- | indra/llui/llflatlistview.cpp | 420 |
1 files changed, 351 insertions, 69 deletions
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index d8084fd9aa..8de44455de 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -1,10 +1,10 @@ /** * @file llflatlistview.cpp - * @brief LLFlatListView base class + * @brief LLFlatListView base class and extension to support messages for several cases of an empty list. * * $LicenseInfo:firstyear=2009&license=viewergpl$ * - * Copyright (c) 2009, Linden Research, Inc. + * Copyright (c) 2009-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -50,14 +50,21 @@ LLFlatListView::Params::Params() allow_select("allow_select"), multi_select("multi_select"), keep_one_selected("keep_one_selected"), + keep_selection_visible_on_reshape("keep_selection_visible_on_reshape",false), no_items_text("no_items_text") {}; void LLFlatListView::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) { + S32 delta = height - getRect().getHeight(); LLScrollContainer::reshape(width, height, called_from_parent); setItemsNoScrollWidth(width); rearrangeItems(); + + if(delta!= 0 && mKeepSelectionVisibleOnReshape) + { + ensureSelectedVisible(); + } } const LLRect& LLFlatListView::getItemsRect() const @@ -150,7 +157,7 @@ bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, } -bool LLFlatListView::removeItem(LLPanel* item) +bool LLFlatListView::removeItem(LLPanel* item, bool rearrange) { if (!item) return false; if (item->getParent() != mItemsPanel) return false; @@ -158,22 +165,22 @@ bool LLFlatListView::removeItem(LLPanel* item) item_pair_t* item_pair = getItemPair(item); if (!item_pair) return false; - return removeItemPair(item_pair); + return removeItemPair(item_pair, rearrange); } -bool LLFlatListView::removeItemByValue(const LLSD& value) +bool LLFlatListView::removeItemByValue(const LLSD& value, bool rearrange) { if (value.isUndefined()) return false; item_pair_t* item_pair = getItemPair(value); if (!item_pair) return false; - return removeItemPair(item_pair); + return removeItemPair(item_pair, rearrange); } -bool LLFlatListView::removeItemByUUID(const LLUUID& uuid) +bool LLFlatListView::removeItemByUUID(const LLUUID& uuid, bool rearrange) { - return removeItemByValue(LLSD(uuid)); + return removeItemByValue(LLSD(uuid), rearrange); } LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const @@ -297,8 +304,32 @@ void LLFlatListView::setNoItemsCommentText(const std::string& comment_text) mNoItemsCommentTextbox->setValue(comment_text); } +U32 LLFlatListView::size(const bool only_visible_items) const +{ + if (only_visible_items) + { + U32 size = 0; + for (pairs_const_iterator_t + iter = mItemPairs.begin(), + iter_end = mItemPairs.end(); + iter != iter_end; ++iter) + { + if ((*iter)->first->getVisible()) + ++size; + } + return size; + } + else + { + return mItemPairs.size(); + } +} + void LLFlatListView::clear() { + // This will clear mSelectedItemPairs, calling all appropriate callbacks. + resetSelection(); + // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex. for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) { @@ -307,7 +338,6 @@ void LLFlatListView::clear() delete *it; } mItemPairs.clear(); - mSelectedItemPairs.clear(); // also set items panel height to zero. Reshape it to allow reshaping of non-item children LLRect rc = mItemsPanel->getRect(); @@ -358,6 +388,8 @@ LLFlatListView::LLFlatListView(const LLFlatListView::Params& p) , mCommitOnSelectionChange(false) , mPrevNotifyParentRect(LLRect()) , mNoItemsCommentTextbox(NULL) + , mIsConsecutiveSelection(false) + , mKeepSelectionVisibleOnReshape(p.keep_selection_visible_on_reshape) { mBorderThickness = getBorderWidth(); @@ -426,7 +458,7 @@ void LLFlatListView::rearrangeItems() { static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); - setNoItemsCommentVisible(mItemPairs.empty()); + setNoItemsCommentVisible(0==size()); if (mItemPairs.empty()) return; @@ -504,9 +536,88 @@ void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) //*TODO find a better place for that enforcing stuff if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return; - - if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection(); + + if ( (mask & MASK_SHIFT) && !(mask & MASK_CONTROL) + && mMultipleSelection && !mSelectedItemPairs.empty() ) + { + item_pair_t* last_selected_pair = mSelectedItemPairs.back(); + + // If item_pair is already selected - do nothing + if (last_selected_pair == item_pair) + return; + + bool grab_items = false; + bool reverse = false; + pairs_list_t pairs_to_select; + + // Pick out items from list between last selected and current clicked item_pair. + for (pairs_iterator_t + iter = mItemPairs.begin(), + iter_end = mItemPairs.end(); + iter != iter_end; ++iter) + { + item_pair_t* cur = *iter; + if (cur == last_selected_pair || cur == item_pair) + { + // We've got reverse selection if last grabed item isn't a new selection. + reverse = grab_items && (cur != item_pair); + grab_items = !grab_items; + // Skip last selected and current clicked item pairs. + continue; + } + if (!cur->first->getVisible()) + { + // Skip invisible item pairs. + continue; + } + if (grab_items) + { + pairs_to_select.push_back(cur); + } + } + + if (reverse) + { + pairs_to_select.reverse(); + } + + pairs_to_select.push_back(item_pair); + + for (pairs_iterator_t + iter = pairs_to_select.begin(), + iter_end = pairs_to_select.end(); + iter != iter_end; ++iter) + { + item_pair_t* pair_to_select = *iter; + if (isSelected(pair_to_select)) + { + // Item was already selected but there is a need to keep order from last selected pair to new selection. + // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair(). + mSelectedItemPairs.remove(pair_to_select); + mSelectedItemPairs.push_back(pair_to_select); + } + else + { + selectItemPair(pair_to_select, true); + } + } + + if (!select_item) + { + // Update last selected item border. + mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); + } + return; + } + + //no need to do additional commit on selection reset + if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection(true); + + //only CTRL usage allows to deselect an item, usual clicking on an item cannot deselect it + if (mask & MASK_CONTROL) selectItemPair(item_pair, select_item); + else + selectItemPair(item_pair, true); } void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask) @@ -558,11 +669,11 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask) } break; } - case 'A': + case KEY_ESCAPE: { - if(MASK_CONTROL & mask) + if (mask == MASK_NONE) { - handled = (BOOL)selectAll(); + setFocus(FALSE); // pass focus to the game area (EXT-8357) } break; } @@ -675,10 +786,24 @@ bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select) // Stretch selected item rect to ensure it won't be clipped mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); + // By default mark it as not consecutive selection + mIsConsecutiveSelection = false; return true; } +void LLFlatListView::scrollToShowFirstSelectedItem() +{ + if (!mSelectedItemPairs.size()) return; + + LLRect selected_rc = mSelectedItemPairs.front()->first->getRect(); + + if (selected_rc.isValid()) + { + scrollToShowRect(selected_rc); + } +} + LLRect LLFlatListView::getLastSelectedItemRect() { if (!mSelectedItemPairs.size()) @@ -691,14 +816,44 @@ LLRect LLFlatListView::getLastSelectedItemRect() void LLFlatListView::selectFirstItem () { - selectItemPair(mItemPairs.front(), true); - ensureSelectedVisible(); + // No items - no actions! + if (0 == size()) return; + + // Select first visible item + for (pairs_iterator_t + iter = mItemPairs.begin(), + iter_end = mItemPairs.end(); + iter != iter_end; ++iter) + { + // skip invisible items + if ( (*iter)->first->getVisible() ) + { + selectItemPair(*iter, true); + ensureSelectedVisible(); + break; + } + } } void LLFlatListView::selectLastItem () { - selectItemPair(mItemPairs.back(), true); - ensureSelectedVisible(); + // No items - no actions! + if (0 == size()) return; + + // Select last visible item + for (pairs_list_t::reverse_iterator + r_iter = mItemPairs.rbegin(), + r_iter_end = mItemPairs.rend(); + r_iter != r_iter_end; ++r_iter) + { + // skip invisible items + if ( (*r_iter)->first->getVisible() ) + { + selectItemPair(*r_iter, true); + ensureSelectedVisible(); + break; + } + } } void LLFlatListView::ensureSelectedVisible() @@ -716,14 +871,25 @@ void LLFlatListView::ensureSelectedVisible() bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection) { // No items - no actions! - if ( !mItemPairs.size() ) + if ( 0 == size() ) return false; - - item_pair_t* to_sel_pair = NULL; - item_pair_t* cur_sel_pair = NULL; + if (!mIsConsecutiveSelection) + { + // Leave only one item selected if list has not consecutive selection + if (mSelectedItemPairs.size() && !reset_selection) + { + item_pair_t* cur_sel_pair = mSelectedItemPairs.back(); + resetSelection(); + selectItemPair (cur_sel_pair, true); + } + } + if ( mSelectedItemPairs.size() ) { + item_pair_t* to_sel_pair = NULL; + item_pair_t* cur_sel_pair = NULL; + // Take the last selected pair cur_sel_pair = mSelectedItemPairs.back(); // Bases on given direction choose next item to select @@ -757,43 +923,52 @@ bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selecti } } } + + if ( to_sel_pair ) + { + bool select = true; + if ( reset_selection ) + { + // Reset current selection if we were asked about it + resetSelection(); + } + else + { + // If item already selected and no reset request than we should deselect last selected item. + select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair)); + } + // Select/Deselect next item + selectItemPair(select ? to_sel_pair : cur_sel_pair, select); + // Mark it as consecutive selection + mIsConsecutiveSelection = true; + return true; + } } else { // If there weren't selected items then choose the first one bases on given direction - cur_sel_pair = (is_up_direction) ? mItemPairs.back() : mItemPairs.front(); // Force selection to first item - to_sel_pair = cur_sel_pair; - } - - - if ( to_sel_pair ) - { - bool select = true; - - if ( reset_selection ) - { - // Reset current selection if we were asked about it - resetSelection(); - } + if (is_up_direction) + selectLastItem(); else - { - // If item already selected and no reset request than we should deselect last selected item. - select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair)); - } - - // Select/Deselect next item - selectItemPair(select ? to_sel_pair : cur_sel_pair, select); - + selectFirstItem(); + // Mark it as consecutive selection + mIsConsecutiveSelection = true; return true; } + return false; } -bool LLFlatListView::selectAll() +BOOL LLFlatListView::canSelectAll() const +{ + return 0 != size() && mAllowSelection && mMultipleSelection; +} + +void LLFlatListView::selectAll() { if (!mAllowSelection || !mMultipleSelection) - return false; + return; mSelectedItemPairs.clear(); @@ -813,8 +988,6 @@ bool LLFlatListView::selectAll() // Stretch selected item rect to ensure it won't be clipped mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); - - return true; } bool LLFlatListView::isSelected(item_pair_t* item_pair) const @@ -825,11 +998,12 @@ bool LLFlatListView::isSelected(item_pair_t* item_pair) const return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end; } -bool LLFlatListView::removeItemPair(item_pair_t* item_pair) +bool LLFlatListView::removeItemPair(item_pair_t* item_pair, bool rearrange) { llassert(item_pair); bool deleted = false; + bool selection_changed = false; for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it) { item_pair_t* _item_pair = *it; @@ -849,6 +1023,7 @@ bool LLFlatListView::removeItemPair(item_pair_t* item_pair) if (selected_item_pair == item_pair) { it = mSelectedItemPairs.erase(it); + selection_changed = true; break; } } @@ -857,8 +1032,16 @@ bool LLFlatListView::removeItemPair(item_pair_t* item_pair) item_pair->first->die(); delete item_pair; + if (rearrange) + { rearrangeItems(); notifyParentItemsRectChanged(); + } + + if (selection_changed && mCommitOnSelectionChange) + { + onCommit(); + } return true; } @@ -903,25 +1086,7 @@ void LLFlatListView::setNoItemsCommentVisible(bool visible) const { if (mNoItemsCommentTextbox) { - if (visible) - { -/* -// *NOTE: MA 2010-02-04 -// Deprecated after params of the comment text box were moved into widget (flat_list_view.xml) -// can be removed later if nothing happened. - // We have to update child rect here because of issues with rect after reshaping while creating LLTextbox - // It is possible to have invalid LLRect if Flat List is in LLAccordionTab - LLRect comment_rect = getLocalRect(); - - // To see comment correctly (EXT - 3244) in mNoItemsCommentTextbox we must get border width - // of LLFlatListView (@see getBorderWidth()) and stretch mNoItemsCommentTextbox to this width - // But getBorderWidth() returns 0 if LLFlatListView not visible. So we have to get border width - // from 'scroll_border' - LLViewBorder* scroll_border = getChild<LLViewBorder>("scroll border"); - comment_rect.stretch(-scroll_border->getBorderWidth()); - mNoItemsCommentTextbox->setRect(comment_rect); -*/ - } + mSelectedItemsBorder->setVisible(!visible); mNoItemsCommentTextbox->setVisible(visible); } } @@ -951,12 +1116,21 @@ void LLFlatListView::getValues(std::vector<LLSD>& values) const // virtual void LLFlatListView::onFocusReceived() { + if (size()) + { mSelectedItemsBorder->setVisible(TRUE); + } + gEditMenuHandler = this; } // virtual void LLFlatListView::onFocusLost() { mSelectedItemsBorder->setVisible(FALSE); + // Route menu back to the default + if( gEditMenuHandler == this ) + { + gEditMenuHandler = NULL; + } } //virtual @@ -1060,4 +1234,112 @@ void LLFlatListView::detachItems(std::vector<LLPanel*>& detached_items) } } + +/************************************************************************/ +/* LLFlatListViewEx implementation */ +/************************************************************************/ +LLFlatListViewEx::Params::Params() +: no_items_msg("no_items_msg") +, no_filtered_items_msg("no_filtered_items_msg") +{ + +} + +LLFlatListViewEx::LLFlatListViewEx(const Params& p) +: LLFlatListView(p) +, mNoFilteredItemsMsg(p.no_filtered_items_msg) +, mNoItemsMsg(p.no_items_msg) +, mForceShowingUnmatchedItems(false) +, mHasMatchedItems(false) +{ + +} + +void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string) +{ + bool items_filtered = !filter_string.empty(); + if (items_filtered) + { + // items were filtered + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(filter_string); + std::string text = mNoFilteredItemsMsg; + LLStringUtil::format(text, args); + setNoItemsCommentText(text); + } + else + { + // list does not contain any items at all + setNoItemsCommentText(mNoItemsMsg); + } + +} + +bool LLFlatListViewEx::getForceShowingUnmatchedItems() +{ + return mForceShowingUnmatchedItems; +} + +void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show) +{ + mForceShowingUnmatchedItems = show; +} + +void LLFlatListViewEx::setFilterSubString(const std::string& filter_str) +{ + if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString)) + { + mFilterSubString = filter_str; + updateNoItemsMessage(mFilterSubString); + filterItems(); + } +} + +void LLFlatListViewEx::filterItems() +{ + typedef std::vector <LLPanel*> item_panel_list_t; + + std::string cur_filter = mFilterSubString; + LLStringUtil::toUpper(cur_filter); + + LLSD action; + action.with("match_filter", cur_filter); + + item_panel_list_t items; + getItems(items); + + mHasMatchedItems = false; + for (item_panel_list_t::iterator + iter = items.begin(), + iter_end = items.end(); + iter != iter_end; ++iter) + { + LLPanel* pItem = (*iter); + // 0 signifies that filter is matched, + // i.e. we don't hide items that don't support 'match_filter' action, separators etc. + if (0 == pItem->notify(action)) + { + mHasMatchedItems = true; + pItem->setVisible(true); + } + else + { + // TODO: implement (re)storing of current selection. + if(!mForceShowingUnmatchedItems) + { + selectItem(pItem, false); + } + pItem->setVisible(mForceShowingUnmatchedItems); + } + } + + sort(); + notifyParentItemsRectChanged(); +} + +bool LLFlatListViewEx::hasMatchedItems() +{ + return mHasMatchedItems; +} + //EOF |