1198 lines
36 KiB
C++
1198 lines
36 KiB
C++
/**
|
|
* @file llpanelteleporthistory.cpp
|
|
* @brief Teleport history represented by a scrolling list
|
|
*
|
|
* $LicenseInfo:firstyear=2009&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 "llfloaterreg.h"
|
|
#include "llmenubutton.h"
|
|
|
|
#include "llfloaterworldmap.h"
|
|
#include "llpanelteleporthistory.h"
|
|
#include "llworldmap.h"
|
|
#include "llteleporthistorystorage.h"
|
|
#include "lltextutil.h"
|
|
|
|
#include "llaccordionctrl.h"
|
|
#include "llaccordionctrltab.h"
|
|
#include "llflatlistview.h"
|
|
#include "llfloatersidepanelcontainer.h"
|
|
#include "llnotificationsutil.h"
|
|
#include "lltextbox.h"
|
|
#include "lltoggleablemenu.h"
|
|
#include "llviewermenu.h"
|
|
#include "lllandmarkactions.h"
|
|
#include "llclipboard.h"
|
|
#include "lltrans.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, LLToggleableMenu *menu, const std::string ®ion_name,
|
|
LLDate date, 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 setDate(LLDate date);
|
|
void setHighlightedText(const std::string& text);
|
|
void updateTitle();
|
|
void updateTimestamp();
|
|
std::string getTimestamp();
|
|
|
|
/*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();
|
|
void showMenu(S32 x, S32 y);
|
|
|
|
LLButton* mProfileBtn;
|
|
LLTextBox* mTitle;
|
|
LLTextBox* mTimeTextBox;
|
|
|
|
LLToggleableMenu *mMenu;
|
|
|
|
S32 mIndex;
|
|
std::string mRegionName;
|
|
std::string mHighlight;
|
|
LLDate mDate;
|
|
LLRootHandle<LLTeleportHistoryFlatItem> mItemHandle;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class LLTeleportHistoryFlatItemStorage: public LLSingleton<LLTeleportHistoryFlatItemStorage>
|
|
{
|
|
LLSINGLETON_EMPTY_CTOR(LLTeleportHistoryFlatItemStorage);
|
|
protected:
|
|
typedef std::vector< LLHandle<LLTeleportHistoryFlatItem> > flat_item_list_t;
|
|
|
|
public:
|
|
LLTeleportHistoryFlatItem* getFlatItemForPersistentItem (
|
|
LLToggleableMenu *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, LLToggleableMenu *menu, const std::string ®ion_name,
|
|
LLDate date, const std::string &hl)
|
|
: LLPanel(),
|
|
mIndex(index),
|
|
mMenu(menu),
|
|
mRegionName(region_name),
|
|
mDate(date),
|
|
mHighlight(hl)
|
|
{
|
|
buildFromFile("panel_teleport_history_item.xml");
|
|
}
|
|
|
|
LLTeleportHistoryFlatItem::~LLTeleportHistoryFlatItem()
|
|
{
|
|
}
|
|
|
|
//virtual
|
|
bool LLTeleportHistoryFlatItem::postBuild()
|
|
{
|
|
mTitle = getChild<LLTextBox>("region");
|
|
|
|
mTimeTextBox = getChild<LLTextBox>("timestamp");
|
|
|
|
mProfileBtn = getChild<LLButton>("profile_btn");
|
|
|
|
mProfileBtn->setClickedCallback(boost::bind(&LLTeleportHistoryFlatItem::onProfileBtnClick, this));
|
|
|
|
updateTitle();
|
|
updateTimestamp();
|
|
|
|
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::setDate(LLDate date)
|
|
{
|
|
mDate = date;
|
|
}
|
|
|
|
std::string LLTeleportHistoryFlatItem::getTimestamp()
|
|
{
|
|
const LLDate &date = mDate;
|
|
std::string timestamp = "";
|
|
|
|
LLDate now = LLDate::now();
|
|
S32 now_year, now_month, now_day, now_hour, now_min, now_sec;
|
|
now.split(&now_year, &now_month, &now_day, &now_hour, &now_min, &now_sec);
|
|
|
|
const S32 seconds_in_day = 24 * 60 * 60;
|
|
S32 seconds_today = now_hour * 60 * 60 + now_min * 60 + now_sec;
|
|
S32 time_diff = (S32) now.secondsSinceEpoch() - (S32) date.secondsSinceEpoch();
|
|
|
|
// Only show timestamp for today and yesterday
|
|
if(time_diff < seconds_today + seconds_in_day)
|
|
{
|
|
timestamp = "[" + LLTrans::getString("TimeHour12")+"]:["
|
|
+ LLTrans::getString("TimeMin")+"] ["+ LLTrans::getString("TimeAMPM")+"]";
|
|
LLSD substitution;
|
|
substitution["datetime"] = (S32) date.secondsSinceEpoch();
|
|
LLStringUtil::format(timestamp, substitution);
|
|
}
|
|
|
|
return timestamp;
|
|
|
|
}
|
|
|
|
void LLTeleportHistoryFlatItem::updateTitle()
|
|
{
|
|
static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", LLColor4U(255, 255, 255));
|
|
|
|
LLTextUtil::textboxSetHighlightedVal(
|
|
mTitle,
|
|
LLStyle::Params().color(sFgColor),
|
|
mRegionName,
|
|
mHighlight);
|
|
}
|
|
|
|
void LLTeleportHistoryFlatItem::updateTimestamp()
|
|
{
|
|
static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", LLColor4U(255, 255, 255));
|
|
|
|
LLTextUtil::textboxSetHighlightedVal(
|
|
mTimeTextBox,
|
|
LLStyle::Params().color(sFgColor),
|
|
getTimestamp(),
|
|
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)
|
|
{
|
|
LLPanel::handleRightMouseDown(x, y, mask);
|
|
showMenu(x, y);
|
|
return true;
|
|
}
|
|
|
|
void LLTeleportHistoryFlatItem::showPlaceInfoPanel(S32 index)
|
|
{
|
|
LLSD params;
|
|
params["id"] = index;
|
|
params["type"] = "teleport_history";
|
|
|
|
LLFloaterSidePanelContainer::showPanel("places", params);
|
|
}
|
|
|
|
void LLTeleportHistoryFlatItem::onProfileBtnClick()
|
|
{
|
|
LLTeleportHistoryFlatItem::showPlaceInfoPanel(mIndex);
|
|
}
|
|
|
|
void LLTeleportHistoryFlatItem::showMenu(S32 x, S32 y)
|
|
{
|
|
mMenu->setButtonRect(this);
|
|
mMenu->buildDrawLabels();
|
|
mMenu->arrangeAndClear();
|
|
mMenu->updateParent(LLMenuGL::sMenuContainer);
|
|
|
|
LLMenuGL::showPopup(this, mMenu, x, y);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLTeleportHistoryFlatItem*
|
|
LLTeleportHistoryFlatItemStorage::getFlatItemForPersistentItem (
|
|
LLToggleableMenu *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->setDate(persistent_item.mDate);
|
|
item->setHighlightedText(hl);
|
|
item->setVisible(true);
|
|
item->updateTitle();
|
|
item->updateTimestamp();
|
|
}
|
|
else
|
|
{
|
|
// Item already added to parent
|
|
item = NULL;
|
|
}
|
|
}
|
|
|
|
if ( !item )
|
|
{
|
|
item = new LLTeleportHistoryFlatItem(cur_item_index,
|
|
menu,
|
|
persistent_item.mTitle,
|
|
persistent_item.mDate,
|
|
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();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// Not yet implemented; need to remove buildPanel() from constructor when we switch
|
|
//static LLRegisterPanelClassWrapper<LLTeleportHistoryPanel> t_teleport_history("panel_teleport_history");
|
|
|
|
LLTeleportHistoryPanel::LLTeleportHistoryPanel()
|
|
: LLPanelPlacesTab(),
|
|
mDirty(true),
|
|
mCurrentItem(0),
|
|
mTeleportHistory(NULL),
|
|
mHistoryAccordion(NULL),
|
|
mAccordionTabMenu(NULL),
|
|
mLastSelectedFlatlList(NULL),
|
|
mLastSelectedItemIndex(-1),
|
|
mGearItemMenu(NULL),
|
|
mSortingMenu(NULL)
|
|
{
|
|
buildFromFile( "panel_teleport_history.xml");
|
|
}
|
|
|
|
LLTeleportHistoryPanel::~LLTeleportHistoryPanel()
|
|
{
|
|
LLTeleportHistoryFlatItemStorage::instance().purge();
|
|
mTeleportHistoryChangedConnection.disconnect();
|
|
}
|
|
|
|
bool LLTeleportHistoryPanel::postBuild()
|
|
{
|
|
mCommitCallbackRegistrar.add("TeleportHistory.GearMenu.Action", boost::bind(&LLTeleportHistoryPanel::onGearMenuAction, this, _2));
|
|
mEnableCallbackRegistrar.add("TeleportHistory.GearMenu.Enable", boost::bind(&LLTeleportHistoryPanel::isActionEnabled, this, _2));
|
|
|
|
// init menus before list, since menus are passed to list
|
|
mGearItemMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_teleport_history_item.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
|
|
mGearItemMenu->setAlwaysShowMenu(true); // all items can be disabled if nothing is selected, show anyway
|
|
mSortingMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_teleport_history_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
|
|
|
|
mTeleportHistory = LLTeleportHistoryStorage::getInstance();
|
|
if (mTeleportHistory)
|
|
{
|
|
mTeleportHistoryChangedConnection = mTeleportHistory->setHistoryChangedCallback(boost::bind(&LLTeleportHistoryPanel::onTeleportHistoryChange, this, _1));
|
|
}
|
|
|
|
mHistoryAccordion = getChild<LLAccordionCtrl>("history_accordion");
|
|
|
|
if (mHistoryAccordion)
|
|
{
|
|
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.push_back(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.at(mItemContainers.size() - 1);
|
|
tab->setDisplayChildren(true);
|
|
setAccordionCollapsedByUser(tab, false);
|
|
}
|
|
|
|
if (mItemContainers.size() > 2)
|
|
{
|
|
LLAccordionCtrlTab* tab = mItemContainers.at(mItemContainers.size() - 2);
|
|
tab->setDisplayChildren(true);
|
|
setAccordionCollapsedByUser(tab, false);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// virtual
|
|
void LLTeleportHistoryPanel::draw()
|
|
{
|
|
if (mDirty)
|
|
refresh();
|
|
|
|
LLPanelPlacesTab::draw();
|
|
}
|
|
|
|
// virtual
|
|
void LLTeleportHistoryPanel::onSearchEdit(const std::string& string)
|
|
{
|
|
sFilterSubString = string;
|
|
showTeleportHistory();
|
|
}
|
|
|
|
// virtual
|
|
bool LLTeleportHistoryPanel::isSingleItemSelected()
|
|
{
|
|
return mLastSelectedFlatlList && mLastSelectedFlatlList->getSelectedItem();
|
|
}
|
|
|
|
// virtual
|
|
void LLTeleportHistoryPanel::onShowOnMap()
|
|
{
|
|
if (!mLastSelectedFlatlList)
|
|
return;
|
|
|
|
LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
|
|
|
|
if(!itemp)
|
|
return;
|
|
|
|
LLVector3d global_pos = mTeleportHistory->getItems()[itemp->getIndex()].mGlobalPos;
|
|
|
|
if (!global_pos.isExactlyZero())
|
|
{
|
|
LLFloaterWorldMap::getInstance()->trackLocation(global_pos);
|
|
LLFloaterReg::showInstance("world_map", "center");
|
|
}
|
|
}
|
|
|
|
//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()
|
|
{
|
|
if (!mLastSelectedFlatlList)
|
|
return;
|
|
|
|
LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
|
|
if(!itemp)
|
|
return;
|
|
|
|
// teleport to existing item in history, so we don't add it again
|
|
confirmTeleport(itemp->getIndex());
|
|
}
|
|
|
|
// virtual
|
|
void LLTeleportHistoryPanel::onRemoveSelected()
|
|
{
|
|
LLNotificationsUtil::add("ConfirmClearTeleportHistory", LLSD(), LLSD(), boost::bind(&LLTeleportHistoryPanel::onClearTeleportHistoryDialog, this, _1, _2));
|
|
}
|
|
|
|
/*
|
|
// virtual
|
|
void LLTeleportHistoryPanel::onCopySLURL()
|
|
{
|
|
LLScrollListItem* itemp = mHistoryItems->getFirstSelected();
|
|
if(!itemp)
|
|
return;
|
|
|
|
S32 index = itemp->getColumn(LIST_INDEX)->getValue().asInteger();
|
|
|
|
const LLTeleportHistory::slurl_list_t& hist_items = mTeleportHistory->getItems();
|
|
|
|
LLVector3d global_pos = hist_items[index].mGlobalPos;
|
|
|
|
U64 new_region_handle = to_region_handle(global_pos);
|
|
|
|
LLWorldMapMessage::url_callback_t cb = boost::bind(
|
|
&LLPanelPlacesTab::onRegionResponse, this,
|
|
global_pos, _1, _2, _3, _4);
|
|
|
|
LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, cb, std::string("unused"), false);
|
|
}
|
|
*/
|
|
|
|
// virtual
|
|
void LLTeleportHistoryPanel::updateVerbs()
|
|
{
|
|
if (!isTabVisible())
|
|
return;
|
|
|
|
if (sRemoveBtn)
|
|
{
|
|
sRemoveBtn->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
// virtual
|
|
LLToggleableMenu* LLTeleportHistoryPanel::getSelectionMenu()
|
|
{
|
|
return mGearItemMenu;
|
|
}
|
|
|
|
// virtual
|
|
LLToggleableMenu* LLTeleportHistoryPanel::getSortingMenu()
|
|
{
|
|
return mSortingMenu;
|
|
}
|
|
|
|
// virtual
|
|
LLToggleableMenu* LLTeleportHistoryPanel::getCreateMenu()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void LLTeleportHistoryPanel::getNextTab(const LLDate& item_date, S32& tab_idx, LLDate& tab_date)
|
|
{
|
|
const U32 seconds_in_day = 24 * 60 * 60;
|
|
|
|
S32 tabs_cnt = static_cast<S32>(mItemContainers.size());
|
|
S32 curr_year = 0, curr_month = 0, curr_day = 0;
|
|
|
|
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);
|
|
|
|
tab_idx = -1;
|
|
|
|
while (tab_idx < tabs_cnt - 1 && item_date < tab_date)
|
|
{
|
|
tab_idx++;
|
|
|
|
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);
|
|
tab_idx = static_cast<S32>(mItemContainers.size()) - 1 - tab_idx;
|
|
if (tab_idx >= 0)
|
|
{
|
|
LLAccordionCtrlTab* tab = mItemContainers.at(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(mGearItemMenu,
|
|
items[mCurrentItem],
|
|
mCurrentItem,
|
|
filter_string);
|
|
if ( !curr_flat_view->addItem(item, LLUUID::null, ADD_BOTTOM, false) )
|
|
LL_ERRS() << "Couldn't add flat item to teleport history." << LL_ENDL;
|
|
if (mLastSelectedItemIndex == mCurrentItem)
|
|
curr_flat_view->selectItem(item, true);
|
|
}
|
|
|
|
mCurrentItem--;
|
|
|
|
if (++added_items >= ADD_LIMIT)
|
|
break;
|
|
}
|
|
|
|
for (S32 n = static_cast<S32>(mItemContainers.size()) - 1; n >= 0; --n)
|
|
{
|
|
LLAccordionCtrlTab* tab = mItemContainers.at(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 = NULL;
|
|
|
|
if (mItemContainers.size() > 0)
|
|
{
|
|
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(mGearItemMenu,
|
|
history_items[history_items.size() - 1], // Most recent item, it was added instead of removed
|
|
static_cast<S32>(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 = static_cast<S32>(mItemContainers.size()) - 1; tab_idx >= 0; --tab_idx)
|
|
{
|
|
LLAccordionCtrlTab* tab = mItemContainers.at(tab_idx);
|
|
if (!tab->getVisible())
|
|
continue;
|
|
|
|
fv = getFlatListViewFromTab(tab);
|
|
if (!fv)
|
|
{
|
|
showTeleportHistory();
|
|
return;
|
|
}
|
|
|
|
std::vector<LLPanel*> items;
|
|
fv->getItems(items);
|
|
|
|
auto items_cnt = items.size();
|
|
for (size_t 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);
|
|
|
|
mHistoryAccordion->arrange();
|
|
|
|
return; // No need to decrement idexes for the rest of items
|
|
}
|
|
|
|
item->setIndex(item->getIndex() - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLTeleportHistoryPanel::showTeleportHistory()
|
|
{
|
|
mDirty = true;
|
|
|
|
// Starting to add items from last one, in reverse order,
|
|
// since TeleportHistory keeps most recent item at the end
|
|
if (!mTeleportHistory)
|
|
{
|
|
mTeleportHistory = LLTeleportHistoryStorage::getInstance();
|
|
}
|
|
|
|
mCurrentItem = static_cast<S32>(mTeleportHistory->getItems().size()) - 1;
|
|
|
|
for (S32 n = static_cast<S32>(mItemContainers.size()) - 1; n >= 0; --n)
|
|
{
|
|
LLAccordionCtrlTab* tab = mItemContainers.at(n);
|
|
if (tab)
|
|
{
|
|
tab->setVisible(false);
|
|
|
|
LLFlatListView* fv = getFlatListViewFromTab(tab);
|
|
if (fv)
|
|
{
|
|
// 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();
|
|
|
|
auto tabs_cnt = mItemContainers.size();
|
|
|
|
for (size_t n = 0; n < tabs_cnt; n++)
|
|
{
|
|
LLAccordionCtrlTab* tab = mItemContainers.at(n);
|
|
|
|
if (!tab->getVisible())
|
|
continue;
|
|
|
|
LLFlatListView *flv = getFlatListViewFromTab(tab);
|
|
if (!flv)
|
|
continue;
|
|
|
|
if (flv == selected)
|
|
continue;
|
|
|
|
flv->resetSelection(true);
|
|
}
|
|
|
|
updateVerbs();
|
|
}
|
|
|
|
void LLTeleportHistoryPanel::onReturnKeyPressed()
|
|
{
|
|
// 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 click occurred below the header, don't show this menu
|
|
if (y < tab->getRect().getHeight() - tab->getHeaderHeight() - tab->getPaddingBottom())
|
|
return;
|
|
|
|
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
|
|
llassert(LLMenuGL::sMenuContainer != NULL);
|
|
mAccordionTabMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
|
|
"menu_teleport_history_tab.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
|
|
|
|
mAccordionTabMenu->setItemVisible("TabOpen", !tab->isExpanded());
|
|
mAccordionTabMenu->setItemVisible("TabClose", tab->isExpanded());
|
|
|
|
mAccordionTabMenu->show(x, y);
|
|
LLMenuGL::showPopup(tab, mAccordionTabMenu, x, y);
|
|
}
|
|
|
|
void LLTeleportHistoryPanel::onAccordionTabOpen(LLAccordionCtrlTab *tab)
|
|
{
|
|
tab->setDisplayChildren(true);
|
|
mHistoryAccordion->arrange();
|
|
}
|
|
|
|
void LLTeleportHistoryPanel::onAccordionTabClose(LLAccordionCtrlTab *tab)
|
|
{
|
|
tab->setDisplayChildren(false);
|
|
mHistoryAccordion->arrange();
|
|
}
|
|
|
|
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::gotSLURLCallback(const std::string& slurl)
|
|
{
|
|
LLClipboard::instance().copyToClipboard(utf8str_to_wstring(slurl), 0, static_cast<S32>(slurl.size()));
|
|
|
|
LLSD args;
|
|
args["SLURL"] = slurl;
|
|
|
|
LLNotificationsUtil::add("CopySLURL", args);
|
|
}
|
|
|
|
void LLTeleportHistoryPanel::onGearMenuAction(const LLSD& userdata)
|
|
{
|
|
std::string command_name = userdata.asString();
|
|
|
|
if ("expand_all" == command_name)
|
|
{
|
|
auto tabs_cnt = mItemContainers.size();
|
|
|
|
for (size_t n = 0; n < tabs_cnt; n++)
|
|
{
|
|
mItemContainers.at(n)->setDisplayChildren(true);
|
|
}
|
|
mHistoryAccordion->arrange();
|
|
}
|
|
else if ("collapse_all" == command_name)
|
|
{
|
|
auto tabs_cnt = mItemContainers.size();
|
|
|
|
for (size_t n = 0; n < tabs_cnt; n++)
|
|
{
|
|
mItemContainers.at(n)->setDisplayChildren(false);
|
|
}
|
|
mHistoryAccordion->arrange();
|
|
|
|
if (mLastSelectedFlatlList)
|
|
{
|
|
mLastSelectedFlatlList->resetSelection();
|
|
}
|
|
}
|
|
|
|
S32 index = -1;
|
|
if (mLastSelectedFlatlList)
|
|
{
|
|
LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
|
|
if (itemp)
|
|
{
|
|
index = itemp->getIndex();
|
|
}
|
|
}
|
|
|
|
if ("teleport" == command_name)
|
|
{
|
|
confirmTeleport(index);
|
|
}
|
|
else if ("view" == command_name)
|
|
{
|
|
LLTeleportHistoryFlatItem::showPlaceInfoPanel(index);
|
|
}
|
|
else if ("show_on_map" == command_name)
|
|
{
|
|
LLTeleportHistoryStorage::getInstance()->showItemOnMap(index);
|
|
}
|
|
else if ("copy_slurl" == command_name)
|
|
{
|
|
LLVector3d globalPos = LLTeleportHistoryStorage::getInstance()->getItems()[index].mGlobalPos;
|
|
LLLandmarkActions::getSLURLfromPosGlobal(globalPos,
|
|
boost::bind(&LLTeleportHistoryPanel::gotSLURLCallback, _1));
|
|
}
|
|
else if ("remove" == command_name)
|
|
{
|
|
LLTeleportHistoryStorage::getInstance()->removeItem(index);
|
|
LLTeleportHistoryStorage::getInstance()->save();
|
|
showTeleportHistory();
|
|
}
|
|
}
|
|
|
|
bool LLTeleportHistoryPanel::isActionEnabled(const LLSD& userdata) const
|
|
{
|
|
std::string command_name = userdata.asString();
|
|
|
|
if (command_name == "collapse_all"
|
|
|| command_name == "expand_all")
|
|
{
|
|
auto tabs_cnt = mItemContainers.size();
|
|
|
|
bool has_expanded_tabs = false;
|
|
bool has_collapsed_tabs = false;
|
|
|
|
for (size_t n = 0; n < tabs_cnt; n++)
|
|
{
|
|
LLAccordionCtrlTab* tab = mItemContainers.at(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;
|
|
}
|
|
}
|
|
|
|
if (command_name == "collapse_all")
|
|
{
|
|
return has_expanded_tabs;
|
|
}
|
|
|
|
if (command_name == "expand_all")
|
|
{
|
|
return has_collapsed_tabs;
|
|
}
|
|
}
|
|
|
|
if (command_name == "clear_history")
|
|
{
|
|
return mTeleportHistory->getItems().size() > 0;
|
|
}
|
|
|
|
if ("teleport" == command_name
|
|
|| "view" == command_name
|
|
|| "show_on_map" == command_name
|
|
|| "copy_slurl" == command_name
|
|
|| "remove" == command_name)
|
|
{
|
|
if (!mLastSelectedFlatlList)
|
|
{
|
|
return false;
|
|
}
|
|
LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
|
|
return itemp != NULL;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
return false;
|
|
}
|