From d0d05ddddfe6736b50e589a20d4d6ea457ad9b32 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 22 Sep 2023 23:38:20 +0300 Subject: SL-15039 Prune unused preferences #4 --- indra/newview/llinventorybridge.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index db347f7096..cb009ef1ee 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -94,6 +94,7 @@ typedef std::pair two_uuids_t; typedef std::list two_uuids_list_t; const F32 SOUND_GAIN = 1.0f; +const F32 FOLDER_LOADING_MESSAGE_DELAY = 0.5f; // Seconds to wait before showing the LOADING... text in folder views struct LLMoveInv { @@ -2243,9 +2244,7 @@ void LLFolderBridge::buildDisplayName() const std::string LLFolderBridge::getLabelSuffix() const { - static LLCachedControl folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f); - - if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay()) + if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= FOLDER_LOADING_MESSAGE_DELAY) { return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); } @@ -4686,9 +4685,7 @@ LLUIImagePtr LLMarketplaceFolderBridge::getMarketplaceFolderIcon(BOOL is_open) c std::string LLMarketplaceFolderBridge::getLabelSuffix() const { - static LLCachedControl folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f); - - if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay()) + if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= FOLDER_LOADING_MESSAGE_DELAY) { return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); } -- cgit v1.2.3 From 136a993ef2fad587cb61d5aa2b142ac9119d2e42 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Wed, 11 Oct 2023 20:56:25 +0200 Subject: SL-20419 Receiving new objects or items, etc causes inventory to switch from recent to my inventory tab --- indra/newview/llinventorybridge.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 385a2569a5..661db13baa 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -1765,7 +1765,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) } else if ("show_in_main_panel" == action) { - LLInventoryPanel::openInventoryPanelAndSetSelection(TRUE, mUUID, TRUE); + LLInventoryPanel::openInventoryPanelAndSetSelection(true, mUUID, true); return; } else if ("cut" == action) @@ -3396,7 +3396,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) } else if ("show_in_main_panel" == action) { - LLInventoryPanel::openInventoryPanelAndSetSelection(TRUE, mUUID, TRUE); + LLInventoryPanel::openInventoryPanelAndSetSelection(true, mUUID, true); return; } else if ("cut" == action) -- cgit v1.2.3 From 19ac7d18e811748c9f9c756a7830429714bc8ca2 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Tue, 17 Oct 2023 17:33:36 +0300 Subject: SL-20465 allow creating new items from the right-click context menu --- indra/newview/llinventorybridge.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 661db13baa..c41a4c5686 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -766,7 +766,7 @@ void hide_context_entries(LLMenuGL& menu, // descend into split menus: LLMenuItemBranchGL* branchp = dynamic_cast(menu_item); - if ((name == "More") && branchp) + if (((name == "More") || (name == "create_new")) && branchp) { hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries); } @@ -821,7 +821,7 @@ void hide_context_entries(LLMenuGL& menu, // so that some other UI element from multi-select doesn't later set this invisible. menu_item->pushVisible(TRUE); - bool enabled = (menu_item->getEnabled() == TRUE); + bool enabled = true; for (itor2 = disabled_entries.begin(); enabled && (itor2 != disabled_entries.end()); ++itor2) { enabled &= (*itor2 != name); @@ -4269,6 +4269,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items disabled_items.push_back(std::string("New Folder")); disabled_items.push_back(std::string("upload_def")); + disabled_items.push_back(std::string("create_new")); } if (favorites == mUUID) { @@ -4291,6 +4292,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items { disabled_items.push_back(std::string("New Folder")); disabled_items.push_back(std::string("upload_def")); + disabled_items.push_back(std::string("create_new")); } if (marketplace_listings_id == mUUID) { @@ -4352,6 +4354,17 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items if (!isMarketplaceListingsFolder()) { items.push_back(std::string("upload_def")); + items.push_back(std::string("create_new")); + items.push_back(std::string("New Script")); + items.push_back(std::string("New Note")); + items.push_back(std::string("New Gesture")); + items.push_back(std::string("New Clothes")); + items.push_back(std::string("New Body Parts")); + items.push_back(std::string("New Settings")); + if (!LLEnvironment::instance().isInventoryEnabled()) + { + disabled_items.push_back("New Settings"); + } } } getClipboardEntries(false, items, disabled_items, flags); -- cgit v1.2.3 From a62f5bfac1e55455c67e1c36eb388af99795dab6 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 14 Oct 2023 02:40:59 +0300 Subject: SL-20232 Allow deletion of worn items #1 --- indra/newview/llinventorybridge.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index c41a4c5686..4d7fc41ae3 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -309,9 +309,9 @@ void LLInvFVBridge::setCreationDate(time_t creation_date_utc) // Can be destroyed (or moved to trash) -BOOL LLInvFVBridge::isItemRemovable() const +BOOL LLInvFVBridge::isItemRemovable(bool check_worn) const { - return get_is_item_removable(getInventoryModel(), mUUID); + return get_is_item_removable(getInventoryModel(), mUUID, check_worn); } // Can be moved to another folder @@ -1063,7 +1063,7 @@ void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items, items.push_back(std::string("Delete")); - if (!isItemRemovable() || isPanelActive("Favorite Items")) + if (!isItemRemovable(false) || isPanelActive("Favorite Items")) { disabled_items.push_back(std::string("Delete")); } @@ -2405,20 +2405,21 @@ void LLFolderBridge::update() class LLIsItemRemovable : public LLFolderViewFunctor { public: - LLIsItemRemovable() : mPassed(TRUE) {} + LLIsItemRemovable(bool check_worn = true) : mPassed(TRUE), mCheckWorn(check_worn) {} virtual void doFolder(LLFolderViewFolder* folder) { - mPassed &= folder->getViewModelItem()->isItemRemovable(); + mPassed &= folder->getViewModelItem()->isItemRemovable(mCheckWorn); } virtual void doItem(LLFolderViewItem* item) { - mPassed &= item->getViewModelItem()->isItemRemovable(); + mPassed &= item->getViewModelItem()->isItemRemovable(mCheckWorn); } BOOL mPassed; + bool mCheckWorn; }; // Can be destroyed (or moved to trash) -BOOL LLFolderBridge::isItemRemovable() const +BOOL LLFolderBridge::isItemRemovable(bool check_worn) const { if (!get_is_category_removable(getInventoryModel(), mUUID)) { -- cgit v1.2.3 From ce83f77e7a5f32ac00b0a563fcf68ff98957f684 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 17 Oct 2023 00:34:00 +0300 Subject: SL-20232 Allow deletion of folders with worn content in tree view #3 --- indra/newview/llinventorybridge.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 4d7fc41ae3..02ca331bff 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2430,7 +2430,7 @@ BOOL LLFolderBridge::isItemRemovable(bool check_worn) const LLFolderViewFolder* folderp = dynamic_cast(panel ? panel->getItemByID(mUUID) : NULL); if (folderp) { - LLIsItemRemovable folder_test; + LLIsItemRemovable folder_test(check_worn); folderp->applyFunctorToChildren(folder_test); if (!folder_test.mPassed) { @@ -6626,6 +6626,26 @@ LLInventoryObject* LLObjectBridge::getObject() const return object; } +LLViewerInventoryItem* LLObjectBridge::getItem() const +{ + LLInventoryModel* model = getInventoryModel(); + if (model) + { + return model->getItem(mUUID); + } + return NULL; +} + +LLViewerInventoryCategory* LLObjectBridge::getCategory() const +{ + LLInventoryModel* model = getInventoryModel(); + if (model) + { + return model->getCategory(mUUID); + } + return NULL; +} + // virtual void LLObjectBridge::performAction(LLInventoryModel* model, std::string action) { -- cgit v1.2.3 From e233e4fb50e513b58f4452086182b60b25440fac Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 17 Oct 2023 01:35:47 +0300 Subject: SL-20232 Allow deletion of worn items #5 --- indra/newview/llinventorybridge.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 02ca331bff..a12c4f7f76 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4504,7 +4504,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& return; } - if (!isItemRemovable()) + if (!isItemRemovable(false)) { disabled_items.push_back(std::string("Delete")); } -- cgit v1.2.3 From aff01e2fd4fb5475eb42ce58d1ad997341cb7a94 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 10 Nov 2023 23:36:33 +0200 Subject: SL-20575 Fix some deletion issues - Fixes 'cut' checks for gallery - Checking if large folder is deletable should be faster now - For large flat root folders eliminated delay of looking for cof Might be a good idea to start cashing isItemRemovable if category version hasn't changed. --- indra/newview/llinventorybridge.cpp | 42 ++++++------------------------------- 1 file changed, 6 insertions(+), 36 deletions(-) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index a12c4f7f76..4fb60b4c55 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2399,49 +2399,19 @@ void LLFolderBridge::update() } } - -// Iterate through a folder's children to determine if -// all the children are removable. -class LLIsItemRemovable : public LLFolderViewFunctor -{ -public: - LLIsItemRemovable(bool check_worn = true) : mPassed(TRUE), mCheckWorn(check_worn) {} - virtual void doFolder(LLFolderViewFolder* folder) - { - mPassed &= folder->getViewModelItem()->isItemRemovable(mCheckWorn); - } - virtual void doItem(LLFolderViewItem* item) - { - mPassed &= item->getViewModelItem()->isItemRemovable(mCheckWorn); - } - BOOL mPassed; - bool mCheckWorn; -}; - // Can be destroyed (or moved to trash) BOOL LLFolderBridge::isItemRemovable(bool check_worn) const { - if (!get_is_category_removable(getInventoryModel(), mUUID)) + if (!get_is_category_and_children_removable(getInventoryModel(), mUUID, check_worn)) { return FALSE; } - LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewFolder* folderp = dynamic_cast(panel ? panel->getItemByID(mUUID) : NULL); - if (folderp) - { - LLIsItemRemovable folder_test(check_worn); - folderp->applyFunctorToChildren(folder_test); - if (!folder_test.mPassed) - { - return FALSE; - } - } - - if (isMarketplaceListingsFolder() && (!LLMarketplaceData::instance().isSLMDataFetched() || LLMarketplaceData::instance().getActivationState(mUUID))) - { - return FALSE; - } + if (isMarketplaceListingsFolder() + && (!LLMarketplaceData::instance().isSLMDataFetched() || LLMarketplaceData::instance().getActivationState(mUUID))) + { + return FALSE; + } return TRUE; } -- cgit v1.2.3 From 365792422612f391d175e21c7162d536d4a07647 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Tue, 14 Nov 2023 13:41:29 +0200 Subject: SL-20599 restore "New folder" option for "Marketplace Listings" floater --- indra/newview/llinventorybridge.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 4fb60b4c55..5b2a1366bd 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4336,6 +4336,10 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items { disabled_items.push_back("New Settings"); } + } + else + { + items.push_back(std::string("New Listing Folder")); } } getClipboardEntries(false, items, disabled_items, flags); -- cgit v1.2.3 From fda96e36806dab544709ac87156949c3accc5cf8 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 15 Nov 2023 22:15:03 +0200 Subject: SL-20575 When opening menu cache results and spread load over frames --- indra/newview/llinventorybridge.cpp | 228 ++++++++++++++++++++++++++++++++++-- 1 file changed, 221 insertions(+), 7 deletions(-) (limited to 'indra/newview/llinventorybridge.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 5b2a1366bd..acdbeead9d 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -773,9 +773,6 @@ void hide_context_entries(LLMenuGL& menu, bool found = false; - std::string myinput; - std::vector mylist{ "a", "b", "c" }; - menuentry_vec_t::const_iterator itor2 = std::find(entries_to_show.begin(), entries_to_show.end(), name); if (itor2 != entries_to_show.end()) { @@ -872,7 +869,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, } items.push_back(std::string("Cut")); - if (!isItemMovable() || !isItemRemovable()) + if (!isItemMovable() || !canMenuCut()) { disabled_items.push_back(std::string("Cut")); } @@ -921,7 +918,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, if(!single_folder_root) { items.push_back(std::string("Cut")); - if (!isItemMovable() || !isItemRemovable()) + if (!isItemMovable() || !canMenuCut()) { disabled_items.push_back(std::string("Cut")); } @@ -1063,7 +1060,7 @@ void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items, items.push_back(std::string("Delete")); - if (!isItemRemovable(false) || isPanelActive("Favorite Items")) + if (isPanelActive("Favorite Items") || !canMenuDelete()) { disabled_items.push_back(std::string("Delete")); } @@ -1219,6 +1216,16 @@ void LLInvFVBridge::addLinkReplaceMenuOption(menuentry_vec_t& items, menuentry_v } } +bool LLInvFVBridge::canMenuDelete() +{ + return isItemRemovable(false); +} + +bool LLInvFVBridge::canMenuCut() +{ + return isItemRemovable(true); +} + // *TODO: remove this BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const { @@ -4478,7 +4485,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& return; } - if (!isItemRemovable(false)) + if (!canMenuDelete()) { disabled_items.push_back(std::string("Delete")); } @@ -4848,6 +4855,192 @@ void LLFolderBridge::modifyOutfit(BOOL append) } } +//static +void LLFolderBridge::onCanDeleteIdle(void* user_data) +{ + LLFolderBridge* self = (LLFolderBridge*)user_data; + + // we really need proper onidle mechanics that returns available time + const F32 EXPIRY_SECONDS = 0.008f; + LLTimer timer; + timer.setTimerExpirySec(EXPIRY_SECONDS); + + LLInventoryModel* model = self->getInventoryModel(); + if (model) + { + switch (self->mCanDeleteFolderState) + { + case CDS_INIT_FOLDER_CHECK: + // Can still be expensive, split it further? + model->collectDescendents( + self->mUUID, + self->mFoldersToCheck, + self->mItemsToCheck, + LLInventoryModel::EXCLUDE_TRASH); + self->mCanDeleteFolderState = CDS_PROCESSING_ITEMS; + break; + + case CDS_PROCESSING_ITEMS: + while (!timer.hasExpired() && !self->mItemsToCheck.empty()) + { + LLViewerInventoryItem* item = self->mItemsToCheck.back().get(); + if (item) + { + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(item)) + { + if (get_is_item_worn(item)) + { + // At the moment we disable 'cut' if category has worn items (do we need to?) + // but allow 'delete' to happen since it will prompt user to detach + self->mCanCut = false; + } + } + + if (!item->getIsLinkType() && get_is_item_worn(item)) + { + self->mCanCut = false; + } + } + self->mItemsToCheck.pop_back(); + } + self->mCanDeleteFolderState = CDS_PROCESSING_FOLDERS; + break; + case CDS_PROCESSING_FOLDERS: + { + const LLViewerInventoryItem* base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); + LLViewerInventoryCategory* outfit_linked_category = base_outfit_link ? base_outfit_link->getLinkedCategory() : nullptr; + + while (!timer.hasExpired() && !self->mFoldersToCheck.empty()) + { + LLViewerInventoryCategory* cat = self->mFoldersToCheck.back().get(); + if (cat) + { + const LLFolderType::EType folder_type = cat->getPreferredType(); + if (LLFolderType::lookupIsProtectedType(folder_type)) + { + self->mCanCut = false; + self->mCanDelete = false; + self->completeDeleteProcessing(); + break; + } + + // Can't delete the outfit that is currently being worn. + if (folder_type == LLFolderType::FT_OUTFIT) + { + if (cat == outfit_linked_category) + { + self->mCanCut = false; + self->mCanDelete = false; + self->completeDeleteProcessing(); + break; + } + } + } + self->mFoldersToCheck.pop_back(); + } + } + self->mCanDeleteFolderState = CDS_DONE; + break; + case CDS_DONE: + self->completeDeleteProcessing(); + break; + } + } +} + +bool LLFolderBridge::canMenuDelete() +{ + LLInventoryModel* model = getInventoryModel(); + if (!model) return false; + LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); + if (!category) + { + return false; + } + + S32 version = category->getVersion(); + if (mLastCheckedVersion == version) + { + return mCanDelete; + } + + initCanDeleteProcessing(model, version); + return false; +} + +bool LLFolderBridge::canMenuCut() +{ + LLInventoryModel* model = getInventoryModel(); + if (!model) return false; + LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); + if (!category) + { + return false; + } + + S32 version = category->getVersion(); + if (mLastCheckedVersion == version) + { + return mCanCut; + } + + initCanDeleteProcessing(model, version); + return false; +} + +void LLFolderBridge::initCanDeleteProcessing(LLInventoryModel* model, S32 version) +{ + if (mCanDeleteFolderState == CDS_DONE + || mInProgressVersion != version) + { + if (get_is_category_removable(model, mUUID)) + { + // init recursive check of content + mInProgressVersion = version; + mCanCut = true; + mCanDelete = true; + mCanDeleteFolderState = CDS_INIT_FOLDER_CHECK; + mFoldersToCheck.clear(); + mItemsToCheck.clear(); + gIdleCallbacks.addFunction(onCanDeleteIdle, this); + } + else + { + // no check needed + mCanDelete = false; + mCanCut = false; + mLastCheckedVersion = version; + mCanDeleteFolderState = CDS_DONE; + mFoldersToCheck.clear(); + mItemsToCheck.clear(); + } + } +} + +void LLFolderBridge::completeDeleteProcessing() +{ + LLInventoryModel* model = getInventoryModel(); + LLViewerInventoryCategory* category = model ? (LLViewerInventoryCategory*)model->getCategory(mUUID) : nullptr; + if (model && category && category->getVersion() == mInProgressVersion) + { + mLastCheckedVersion = mInProgressVersion; + mCanDeleteFolderState = CDS_DONE; + gIdleCallbacks.deleteFunction(onCanDeleteIdle, this); + } + else + { + mCanDelete = false; + mCanCut = false; + mLastCheckedVersion = LLViewerInventoryCategory::VERSION_UNKNOWN; + mCanDeleteFolderState = CDS_DONE; + } + + if (mRoot) + { + mRoot->updateMenu(); + } +} + // +=================================================+ // | LLMarketplaceFolderBridge | @@ -5009,6 +5202,27 @@ void drop_to_favorites_cb(const LLUUID& id, LLPointer cb1, cb2->fire(id); } +LLFolderBridge::LLFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) + : LLInvFVBridge(inventory, root, uuid) + , mCallingCards(FALSE) + , mWearables(FALSE) + , mIsLoading(false) + , mShowDescendantsCount(false) + , mCanDeleteFolderState(CDS_DONE) + , mLastCheckedVersion(S32_MIN) + , mInProgressVersion(S32_MIN) + , mCanDelete(false) + , mCanCut(false) +{ +} + +LLFolderBridge::~LLFolderBridge() +{ + gIdleCallbacks.deleteFunction(onCanDeleteIdle, this); +} + void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item, LLPointer cb) { // use callback to rearrange favorite landmarks after adding -- cgit v1.2.3