/* * @file llfolderviewmodelinventory.cpp * @brief Implementation of the inventory-specific view model * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * 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. * * 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. * * 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 "llviewerprecompiledheaders.h" #include "llfolderviewmodelinventory.h" #include "llinventorymodelbackgroundfetch.h" #include "llinventoryfunctions.h" #include "llinventorypanel.h" #include "lltooldraganddrop.h" #include "llfavoritesbar.h" // // class LLFolderViewModelInventory // static LLTrace::BlockTimerStatHandle FTM_INVENTORY_SORT("Inventory Sort"); bool LLFolderViewModelInventory::startDrag(std::vector& items) { std::vector types; uuid_vec_t cargo_ids; std::vector::iterator item_it; bool can_drag = true; if (!items.empty()) { for (item_it = items.begin(); item_it != items.end(); ++item_it) { EDragAndDropType type = DAD_NONE; LLUUID id = LLUUID::null; can_drag = can_drag && static_cast(*item_it)->startDrag(&type, &id); types.push_back(type); cargo_ids.push_back(id); } LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, static_cast(items.front())->getDragSource(), mTaskID); } return can_drag; } void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) { LL_RECORD_BLOCK_TIME(FTM_INVENTORY_SORT); if (!folder->areChildrenInited() || !needsSort(folder->getViewModelItem())) return; LLFolderViewModelItemInventory* modelp = static_cast(folder->getViewModelItem()); if (modelp->getUUID().isNull()) return; for (std::list::iterator it = folder->getFoldersBegin(), end_it = folder->getFoldersEnd(); it != end_it; ++it) { // Recursive call to sort() on child (CHUI-849) LLFolderViewFolder* child_folderp = *it; sort(child_folderp); if (child_folderp->getFoldersCount() > 0) { time_t most_recent_folder_time = static_cast((*child_folderp->getFoldersBegin())->getViewModelItem())->getCreationDate(); LLFolderViewModelItemInventory* modelp = static_cast(child_folderp->getViewModelItem()); if (most_recent_folder_time > modelp->getCreationDate()) { modelp->setCreationDate(most_recent_folder_time); } } if (child_folderp->getItemsCount() > 0) { time_t most_recent_item_time = static_cast((*child_folderp->getItemsBegin())->getViewModelItem())->getCreationDate(); LLFolderViewModelItemInventory* modelp = static_cast(child_folderp->getViewModelItem()); if (most_recent_item_time > modelp->getCreationDate()) { modelp->setCreationDate(most_recent_item_time); } } } base_t::sort(folder); } bool LLFolderViewModelInventory::contentsReady() { return !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); } bool LLFolderViewModelInventory::isFolderComplete(LLFolderViewFolder* folder) { LLFolderViewModelItemInventory* modelp = static_cast(folder->getViewModelItem()); LLUUID cat_id = modelp->getUUID(); if (cat_id.isNull()) { return false; } LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); if (cat) { // don't need to check version - descendents_server == -1 if we have no data S32 descendents_server = cat->getDescendentCount(); S32 descendents_actual = cat->getViewerDescendentCount(); if (descendents_server == descendents_actual || (descendents_actual > 0 && descendents_server == -1)) // content was loaded in previous session { return true; } } return false; } //virtual void LLFolderViewModelItemInventory::addChild(LLFolderViewModelItem* child) { LLFolderViewModelItemInventory* model_child = static_cast(child); mLastAddedChildCreationDate = model_child->getCreationDate(); // this will requestSort() LLFolderViewModelItemCommon::addChild(child); } void LLFolderViewModelItemInventory::requestSort() { LLFolderViewModelItemCommon::requestSort(); LLFolderViewFolder* folderp = dynamic_cast(mFolderViewItem); if (folderp) { folderp->requestArrange(); } LLInventorySort sorter = static_cast(mRootViewModel).getSorter(); // Sort by date potentially affects parent folders which use a date // derived from newest item in them if (sorter.isByDate() && mParent) { // If this is an item, parent needs to be resorted // This case shouldn't happen, unless someone calls item->requestSort() if (!folderp) { mParent->requestSort(); } // if this is a folder, check sort rules for folder first else if (sorter.isFoldersByDate()) { if (mLastAddedChildCreationDate == -1 // nothing was added, some other reason for resort || mLastAddedChildCreationDate > getCreationDate()) // newer child { LLFolderViewModelItemInventory* model_parent = static_cast(mParent); model_parent->mLastAddedChildCreationDate = mLastAddedChildCreationDate; mParent->requestSort(); } } } mLastAddedChildCreationDate = -1; } void LLFolderViewModelItemInventory::setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset, std::string::size_type string_size) { bool generation_skip = mMarkedDirtyGeneration >= 0 && mPrevPassedAllFilters && mMarkedDirtyGeneration < mRootViewModel.getFilter().getFirstSuccessGeneration(); S32 last_generation = mLastFilterGeneration; LLFolderViewModelItemCommon::setPassedFilter(passed, filter_generation, string_offset, string_size); bool before = mPrevPassedAllFilters; mPrevPassedAllFilters = passedFilter(filter_generation); if (before != mPrevPassedAllFilters // Change of state || generation_skip // Was marked dirty // Potential change from being in-progress and invisible to visible) || (mPrevPassedAllFilters && last_generation < mRootViewModel.getFilter().getFirstRequiredGeneration())) { // Need to rearrange the folder if the filtered state of the item changed, // previously passed item skipped filter generation changes while being dirty // or previously passed not yet filtered item was marked dirty LLFolderViewFolder* parent_folder = mFolderViewItem->getParentFolder(); if (parent_folder) { parent_folder->requestArrange(); } } } bool LLFolderViewModelItemInventory::filterChildItem( LLFolderViewModelItem* item, LLFolderViewFilter& filter ) { S32 filter_generation = filter.getCurrentGeneration(); bool continue_filtering = true; if (item) { if (item->getLastFilterGeneration() < filter_generation) { // Recursive application of the filter for child items (CHUI-849) continue_filtering = item->filter(filter); } // Update latest generation to pass filter in parent and propagate up to root if (item->passedFilter()) { LLFolderViewModelItemInventory* view_model = this; while (view_model && view_model->mMostFilteredDescendantGeneration < filter_generation) { view_model->mMostFilteredDescendantGeneration = filter_generation; view_model = static_cast(view_model->mParent); } } } return continue_filtering; } bool LLFolderViewModelItemInventory::filter( LLFolderViewFilter& filter) { const S32 filter_generation = filter.getCurrentGeneration(); const S32 must_pass_generation = filter.getFirstRequiredGeneration(); if (getLastFilterGeneration() >= must_pass_generation && getLastFolderFilterGeneration() >= must_pass_generation && !passedFilter(must_pass_generation)) { // failed to pass an earlier filter that was a subset of the current one // go ahead and flag this item as not pass setPassedFilter(false, filter_generation); setPassedFolderFilter(false, filter_generation); return true; } // *TODO : Revise the logic for fast pass on less restrictive filter case /* const S32 sufficient_pass_generation = filter.getFirstSuccessGeneration(); if (getLastFilterGeneration() >= sufficient_pass_generation && getLastFolderFilterGeneration() >= sufficient_pass_generation && passedFilter(sufficient_pass_generation)) { // passed an earlier filter that was a superset of the current one // go ahead and flag this item as pass setPassedFilter(true, filter_generation); setPassedFolderFilter(true, filter_generation); return true; } */ bool is_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY); const bool passed_filter_folder = is_folder ? filter.checkFolder(this) : true; setPassedFolderFilter(passed_filter_folder, filter_generation); bool continue_filtering = true; if (!mChildren.empty() && (getLastFilterGeneration() < must_pass_generation // haven't checked descendants against minimum required generation to pass || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement { // now query children for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); iter != end_iter; ++iter) { continue_filtering = filterChildItem((*iter), filter); if (!continue_filtering) { break; } } } // If we didn't use all the filter time that means we filtered all of our descendants so we can filter ourselves now if (continue_filtering) { // This is where filter check on the item done (CHUI-849) const bool passed_filter = filter.check(this); if (passed_filter && mChildren.empty() && is_folder) // Update the latest filter generation for empty folders { LLFolderViewModelItemInventory* view_model = this; while (view_model && view_model->mMostFilteredDescendantGeneration < filter_generation) { view_model->mMostFilteredDescendantGeneration = filter_generation; view_model = static_cast(view_model->mParent); } } setPassedFilter(passed_filter, filter_generation, filter.getStringMatchOffset(this), filter.getFilterStringSize()); continue_filtering = !filter.isTimedOut(); } return continue_filtering; } //LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() //{ // return &mInventoryViewModel; //} // // //const LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() const //{ // return &mInventoryViewModel; //} bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a, const LLFolderViewModelItemInventory* const& b) const { // Ignore sort order for landmarks in the Favorites folder. // In that folder, landmarks should be always sorted as in the Favorites bar. See EXT-719 if (a->getSortGroup() == SG_ITEM && b->getSortGroup() == SG_ITEM && a->getInventoryType() == LLInventoryType::IT_LANDMARK && b->getInventoryType() == LLInventoryType::IT_LANDMARK) { static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); // If both landmarks are in the Favorites folder... if (gInventory.isObjectDescendentOf(a->getUUID(), favorites_folder_id) && gInventory.isObjectDescendentOf(b->getUUID(), favorites_folder_id)) { // Get their index in that folder S32 a_sort = LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()); S32 b_sort = LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); // Note: this test is a bit overkill: since they are both in the Favorites folder, we shouldn't get negative index values... if (!((a_sort < 0) && (b_sort < 0))) { return a_sort < b_sort; } } } // We sort by name if we aren't sorting by date // OR if these are folders and we are sorting folders by name. bool by_name = ((!mByDate || (mFoldersByName && (a->getSortGroup() != SG_ITEM))) && !mFoldersByWeight); if (a->getSortGroup() != b->getSortGroup()) { if (mSystemToTop) { // Group order is System Folders, Trash, Normal Folders, Items return (a->getSortGroup() < b->getSortGroup()); } else if (mByDate) { // Trash needs to go to the bottom if we are sorting by date if ( (a->getSortGroup() == SG_TRASH_FOLDER) || (b->getSortGroup() == SG_TRASH_FOLDER)) { return (b->getSortGroup() == SG_TRASH_FOLDER); } } } if (by_name) { S32 compare = LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()); if (0 == compare) { return (a->getCreationDate() > b->getCreationDate()); } else { return (compare < 0); } } else if (mFoldersByWeight) { S32 weight_a = compute_stock_count(a->getUUID()); S32 weight_b = compute_stock_count(b->getUUID()); if (weight_a == weight_b) { // Equal weight -> use alphabetical order return (LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()) < 0); } else if (weight_a == COMPUTE_STOCK_INFINITE) { // No stock -> move a at the end of the list return false; } else if (weight_b == COMPUTE_STOCK_INFINITE) { // No stock -> move b at the end of the list return true; } else { // Lighter is first (sorted in increasing order of weight) return (weight_a < weight_b); } } else { time_t first_create = a->getCreationDate(); time_t second_create = b->getCreationDate(); if (first_create == second_create) { return (LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()) < 0); } else { return (first_create > second_create); } } } LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) : LLFolderViewModelItemCommon(root_view_model), mPrevPassedAllFilters(false), mLastAddedChildCreationDate(-1) { }