diff options
Diffstat (limited to 'indra/newview/llpanelteleporthistory.cpp')
-rw-r--r-- | indra/newview/llpanelteleporthistory.cpp | 1071 |
1 files changed, 979 insertions, 92 deletions
diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 22e8ada2ea..9b8167b15a 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -2,30 +2,25 @@ * @file llpanelteleporthistory.cpp * @brief Teleport history represented by a scrolling list * - * $LicenseInfo:firstyear=2009&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2009&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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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$ */ @@ -37,65 +32,464 @@ #include "llpanelteleporthistory.h" #include "llsidetray.h" #include "llworldmap.h" +#include "llteleporthistorystorage.h" +#include "lltextutil.h" + +#include "llaccordionctrl.h" +#include "llaccordionctrltab.h" +#include "llflatlistview.h" +#include "llnotificationsutil.h" +#include "lltextbox.h" +#include "llviewermenu.h" +#include "lllandmarkactions.h" +#include "llclipboard.h" + +// Maximum number of items that can be added to a list in one pass. +// Used to limit time spent for items list update per frame. +static const U32 ADD_LIMIT = 50; + +static const std::string COLLAPSED_BY_USER = "collapsed_by_user"; + +class LLTeleportHistoryFlatItem : public LLPanel +{ +public: + LLTeleportHistoryFlatItem(S32 index, LLTeleportHistoryPanel::ContextMenu *context_menu, const std::string ®ion_name, const std::string &hl); + virtual ~LLTeleportHistoryFlatItem(); + + virtual BOOL postBuild(); + + /*virtual*/ S32 notify(const LLSD& info); + + S32 getIndex() { return mIndex; } + void setIndex(S32 index) { mIndex = index; } + const std::string& getRegionName() { return mRegionName;} + void setRegionName(const std::string& name); + void setHighlightedText(const std::string& text); + void updateTitle(); + + /*virtual*/ void setValue(const LLSD& value); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + + static void showPlaceInfoPanel(S32 index); + + LLHandle<LLTeleportHistoryFlatItem> getItemHandle() { mItemHandle.bind(this); return mItemHandle; } + +private: + void onProfileBtnClick(); + + LLButton* mProfileBtn; + LLTextBox* mTitle; + + LLTeleportHistoryPanel::ContextMenu *mContextMenu; + + S32 mIndex; + std::string mRegionName; + std::string mHighlight; + LLRootHandle<LLTeleportHistoryFlatItem> mItemHandle; +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class LLTeleportHistoryFlatItemStorage: public LLSingleton<LLTeleportHistoryFlatItemStorage> { +protected: + typedef std::vector< LLHandle<LLTeleportHistoryFlatItem> > flat_item_list_t; + +public: + LLTeleportHistoryFlatItem* getFlatItemForPersistentItem ( + LLTeleportHistoryPanel::ContextMenu *context_menu, + const LLTeleportHistoryPersistentItem& persistent_item, + const S32 cur_item_index, + const std::string &hl); + + void removeItem(LLTeleportHistoryFlatItem* item); + + void purge(); + +private: + + flat_item_list_t mItems; +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +LLTeleportHistoryFlatItem::LLTeleportHistoryFlatItem(S32 index, LLTeleportHistoryPanel::ContextMenu *context_menu, const std::string ®ion_name, const std::string &hl) +: LLPanel(), + mIndex(index), + mContextMenu(context_menu), + mRegionName(region_name), + mHighlight(hl) +{ + buildFromFile( "panel_teleport_history_item.xml"); +} + +LLTeleportHistoryFlatItem::~LLTeleportHistoryFlatItem() +{ +} + +//virtual +BOOL LLTeleportHistoryFlatItem::postBuild() +{ + mTitle = getChild<LLTextBox>("region"); + + mProfileBtn = getChild<LLButton>("profile_btn"); + + mProfileBtn->setClickedCallback(boost::bind(&LLTeleportHistoryFlatItem::onProfileBtnClick, this)); + + updateTitle(); + + return true; +} + +S32 LLTeleportHistoryFlatItem::notify(const LLSD& info) +{ + if(info.has("detach")) + { + delete mMouseDownSignal; + mMouseDownSignal = NULL; + delete mRightMouseDownSignal; + mRightMouseDownSignal = NULL; + return 1; + } + return 0; +} + +void LLTeleportHistoryFlatItem::setValue(const LLSD& value) +{ + if (!value.isMap()) return;; + if (!value.has("selected")) return; + getChildView("selected_icon")->setVisible( value["selected"]); +} + +void LLTeleportHistoryFlatItem::setHighlightedText(const std::string& text) +{ + mHighlight = text; +} + +void LLTeleportHistoryFlatItem::setRegionName(const std::string& name) +{ + mRegionName = name; +} + +void LLTeleportHistoryFlatItem::updateTitle() +{ + LLTextUtil::textboxSetHighlightedVal( + mTitle, + LLStyle::Params(), + mRegionName, + mHighlight); +} + +void LLTeleportHistoryFlatItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible( true); + mProfileBtn->setVisible(true); + + LLPanel::onMouseEnter(x, y, mask); +} + +void LLTeleportHistoryFlatItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible( false); + mProfileBtn->setVisible(false); + + LLPanel::onMouseLeave(x, y, mask); +} + +// virtual +BOOL LLTeleportHistoryFlatItem::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (mContextMenu) + mContextMenu->show(this, mIndex, x, y); + + return LLPanel::handleRightMouseDown(x, y, mask); +} + +void LLTeleportHistoryFlatItem::showPlaceInfoPanel(S32 index) +{ + LLSD params; + params["id"] = index; + params["type"] = "teleport_history"; + + LLSideTray::getInstance()->showPanel("panel_places", params); +} + +void LLTeleportHistoryFlatItem::onProfileBtnClick() +{ + LLTeleportHistoryFlatItem::showPlaceInfoPanel(mIndex); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +LLTeleportHistoryFlatItem* +LLTeleportHistoryFlatItemStorage::getFlatItemForPersistentItem ( + LLTeleportHistoryPanel::ContextMenu *context_menu, + const LLTeleportHistoryPersistentItem& persistent_item, + const S32 cur_item_index, + const std::string &hl) +{ + LLTeleportHistoryFlatItem* item = NULL; + if ( cur_item_index < (S32) mItems.size() ) + { + item = mItems[cur_item_index].get(); + if (item->getParent() == NULL) + { + item->setIndex(cur_item_index); + item->setRegionName(persistent_item.mTitle); + item->setHighlightedText(hl); + item->setVisible(TRUE); + item->updateTitle(); + } + else + { + // Item already added to parent + item = NULL; + } + } + + if ( !item ) + { + item = new LLTeleportHistoryFlatItem(cur_item_index, + context_menu, + persistent_item.mTitle, + hl); + mItems.push_back(item->getItemHandle()); + } + + return item; +} + +void LLTeleportHistoryFlatItemStorage::removeItem(LLTeleportHistoryFlatItem* item) +{ + if (item) + { + flat_item_list_t::iterator item_iter = std::find(mItems.begin(), + mItems.end(), + item->getItemHandle()); + if (item_iter != mItems.end()) + { + mItems.erase(item_iter); + } + } +} + +void LLTeleportHistoryFlatItemStorage::purge() +{ + for ( flat_item_list_t::iterator + it = mItems.begin(), + it_end = mItems.end(); + it != it_end; ++it ) + { + LLHandle <LLTeleportHistoryFlatItem> item_handle = *it; + if ( !item_handle.isDead() && item_handle.get()->getParent() == NULL ) + { + item_handle.get()->die(); + } + } + mItems.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +LLTeleportHistoryPanel::ContextMenu::ContextMenu() : + mMenu(NULL), mIndex(0) +{ +} + +void LLTeleportHistoryPanel::ContextMenu::show(LLView* spawning_view, S32 index, S32 x, S32 y) +{ + if (mMenu) + { + //preventing parent (menu holder) from deleting already "dead" context menus on exit + LLView* parent = mMenu->getParent(); + if (parent) + { + parent->removeChild(mMenu); + } + delete mMenu; + } + + mIndex = index; + mMenu = createMenu(); + + mMenu->show(x, y); + LLMenuGL::showPopup(spawning_view, mMenu, x, y); +} + +LLContextMenu* LLTeleportHistoryPanel::ContextMenu::createMenu() +{ + // set up the callbacks for all of the avatar menu items + // (N.B. callbacks don't take const refs as mID is local scope) + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + + registrar.add("TeleportHistory.Teleport", boost::bind(&LLTeleportHistoryPanel::ContextMenu::onTeleport, this)); + registrar.add("TeleportHistory.MoreInformation",boost::bind(&LLTeleportHistoryPanel::ContextMenu::onInfo, this)); + registrar.add("TeleportHistory.CopyToClipboard",boost::bind(&LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard, this)); + + // create the context menu from the XUI + return LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>( + "menu_teleport_history_item.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); +} + +void LLTeleportHistoryPanel::ContextMenu::onTeleport() +{ + confirmTeleport(mIndex); +} + +void LLTeleportHistoryPanel::ContextMenu::onInfo() +{ + LLTeleportHistoryFlatItem::showPlaceInfoPanel(mIndex); +} + +//static +void LLTeleportHistoryPanel::ContextMenu::gotSLURLCallback(const std::string& slurl) +{ + gClipboard.copyFromString(utf8str_to_wstring(slurl)); +} + +void LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard() +{ + LLVector3d globalPos = LLTeleportHistoryStorage::getInstance()->getItems()[mIndex].mGlobalPos; + LLLandmarkActions::getSLURLfromPosGlobal(globalPos, + boost::bind(&LLTeleportHistoryPanel::ContextMenu::gotSLURLCallback, _1)); +} // Not yet implemented; need to remove buildPanel() from constructor when we switch //static LLRegisterPanelClassWrapper<LLTeleportHistoryPanel> t_teleport_history("panel_teleport_history"); LLTeleportHistoryPanel::LLTeleportHistoryPanel() : LLPanelPlacesTab(), - mFilterSubString(LLStringUtil::null), + mDirty(true), + mCurrentItem(0), mTeleportHistory(NULL), - mHistoryItems(NULL) + mHistoryAccordion(NULL), + mAccordionTabMenu(NULL), + mLastSelectedFlatlList(NULL), + mLastSelectedItemIndex(-1) { - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_teleport_history.xml"); + buildFromFile( "panel_teleport_history.xml"); } LLTeleportHistoryPanel::~LLTeleportHistoryPanel() { + LLTeleportHistoryFlatItemStorage::instance().purge(); + LLView::deleteViewByHandle(mGearMenuHandle); } BOOL LLTeleportHistoryPanel::postBuild() { - mTeleportHistory = LLTeleportHistory::getInstance(); + mTeleportHistory = LLTeleportHistoryStorage::getInstance(); if (mTeleportHistory) { - mTeleportHistory->setHistoryChangedCallback(boost::bind(&LLTeleportHistoryPanel::showTeleportHistory, this)); + mTeleportHistory->setHistoryChangedCallback(boost::bind(&LLTeleportHistoryPanel::onTeleportHistoryChange, this, _1)); } - mHistoryItems = getChild<LLScrollListCtrl>("history_items"); - if (mHistoryItems) + mHistoryAccordion = getChild<LLAccordionCtrl>("history_accordion"); + + if (mHistoryAccordion) { - mHistoryItems->setDoubleClickCallback(onDoubleClickItem, this); - mHistoryItems->setCommitOnSelectionChange(FALSE); - mHistoryItems->setCommitCallback(boost::bind(&LLTeleportHistoryPanel::handleItemSelect, this, _2)); + for (child_list_const_iter_t iter = mHistoryAccordion->beginChild(); iter != mHistoryAccordion->endChild(); iter++) + { + if (dynamic_cast<LLAccordionCtrlTab*>(*iter)) + { + LLAccordionCtrlTab* tab = (LLAccordionCtrlTab*)*iter; + tab->setRightMouseDownCallback(boost::bind(&LLTeleportHistoryPanel::onAccordionTabRightClick, this, _1, _2, _3, _4)); + tab->setDisplayChildren(false); + tab->setDropDownStateChangedCallback(boost::bind(&LLTeleportHistoryPanel::onAccordionExpand, this, _1, _2)); + + // All accordion tabs are collapsed initially + setAccordionCollapsedByUser(tab, true); + + mItemContainers.put(tab); + + LLFlatListView* fl = getFlatListViewFromTab(tab); + if (fl) + { + fl->setCommitOnSelectionChange(true); + fl->setDoubleClickCallback(boost::bind(&LLTeleportHistoryPanel::onDoubleClickItem, this)); + fl->setCommitCallback(boost::bind(&LLTeleportHistoryPanel::handleItemSelect, this, fl)); + fl->setReturnCallback(boost::bind(&LLTeleportHistoryPanel::onReturnKeyPressed, this)); + } + } + } + + // Open first 2 accordion tabs + if (mItemContainers.size() > 1) + { + LLAccordionCtrlTab* tab = mItemContainers.get(mItemContainers.size() - 1); + tab->setDisplayChildren(true); + setAccordionCollapsedByUser(tab, false); + } + + if (mItemContainers.size() > 2) + { + LLAccordionCtrlTab* tab = mItemContainers.get(mItemContainers.size() - 2); + tab->setDisplayChildren(true); + setAccordionCollapsedByUser(tab, false); + } } + getChild<LLPanel>("bottom_panel")->childSetAction("gear_btn",boost::bind(&LLTeleportHistoryPanel::onGearButtonClicked, this)); + + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + + registrar.add("TeleportHistory.ExpandAllFolders", boost::bind(&LLTeleportHistoryPanel::onExpandAllFolders, this)); + registrar.add("TeleportHistory.CollapseAllFolders", boost::bind(&LLTeleportHistoryPanel::onCollapseAllFolders, this)); + registrar.add("TeleportHistory.ClearTeleportHistory", boost::bind(&LLTeleportHistoryPanel::onClearTeleportHistory, this)); + mEnableCallbackRegistrar.add("TeleportHistory.GearMenu.Enable", boost::bind(&LLTeleportHistoryPanel::isActionEnabled, this, _2)); + + LLMenuGL* gear_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_teleport_history_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(gear_menu) + mGearMenuHandle = gear_menu->getHandle(); + return TRUE; } // virtual +void LLTeleportHistoryPanel::draw() +{ + if (mDirty) + refresh(); + + LLPanelPlacesTab::draw(); +} + +// virtual void LLTeleportHistoryPanel::onSearchEdit(const std::string& string) { - if (mFilterSubString != string) - { - mFilterSubString = string; - showTeleportHistory(); - } + sFilterSubString = string; + showTeleportHistory(); +} + +// virtual +bool LLTeleportHistoryPanel::isSingleItemSelected() +{ + return mLastSelectedFlatlList && mLastSelectedFlatlList->getSelectedItem(); } // virtual void LLTeleportHistoryPanel::onShowOnMap() { - LLScrollListItem* itemp = mHistoryItems->getFirstSelected(); - if(!itemp) + if (!mLastSelectedFlatlList) return; - S32 index = itemp->getColumn(LIST_INDEX)->getValue().asInteger(); + LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem()); - const LLTeleportHistory::slurl_list_t& hist_items = mTeleportHistory->getItems(); + if(!itemp) + return; + + LLVector3d global_pos = mTeleportHistory->getItems()[itemp->getIndex()].mGlobalPos; - LLVector3d global_pos = hist_items[index].mGlobalPos; - if (!global_pos.isExactlyZero()) { LLFloaterWorldMap::getInstance()->trackLocation(global_pos); @@ -103,16 +497,32 @@ void LLTeleportHistoryPanel::onShowOnMap() } } +//virtual +void LLTeleportHistoryPanel::onShowProfile() +{ + if (!mLastSelectedFlatlList) + return; + + LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem()); + + if(!itemp) + return; + + LLTeleportHistoryFlatItem::showPlaceInfoPanel(itemp->getIndex()); +} + // virtual void LLTeleportHistoryPanel::onTeleport() { - LLScrollListItem* itemp = mHistoryItems->getFirstSelected(); + if (!mLastSelectedFlatlList) + return; + + LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem()); if(!itemp) return; - S32 index = itemp->getColumn(LIST_INDEX)->getValue().asInteger(); - - mTeleportHistory->goToItem(index); + // teleport to existing item in history, so we don't add it again + confirmTeleport(itemp->getIndex()); } /* @@ -131,7 +541,7 @@ void LLTeleportHistoryPanel::onCopySLURL() U64 new_region_handle = to_region_handle(global_pos); - LLWorldMap::url_callback_t cb = boost::bind( + LLWorldMapMessage::url_callback_t cb = boost::bind( &LLPanelPlacesTab::onRegionResponse, this, global_pos, _1, _2, _3, _4); @@ -139,80 +549,557 @@ void LLTeleportHistoryPanel::onCopySLURL() } */ -void LLTeleportHistoryPanel::showTeleportHistory() +// virtual +void LLTeleportHistoryPanel::updateVerbs() { - const LLTeleportHistory::slurl_list_t& hist_items = mTeleportHistory->getItems(); + if (!isTabVisible()) + return; + + if (!mLastSelectedFlatlList) + { + mTeleportBtn->setEnabled(false); + mShowProfile->setEnabled(false); + mShowOnMapBtn->setEnabled(false); + return; + } + + LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem()); + + mTeleportBtn->setEnabled(NULL != itemp); + mShowProfile->setEnabled(NULL != itemp); + mShowOnMapBtn->setEnabled(NULL != itemp); +} + +void LLTeleportHistoryPanel::getNextTab(const LLDate& item_date, S32& tab_idx, LLDate& tab_date) +{ + const U32 seconds_in_day = 24 * 60 * 60; - mHistoryItems->deleteAllItems(); + S32 tabs_cnt = mItemContainers.size(); + S32 curr_year = 0, curr_month = 0, curr_day = 0; - S32 cur_item = mTeleportHistory->getCurrentItemIndex(); + tab_date = LLDate::now(); + tab_date.split(&curr_year, &curr_month, &curr_day); + tab_date.fromYMDHMS(curr_year, curr_month, curr_day); // Set hour, min, and sec to 0 + tab_date.secondsSinceEpoch(tab_date.secondsSinceEpoch() + seconds_in_day); - for (LLTeleportHistory::slurl_list_t::const_iterator iter = hist_items.begin(); - iter != hist_items.end(); ++iter) + tab_idx = -1; + + while (tab_idx < tabs_cnt - 1 && item_date < tab_date) { - std::string landmark_title = (*iter).mTitle; - LLStringUtil::toUpper(landmark_title); + tab_idx++; - std::string::size_type match_offset = mFilterSubString.size() ? landmark_title.find(mFilterSubString) : std::string::npos; - bool passed = mFilterSubString.size() == 0 || match_offset != std::string::npos; - - if (!passed) + if (tab_idx <= tabs_cnt - 4) + { + // All tabs, except last three, are tabs for one day, so just push tab_date back by one day + tab_date.secondsSinceEpoch(tab_date.secondsSinceEpoch() - seconds_in_day); + } + else if (tab_idx == tabs_cnt - 3) // 6 day and older, low boundary is 1 month + { + tab_date = LLDate::now(); + tab_date.split(&curr_year, &curr_month, &curr_day); + curr_month--; + if (0 == curr_month) + { + curr_month = 12; + curr_year--; + } + tab_date.fromYMDHMS(curr_year, curr_month, curr_day); + } + else if (tab_idx == tabs_cnt - 2) // 1 month and older, low boundary is 6 months + { + tab_date = LLDate::now(); + tab_date.split(&curr_year, &curr_month, &curr_day); + if (curr_month > 6) + { + curr_month -= 6; + } + else + { + curr_month += 6; + curr_year--; + } + tab_date.fromYMDHMS(curr_year, curr_month, curr_day); + } + else // 6 months and older + { + tab_date.secondsSinceEpoch(0); + } + } +} + +// Called to add items, no more, than ADD_LIMIT at time +void LLTeleportHistoryPanel::refresh() +{ + if (!mHistoryAccordion) + { + mDirty = false; + return; + } + + const LLTeleportHistoryStorage::slurl_list_t& items = mTeleportHistory->getItems(); + + // Setting tab_boundary_date to "now", so date from any item would be earlier, than boundary. + // That leads to call to getNextTab to get right tab_idx in first pass + LLDate tab_boundary_date = LLDate::now(); + + LLFlatListView* curr_flat_view = NULL; + std::string filter_string = sFilterSubString; + LLStringUtil::toUpper(filter_string); + + U32 added_items = 0; + while (mCurrentItem >= 0) + { + // Filtering + if (!filter_string.empty()) + { + std::string landmark_title(items[mCurrentItem].mTitle); + LLStringUtil::toUpper(landmark_title); + if( std::string::npos == landmark_title.find(filter_string) ) + { + mCurrentItem--; + continue; + } + } + + // Checking whether date of item is earlier, than tab_boundary_date. + // In that case, item should be added to another tab + const LLDate &date = items[mCurrentItem].mDate; + + if (date < tab_boundary_date) + { + // Getting apropriate tab_idx for this and subsequent items, + // tab_boundary_date would be earliest possible date for this tab + S32 tab_idx = 0; + getNextTab(date, tab_idx, tab_boundary_date); + + LLAccordionCtrlTab* tab = mItemContainers.get(mItemContainers.size() - 1 - tab_idx); + tab->setVisible(true); + + // Expand all accordion tabs when filtering + if(!sFilterSubString.empty()) + { + //store accordion tab state when filter is not empty + tab->notifyChildren(LLSD().with("action","store_state")); + + tab->setDisplayChildren(true); + } + // Restore each tab's expand state when not filtering + else + { + bool collapsed = isAccordionCollapsedByUser(tab); + tab->setDisplayChildren(!collapsed); + + //restore accordion state after all those accodrion tabmanipulations + tab->notifyChildren(LLSD().with("action","restore_state")); + } + + curr_flat_view = getFlatListViewFromTab(tab); + } + + if (curr_flat_view) + { + LLTeleportHistoryFlatItem* item = + LLTeleportHistoryFlatItemStorage::instance() + .getFlatItemForPersistentItem(&mContextMenu, + items[mCurrentItem], + mCurrentItem, + filter_string); + if ( !curr_flat_view->addItem(item, LLUUID::null, ADD_BOTTOM, false) ) + llerrs << "Couldn't add flat item to teleport history." << llendl; + if (mLastSelectedItemIndex == mCurrentItem) + curr_flat_view->selectItem(item, true); + } + + mCurrentItem--; + + if (++added_items >= ADD_LIMIT) + break; + } + + for (S32 n = mItemContainers.size() - 1; n >= 0; --n) + { + LLAccordionCtrlTab* tab = mItemContainers.get(n); + LLFlatListView* fv = getFlatListViewFromTab(tab); + if (fv) + { + fv->notify(LLSD().with("rearrange", LLSD())); + } + } + + mHistoryAccordion->setFilterSubString(sFilterSubString); + + mHistoryAccordion->arrange(); + + updateVerbs(); + + if (mCurrentItem < 0) + mDirty = false; +} + +void LLTeleportHistoryPanel::onTeleportHistoryChange(S32 removed_index) +{ + mLastSelectedItemIndex = -1; + + if (-1 == removed_index) + showTeleportHistory(); // recreate all items + else + { + replaceItem(removed_index); // replace removed item by most recent + updateVerbs(); + } +} + +void LLTeleportHistoryPanel::replaceItem(S32 removed_index) +{ + // Flat list for 'Today' (mItemContainers keeps accordion tabs in reverse order) + LLFlatListView* fv = getFlatListViewFromTab(mItemContainers[mItemContainers.size() - 1]); + + // Empty flat list for 'Today' means that other flat lists are empty as well, + // so all items from teleport history should be added. + if (!fv || fv->size() == 0) + { + showTeleportHistory(); + return; + } + + const LLTeleportHistoryStorage::slurl_list_t& history_items = mTeleportHistory->getItems(); + LLTeleportHistoryFlatItem* item = LLTeleportHistoryFlatItemStorage::instance() + .getFlatItemForPersistentItem(&mContextMenu, + history_items[history_items.size() - 1], // Most recent item, it was added instead of removed + history_items.size(), // index will be decremented inside loop below + sFilterSubString); + + fv->addItem(item, LLUUID::null, ADD_TOP); + + // Index of each item, from last to removed item should be decremented + // to point to the right item in LLTeleportHistoryStorage + for (S32 tab_idx = mItemContainers.size() - 1; tab_idx >= 0; --tab_idx) + { + LLAccordionCtrlTab* tab = mItemContainers.get(tab_idx); + if (!tab->getVisible()) continue; - S32 index = iter - hist_items.begin(); + fv = getFlatListViewFromTab(tab); + if (!fv) + { + showTeleportHistory(); + return; + } + + std::vector<LLPanel*> items; + fv->getItems(items); + + S32 items_cnt = items.size(); + for (S32 n = 0; n < items_cnt; ++n) + { + LLTeleportHistoryFlatItem *item = (LLTeleportHistoryFlatItem*) items[n]; + + if (item->getIndex() == removed_index) + { + LLTeleportHistoryFlatItemStorage::instance().removeItem(item); + + fv->removeItem(item); + + // If flat list becames empty, then accordion tab should be hidden + if (fv->size() == 0) + tab->setVisible(false); - LLSD row; - row["id"] = index; + mHistoryAccordion->arrange(); - LLSD& icon_column = row["columns"][LIST_ICON]; - icon_column["column"] = "landmark_icon"; - icon_column["type"] = "icon"; - icon_column["value"] = "inv_item_landmark.tga"; + return; // No need to decrement idexes for the rest of items + } + + item->setIndex(item->getIndex() - 1); + } + } +} - LLSD& region_column = row["columns"][LIST_ITEM_TITLE]; - region_column["column"] = "region"; - region_column["type"] = "text"; - region_column["value"] = (*iter).mTitle; +void LLTeleportHistoryPanel::showTeleportHistory() +{ + mDirty = true; - LLSD& index_column = row["columns"][LIST_INDEX]; - index_column["column"] = "index"; - index_column["type"] = "text"; - index_column["value"] = index; + // Starting to add items from last one, in reverse order, + // since TeleportHistory keeps most recent item at the end + mCurrentItem = mTeleportHistory->getItems().size() - 1; - mHistoryItems->addElement(row); + for (S32 n = mItemContainers.size() - 1; n >= 0; --n) + { + LLAccordionCtrlTab* tab = mItemContainers.get(n); + tab->setVisible(false); - if (cur_item == index) + LLFlatListView* fv = getFlatListViewFromTab(tab); + if (fv) { - LLScrollListItem* itemp = mHistoryItems->getItem(index); - ((LLScrollListText*)itemp->getColumn(LIST_ITEM_TITLE))->setFontStyle(LLFontGL::BOLD); + // Detached panels are managed by LLTeleportHistoryFlatItemStorage + std::vector<LLPanel*> detached_items; + fv->detachItems(detached_items); } } +} + +void LLTeleportHistoryPanel::handleItemSelect(LLFlatListView* selected) +{ + mLastSelectedFlatlList = selected; + LLTeleportHistoryFlatItem* item = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem()); + if (item) + mLastSelectedItemIndex = item->getIndex(); - togglePanelPlacesButtons(mHistoryItems->getFirstSelected() != NULL); + S32 tabs_cnt = mItemContainers.size(); + + for (S32 n = 0; n < tabs_cnt; n++) + { + LLAccordionCtrlTab* tab = mItemContainers.get(n); + + if (!tab->getVisible()) + continue; + + LLFlatListView *flv = getFlatListViewFromTab(tab); + if (!flv) + continue; + + if (flv == selected) + continue; + + flv->resetSelection(true); + } + + updateVerbs(); } -void LLTeleportHistoryPanel::handleItemSelect(const LLSD& data) +void LLTeleportHistoryPanel::onReturnKeyPressed() { - LLScrollListItem* itemp = mHistoryItems->getFirstSelected(); + // Teleport to selected region as default action on return key pressed + onTeleport(); +} + +void LLTeleportHistoryPanel::onDoubleClickItem() +{ + // If item got doubleclick, then that item is already selected + onTeleport(); +} + +void LLTeleportHistoryPanel::onAccordionTabRightClick(LLView *view, S32 x, S32 y, MASK mask) +{ + LLAccordionCtrlTab *tab = (LLAccordionCtrlTab *) view; - if (!itemp) + // If click occurred below the header, don't show this menu + if (y < tab->getRect().getHeight() - tab->getHeaderHeight() - tab->getPaddingBottom()) return; - togglePanelPlacesButtons(TRUE); + if (mAccordionTabMenu) + { + //preventing parent (menu holder) from deleting already "dead" context menus on exit + LLView* parent = mAccordionTabMenu->getParent(); + if (parent) + { + parent->removeChild(mAccordionTabMenu); + } + delete mAccordionTabMenu; + } + + // set up the callbacks for all of the avatar menu items + // (N.B. callbacks don't take const refs as mID is local scope) + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + + registrar.add("TeleportHistory.TabOpen", boost::bind(&LLTeleportHistoryPanel::onAccordionTabOpen, this, tab)); + registrar.add("TeleportHistory.TabClose", boost::bind(&LLTeleportHistoryPanel::onAccordionTabClose, this, tab)); + + // create the context menu from the XUI + mAccordionTabMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>( + "menu_teleport_history_tab.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); + + mAccordionTabMenu->setItemVisible("TabOpen", !tab->isExpanded() ? true : false); + mAccordionTabMenu->setItemVisible("TabClose", tab->isExpanded() ? true : false); + + mAccordionTabMenu->show(x, y); + LLMenuGL::showPopup(tab, mAccordionTabMenu, x, y); } -//static -void LLTeleportHistoryPanel::onDoubleClickItem(void* user_data) +void LLTeleportHistoryPanel::onAccordionTabOpen(LLAccordionCtrlTab *tab) { - LLTeleportHistoryPanel* self = (LLTeleportHistoryPanel*)user_data; - - LLScrollListItem* itemp = self->mHistoryItems->getFirstSelected(); - if(!itemp) + tab->setDisplayChildren(true); + mHistoryAccordion->arrange(); +} + +void LLTeleportHistoryPanel::onAccordionTabClose(LLAccordionCtrlTab *tab) +{ + tab->setDisplayChildren(false); + mHistoryAccordion->arrange(); +} + +void LLTeleportHistoryPanel::onExpandAllFolders() +{ + S32 tabs_cnt = mItemContainers.size(); + + for (S32 n = 0; n < tabs_cnt; n++) + { + mItemContainers.get(n)->setDisplayChildren(true); + } + mHistoryAccordion->arrange(); +} + +void LLTeleportHistoryPanel::onCollapseAllFolders() +{ + S32 tabs_cnt = mItemContainers.size(); + + for (S32 n = 0; n < tabs_cnt; n++) + { + mItemContainers.get(n)->setDisplayChildren(false); + } + mHistoryAccordion->arrange(); +} + +void LLTeleportHistoryPanel::onClearTeleportHistory() +{ + LLNotificationsUtil::add("ConfirmClearTeleportHistory", LLSD(), LLSD(), boost::bind(&LLTeleportHistoryPanel::onClearTeleportHistoryDialog, this, _1, _2)); +} + +bool LLTeleportHistoryPanel::onClearTeleportHistoryDialog(const LLSD& notification, const LLSD& response) +{ + + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (0 == option) + { + // order does matter, call this first or teleport history will contain one record(current location) + LLTeleportHistory::getInstance()->purgeItems(); + + LLTeleportHistoryStorage *th = LLTeleportHistoryStorage::getInstance(); + th->purgeItems(); + th->save(); + } + + return false; +} + +LLFlatListView* LLTeleportHistoryPanel::getFlatListViewFromTab(LLAccordionCtrlTab *tab) +{ + for (child_list_const_iter_t iter = tab->beginChild(); iter != tab->endChild(); iter++) + { + if (dynamic_cast<LLFlatListView*>(*iter)) + { + return (LLFlatListView*)*iter; // There should be one scroll list per tab. + } + } + + return NULL; +} + +void LLTeleportHistoryPanel::onGearButtonClicked() +{ + LLMenuGL* menu = (LLMenuGL*)mGearMenuHandle.get(); + if (!menu) return; - LLSD key; - key["type"] = LLPanelPlaces::TELEPORT_HISTORY; - key["id"] = itemp->getColumn(LIST_INDEX)->getValue().asInteger(); + // Shows the menu at the top of the button bar. + + // Calculate its coordinates. + LLPanel* bottom_panel = getChild<LLPanel>("bottom_panel"); + menu->arrangeAndClear(); + S32 menu_height = menu->getRect().getHeight(); + S32 menu_x = -2; // *HACK: compensates HPAD in showPopup() + S32 menu_y = bottom_panel->getRect().mTop + menu_height; + + // Actually show the menu. + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, menu, menu_x, menu_y); +} + +bool LLTeleportHistoryPanel::isActionEnabled(const LLSD& userdata) const +{ + S32 tabs_cnt = mItemContainers.size(); + + bool has_expanded_tabs = false; + bool has_collapsed_tabs = false; + + for (S32 n = 0; n < tabs_cnt; n++) + { + LLAccordionCtrlTab* tab = mItemContainers.get(n); + if (!tab->getVisible()) + continue; + + if (tab->getDisplayChildren()) + { + has_expanded_tabs = true; + } + else + { + has_collapsed_tabs = true; + } + + if (has_expanded_tabs && has_collapsed_tabs) + { + break; + } + } + + std::string command_name = userdata.asString(); + + if (has_expanded_tabs && command_name == "collapse_all") + { + return true; + } + + if (has_collapsed_tabs && command_name == "expand_all") + { + return true; + } + + return false; +} + +void LLTeleportHistoryPanel::setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool collapsed) +{ + LLSD param = acc_tab->getValue(); + param[COLLAPSED_BY_USER] = collapsed; + acc_tab->setValue(param); +} + +bool LLTeleportHistoryPanel::isAccordionCollapsedByUser(LLUICtrl* acc_tab) +{ + LLSD param = acc_tab->getValue(); + if(!param.has(COLLAPSED_BY_USER)) + { + return false; + } + return param[COLLAPSED_BY_USER].asBoolean(); +} + +void LLTeleportHistoryPanel::onAccordionExpand(LLUICtrl* ctrl, const LLSD& param) +{ + bool expanded = param.asBoolean(); + // Save accordion tab state to restore it in refresh() + setAccordionCollapsedByUser(ctrl, !expanded); + + // Reset selection upon accordion being collapsed + // to disable "Teleport" and "Map" buttons for hidden item. + if (!expanded && mLastSelectedFlatlList) + { + mLastSelectedFlatlList->resetSelection(); + } +} + +// static +void LLTeleportHistoryPanel::confirmTeleport(S32 hist_idx) +{ + LLSD args; + args["HISTORY_ENTRY"] = LLTeleportHistoryStorage::getInstance()->getItems()[hist_idx].mTitle; + LLNotificationsUtil::add("TeleportToHistoryEntry", args, LLSD(), + boost::bind(&LLTeleportHistoryPanel::onTeleportConfirmation, _1, _2, hist_idx)); +} + +// Called when user reacts upon teleport confirmation dialog. +// static +bool LLTeleportHistoryPanel::onTeleportConfirmation(const LLSD& notification, const LLSD& response, S32 hist_idx) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (0 == option) + { + // Teleport to given history item. + LLTeleportHistoryStorage::getInstance()->goToItem(hist_idx); + } - LLSideTray::getInstance()->showPanel("panel_places", key); + return false; } |