PageRenderTime 515ms CodeModel.GetById 111ms app.highlight 322ms RepoModel.GetById 70ms app.codeStats 1ms

/indra/newview/llpanelteleporthistory.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1109 lines | 791 code | 199 blank | 119 comment | 121 complexity | f935aa1879f464755364025e5a632ad4 MD5 | raw file
   1/** 
   2 * @file llpanelteleporthistory.cpp
   3 * @brief Teleport history represented by a scrolling list
   4 *
   5 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "llfloaterreg.h"
  30#include "llmenubutton.h"
  31
  32#include "llfloaterworldmap.h"
  33#include "llpanelteleporthistory.h"
  34#include "llworldmap.h"
  35#include "llteleporthistorystorage.h"
  36#include "lltextutil.h"
  37
  38#include "llaccordionctrl.h"
  39#include "llaccordionctrltab.h"
  40#include "llflatlistview.h"
  41#include "llfloatersidepanelcontainer.h"
  42#include "llnotificationsutil.h"
  43#include "lltextbox.h"
  44#include "lltoggleablemenu.h"
  45#include "llviewermenu.h"
  46#include "lllandmarkactions.h"
  47#include "llclipboard.h"
  48
  49// Maximum number of items that can be added to a list in one pass.
  50// Used to limit time spent for items list update per frame.
  51static const U32 ADD_LIMIT = 50;
  52
  53static const std::string COLLAPSED_BY_USER = "collapsed_by_user";
  54
  55class LLTeleportHistoryFlatItem : public LLPanel
  56{
  57public:
  58	LLTeleportHistoryFlatItem(S32 index, LLTeleportHistoryPanel::ContextMenu *context_menu, const std::string &region_name, const std::string &hl);
  59	virtual ~LLTeleportHistoryFlatItem();
  60
  61	virtual BOOL postBuild();
  62
  63	/*virtual*/ S32 notify(const LLSD& info);
  64
  65	S32 getIndex() { return mIndex; }
  66	void setIndex(S32 index) { mIndex = index; }
  67	const std::string& getRegionName() { return mRegionName;}
  68	void setRegionName(const std::string& name);
  69	void setHighlightedText(const std::string& text);
  70	void updateTitle();
  71
  72	/*virtual*/ void setValue(const LLSD& value);
  73
  74	void onMouseEnter(S32 x, S32 y, MASK mask);
  75	void onMouseLeave(S32 x, S32 y, MASK mask);
  76	virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
  77
  78	static void showPlaceInfoPanel(S32 index);
  79
  80	LLHandle<LLTeleportHistoryFlatItem> getItemHandle()	{ mItemHandle.bind(this); return mItemHandle; }
  81
  82private:
  83	void onProfileBtnClick();
  84
  85	LLButton* mProfileBtn;
  86	LLTextBox* mTitle;
  87	
  88	LLTeleportHistoryPanel::ContextMenu *mContextMenu;
  89
  90	S32 mIndex;
  91	std::string mRegionName;
  92	std::string mHighlight;
  93	LLRootHandle<LLTeleportHistoryFlatItem> mItemHandle;
  94};
  95
  96////////////////////////////////////////////////////////////////////////////////
  97////////////////////////////////////////////////////////////////////////////////
  98////////////////////////////////////////////////////////////////////////////////
  99
 100class LLTeleportHistoryFlatItemStorage: public LLSingleton<LLTeleportHistoryFlatItemStorage> {
 101protected:
 102	typedef std::vector< LLHandle<LLTeleportHistoryFlatItem> > flat_item_list_t;
 103
 104public:
 105	LLTeleportHistoryFlatItem* getFlatItemForPersistentItem (
 106		LLTeleportHistoryPanel::ContextMenu *context_menu,
 107		const LLTeleportHistoryPersistentItem& persistent_item,
 108		const S32 cur_item_index,
 109		const std::string &hl);
 110
 111	void removeItem(LLTeleportHistoryFlatItem* item);
 112
 113	void purge();
 114
 115private:
 116
 117	flat_item_list_t mItems;
 118};
 119
 120////////////////////////////////////////////////////////////////////////////////
 121////////////////////////////////////////////////////////////////////////////////
 122////////////////////////////////////////////////////////////////////////////////
 123
 124LLTeleportHistoryFlatItem::LLTeleportHistoryFlatItem(S32 index, LLTeleportHistoryPanel::ContextMenu *context_menu, const std::string &region_name, const std::string &hl)
 125:	LLPanel(),
 126	mIndex(index),
 127	mContextMenu(context_menu),
 128	mRegionName(region_name),
 129	mHighlight(hl)
 130{
 131	buildFromFile( "panel_teleport_history_item.xml");
 132}
 133
 134LLTeleportHistoryFlatItem::~LLTeleportHistoryFlatItem()
 135{
 136}
 137
 138//virtual
 139BOOL LLTeleportHistoryFlatItem::postBuild()
 140{
 141	mTitle = getChild<LLTextBox>("region");
 142
 143	mProfileBtn = getChild<LLButton>("profile_btn");
 144        
 145	mProfileBtn->setClickedCallback(boost::bind(&LLTeleportHistoryFlatItem::onProfileBtnClick, this));
 146
 147	updateTitle();
 148
 149	return true;
 150}
 151
 152S32 LLTeleportHistoryFlatItem::notify(const LLSD& info)
 153{
 154	if(info.has("detach"))
 155	{
 156		delete mMouseDownSignal;
 157		mMouseDownSignal = NULL;
 158		delete mRightMouseDownSignal;
 159		mRightMouseDownSignal = NULL;
 160		return 1;
 161	}
 162	return 0;
 163}
 164
 165void LLTeleportHistoryFlatItem::setValue(const LLSD& value)
 166{
 167	if (!value.isMap()) return;;
 168	if (!value.has("selected")) return;
 169	getChildView("selected_icon")->setVisible( value["selected"]);
 170}
 171
 172void LLTeleportHistoryFlatItem::setHighlightedText(const std::string& text)
 173{
 174	mHighlight = text;
 175}
 176
 177void LLTeleportHistoryFlatItem::setRegionName(const std::string& name)
 178{
 179	mRegionName = name;
 180}
 181
 182void LLTeleportHistoryFlatItem::updateTitle()
 183{
 184	static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", LLColor4U(255, 255, 255));
 185
 186	LLTextUtil::textboxSetHighlightedVal(
 187		mTitle,
 188		LLStyle::Params().color(sFgColor),
 189		mRegionName,
 190		mHighlight);
 191}
 192
 193void LLTeleportHistoryFlatItem::onMouseEnter(S32 x, S32 y, MASK mask)
 194{
 195	getChildView("hovered_icon")->setVisible( true);
 196	mProfileBtn->setVisible(true);
 197
 198	LLPanel::onMouseEnter(x, y, mask);
 199}
 200
 201void LLTeleportHistoryFlatItem::onMouseLeave(S32 x, S32 y, MASK mask)
 202{
 203	getChildView("hovered_icon")->setVisible( false);
 204	mProfileBtn->setVisible(false);
 205
 206	LLPanel::onMouseLeave(x, y, mask);
 207}
 208
 209// virtual
 210BOOL LLTeleportHistoryFlatItem::handleRightMouseDown(S32 x, S32 y, MASK mask)
 211{
 212	if (mContextMenu)
 213		mContextMenu->show(this, mIndex, x, y);
 214
 215	return LLPanel::handleRightMouseDown(x, y, mask);
 216}
 217
 218void LLTeleportHistoryFlatItem::showPlaceInfoPanel(S32 index)
 219{
 220	LLSD params;
 221	params["id"] = index;
 222	params["type"] = "teleport_history";
 223
 224	LLFloaterSidePanelContainer::showPanel("places", params);
 225}
 226
 227void LLTeleportHistoryFlatItem::onProfileBtnClick()
 228{
 229	LLTeleportHistoryFlatItem::showPlaceInfoPanel(mIndex);
 230}
 231
 232////////////////////////////////////////////////////////////////////////////////
 233////////////////////////////////////////////////////////////////////////////////
 234////////////////////////////////////////////////////////////////////////////////
 235
 236LLTeleportHistoryFlatItem*
 237LLTeleportHistoryFlatItemStorage::getFlatItemForPersistentItem (
 238	LLTeleportHistoryPanel::ContextMenu *context_menu,
 239	const LLTeleportHistoryPersistentItem& persistent_item,
 240	const S32 cur_item_index,
 241	const std::string &hl)
 242{
 243	LLTeleportHistoryFlatItem* item = NULL;
 244	if ( cur_item_index < (S32) mItems.size() )
 245	{
 246		item = mItems[cur_item_index].get();
 247		if (item->getParent() == NULL)
 248		{
 249			item->setIndex(cur_item_index);
 250			item->setRegionName(persistent_item.mTitle);
 251			item->setHighlightedText(hl);
 252			item->setVisible(TRUE);
 253			item->updateTitle();
 254		}
 255		else
 256		{
 257			// Item already added to parent
 258			item = NULL;
 259		}
 260	}
 261
 262	if ( !item )
 263	{
 264		item = new LLTeleportHistoryFlatItem(cur_item_index,
 265											 context_menu,
 266											 persistent_item.mTitle,
 267											 hl);
 268		mItems.push_back(item->getItemHandle());
 269	}
 270
 271	return item;
 272}
 273
 274void LLTeleportHistoryFlatItemStorage::removeItem(LLTeleportHistoryFlatItem* item)
 275{
 276	if (item)
 277	{
 278		flat_item_list_t::iterator item_iter = std::find(mItems.begin(),
 279														 mItems.end(),
 280														 item->getItemHandle());
 281		if (item_iter != mItems.end())
 282		{
 283			mItems.erase(item_iter);
 284		}
 285	}
 286}
 287
 288void LLTeleportHistoryFlatItemStorage::purge()
 289{
 290	for ( flat_item_list_t::iterator
 291			  it = mItems.begin(),
 292			  it_end = mItems.end();
 293		  it != it_end; ++it )
 294	{
 295		LLHandle <LLTeleportHistoryFlatItem> item_handle = *it;
 296		if ( !item_handle.isDead() && item_handle.get()->getParent() == NULL )
 297		{
 298			item_handle.get()->die();
 299		}
 300	}
 301	mItems.clear();
 302}
 303
 304////////////////////////////////////////////////////////////////////////////////
 305////////////////////////////////////////////////////////////////////////////////
 306////////////////////////////////////////////////////////////////////////////////
 307
 308LLTeleportHistoryPanel::ContextMenu::ContextMenu() :
 309	mMenu(NULL), mIndex(0)
 310{
 311}
 312
 313void LLTeleportHistoryPanel::ContextMenu::show(LLView* spawning_view, S32 index, S32 x, S32 y)
 314{
 315	if (mMenu)
 316	{
 317		//preventing parent (menu holder) from deleting already "dead" context menus on exit
 318		LLView* parent = mMenu->getParent();
 319		if (parent)
 320		{
 321			parent->removeChild(mMenu);
 322		}
 323		delete mMenu;
 324	}
 325
 326	mIndex = index;
 327	mMenu = createMenu();
 328
 329	mMenu->show(x, y);
 330	LLMenuGL::showPopup(spawning_view, mMenu, x, y);
 331}
 332
 333LLContextMenu* LLTeleportHistoryPanel::ContextMenu::createMenu()
 334{
 335	// set up the callbacks for all of the avatar menu items
 336	// (N.B. callbacks don't take const refs as mID is local scope)
 337	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 338
 339	registrar.add("TeleportHistory.Teleport",	boost::bind(&LLTeleportHistoryPanel::ContextMenu::onTeleport, this));
 340	registrar.add("TeleportHistory.MoreInformation",boost::bind(&LLTeleportHistoryPanel::ContextMenu::onInfo, this));
 341	registrar.add("TeleportHistory.CopyToClipboard",boost::bind(&LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard, this));
 342
 343	// create the context menu from the XUI
 344	return LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
 345		"menu_teleport_history_item.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
 346}
 347
 348void LLTeleportHistoryPanel::ContextMenu::onTeleport()
 349{
 350	confirmTeleport(mIndex);
 351}
 352
 353void LLTeleportHistoryPanel::ContextMenu::onInfo()
 354{
 355	LLTeleportHistoryFlatItem::showPlaceInfoPanel(mIndex);
 356}
 357
 358//static
 359void LLTeleportHistoryPanel::ContextMenu::gotSLURLCallback(const std::string& slurl)
 360{
 361	gClipboard.copyFromString(utf8str_to_wstring(slurl));
 362}
 363
 364void LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard()
 365{
 366	LLVector3d globalPos = LLTeleportHistoryStorage::getInstance()->getItems()[mIndex].mGlobalPos;
 367	LLLandmarkActions::getSLURLfromPosGlobal(globalPos,
 368		boost::bind(&LLTeleportHistoryPanel::ContextMenu::gotSLURLCallback, _1));
 369}
 370
 371// Not yet implemented; need to remove buildPanel() from constructor when we switch
 372//static LLRegisterPanelClassWrapper<LLTeleportHistoryPanel> t_teleport_history("panel_teleport_history");
 373
 374LLTeleportHistoryPanel::LLTeleportHistoryPanel()
 375	:	LLPanelPlacesTab(),
 376		mDirty(true),
 377		mCurrentItem(0),
 378		mTeleportHistory(NULL),
 379		mHistoryAccordion(NULL),
 380		mAccordionTabMenu(NULL),
 381		mLastSelectedFlatlList(NULL),
 382		mLastSelectedItemIndex(-1),
 383		mMenuGearButton(NULL)
 384{
 385	buildFromFile( "panel_teleport_history.xml");
 386}
 387
 388LLTeleportHistoryPanel::~LLTeleportHistoryPanel()
 389{
 390	LLTeleportHistoryFlatItemStorage::instance().purge();
 391	if (mGearMenuHandle.get()) mGearMenuHandle.get()->die();
 392	mTeleportHistoryChangedConnection.disconnect();
 393}
 394
 395BOOL LLTeleportHistoryPanel::postBuild()
 396{
 397	mTeleportHistory = LLTeleportHistoryStorage::getInstance();
 398	if (mTeleportHistory)
 399	{
 400		mTeleportHistoryChangedConnection = mTeleportHistory->setHistoryChangedCallback(boost::bind(&LLTeleportHistoryPanel::onTeleportHistoryChange, this, _1));
 401	}
 402
 403	mHistoryAccordion = getChild<LLAccordionCtrl>("history_accordion");
 404
 405	if (mHistoryAccordion)
 406	{
 407		for (child_list_const_iter_t iter = mHistoryAccordion->beginChild(); iter != mHistoryAccordion->endChild(); iter++)
 408		{
 409			if (dynamic_cast<LLAccordionCtrlTab*>(*iter))
 410			{
 411				LLAccordionCtrlTab* tab = (LLAccordionCtrlTab*)*iter;
 412				tab->setRightMouseDownCallback(boost::bind(&LLTeleportHistoryPanel::onAccordionTabRightClick, this, _1, _2, _3, _4));
 413				tab->setDisplayChildren(false);
 414				tab->setDropDownStateChangedCallback(boost::bind(&LLTeleportHistoryPanel::onAccordionExpand, this, _1, _2));
 415
 416				// All accordion tabs are collapsed initially
 417				setAccordionCollapsedByUser(tab, true);
 418
 419				mItemContainers.put(tab);
 420
 421				LLFlatListView* fl = getFlatListViewFromTab(tab);
 422				if (fl)
 423				{
 424					fl->setCommitOnSelectionChange(true);
 425					fl->setDoubleClickCallback(boost::bind(&LLTeleportHistoryPanel::onDoubleClickItem, this));
 426					fl->setCommitCallback(boost::bind(&LLTeleportHistoryPanel::handleItemSelect, this, fl));
 427					fl->setReturnCallback(boost::bind(&LLTeleportHistoryPanel::onReturnKeyPressed, this));
 428				}
 429			}
 430		}
 431
 432		// Open first 2 accordion tabs
 433		if (mItemContainers.size() > 1)
 434		{
 435			LLAccordionCtrlTab* tab = mItemContainers.get(mItemContainers.size() - 1);
 436			tab->setDisplayChildren(true);
 437			setAccordionCollapsedByUser(tab, false);
 438		}
 439
 440		if (mItemContainers.size() > 2)
 441		{
 442			LLAccordionCtrlTab* tab = mItemContainers.get(mItemContainers.size() - 2);
 443			tab->setDisplayChildren(true);
 444			setAccordionCollapsedByUser(tab, false);
 445		}
 446	}
 447
 448	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 449
 450	registrar.add("TeleportHistory.ExpandAllFolders",  boost::bind(&LLTeleportHistoryPanel::onExpandAllFolders,  this));
 451	registrar.add("TeleportHistory.CollapseAllFolders",  boost::bind(&LLTeleportHistoryPanel::onCollapseAllFolders,  this));
 452	registrar.add("TeleportHistory.ClearTeleportHistory",  boost::bind(&LLTeleportHistoryPanel::onClearTeleportHistory,  this));
 453	mEnableCallbackRegistrar.add("TeleportHistory.GearMenu.Enable", boost::bind(&LLTeleportHistoryPanel::isActionEnabled, this, _2));
 454
 455	mMenuGearButton = getChild<LLMenuButton>("gear_btn");
 456
 457	LLToggleableMenu* gear_menu  = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_teleport_history_gear.xml",  gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());;
 458	if(gear_menu)
 459	{
 460		mGearMenuHandle  = gear_menu->getHandle();
 461		mMenuGearButton->setMenu(gear_menu);
 462	}
 463
 464	return TRUE;
 465}
 466
 467// virtual
 468void LLTeleportHistoryPanel::draw()
 469{
 470	if (mDirty)
 471		refresh();
 472
 473	LLPanelPlacesTab::draw();
 474}
 475
 476// virtual
 477void LLTeleportHistoryPanel::onSearchEdit(const std::string& string)
 478{
 479	sFilterSubString = string;
 480	showTeleportHistory();
 481}
 482
 483// virtual
 484bool LLTeleportHistoryPanel::isSingleItemSelected()
 485{
 486	return mLastSelectedFlatlList && mLastSelectedFlatlList->getSelectedItem();
 487}
 488
 489// virtual
 490void LLTeleportHistoryPanel::onShowOnMap()
 491{
 492	if (!mLastSelectedFlatlList)
 493		return;
 494
 495	LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
 496
 497	if(!itemp)
 498		return;
 499
 500	LLVector3d global_pos = mTeleportHistory->getItems()[itemp->getIndex()].mGlobalPos;
 501
 502	if (!global_pos.isExactlyZero())
 503	{
 504		LLFloaterWorldMap::getInstance()->trackLocation(global_pos);
 505		LLFloaterReg::showInstance("world_map", "center");
 506	}
 507}
 508
 509//virtual
 510void LLTeleportHistoryPanel::onShowProfile()
 511{
 512	if (!mLastSelectedFlatlList)
 513		return;
 514
 515	LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
 516
 517	if(!itemp)
 518		return;
 519
 520	LLTeleportHistoryFlatItem::showPlaceInfoPanel(itemp->getIndex());
 521}
 522
 523// virtual
 524void LLTeleportHistoryPanel::onTeleport()
 525{
 526	if (!mLastSelectedFlatlList)
 527		return;
 528
 529	LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
 530	if(!itemp)
 531		return;
 532
 533	// teleport to existing item in history, so we don't add it again
 534	confirmTeleport(itemp->getIndex());
 535}
 536
 537/*
 538// virtual
 539void LLTeleportHistoryPanel::onCopySLURL()
 540{
 541	LLScrollListItem* itemp = mHistoryItems->getFirstSelected();
 542	if(!itemp)
 543		return;
 544
 545	S32 index = itemp->getColumn(LIST_INDEX)->getValue().asInteger();
 546
 547	const LLTeleportHistory::slurl_list_t& hist_items = mTeleportHistory->getItems();
 548
 549	LLVector3d global_pos = hist_items[index].mGlobalPos;
 550
 551	U64 new_region_handle = to_region_handle(global_pos);
 552
 553	LLWorldMapMessage::url_callback_t cb = boost::bind(
 554			&LLPanelPlacesTab::onRegionResponse, this,
 555			global_pos, _1, _2, _3, _4);
 556
 557	LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, cb, std::string("unused"), false);
 558}
 559*/
 560
 561// virtual
 562void LLTeleportHistoryPanel::updateVerbs()
 563{
 564	if (!isTabVisible())
 565		return;
 566
 567	if (!mLastSelectedFlatlList)
 568	{
 569		mTeleportBtn->setEnabled(false);
 570		mShowProfile->setEnabled(false);
 571		mShowOnMapBtn->setEnabled(false);
 572		return;
 573	}
 574
 575	LLTeleportHistoryFlatItem* itemp = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
 576
 577	mTeleportBtn->setEnabled(NULL != itemp);
 578	mShowProfile->setEnabled(NULL != itemp);
 579	mShowOnMapBtn->setEnabled(NULL != itemp);
 580}
 581
 582void LLTeleportHistoryPanel::getNextTab(const LLDate& item_date, S32& tab_idx, LLDate& tab_date)
 583{
 584	const U32 seconds_in_day = 24 * 60 * 60;
 585
 586	S32 tabs_cnt = mItemContainers.size();
 587	S32 curr_year = 0, curr_month = 0, curr_day = 0;
 588
 589	tab_date = LLDate::now();
 590	tab_date.split(&curr_year, &curr_month, &curr_day);
 591	tab_date.fromYMDHMS(curr_year, curr_month, curr_day); // Set hour, min, and sec to 0
 592	tab_date.secondsSinceEpoch(tab_date.secondsSinceEpoch() + seconds_in_day);
 593
 594	tab_idx = -1;
 595
 596	while (tab_idx < tabs_cnt - 1 && item_date < tab_date)
 597	{
 598		tab_idx++;
 599
 600		if (tab_idx <= tabs_cnt - 4)
 601		{
 602			// All tabs, except last three, are tabs for one day, so just push tab_date back by one day
 603			tab_date.secondsSinceEpoch(tab_date.secondsSinceEpoch() - seconds_in_day);
 604		}
 605		else if (tab_idx == tabs_cnt - 3) // 6 day and older, low boundary is 1 month
 606		{
 607			tab_date =  LLDate::now();
 608			tab_date.split(&curr_year, &curr_month, &curr_day);
 609			curr_month--;
 610			if (0 == curr_month)
 611			{
 612				curr_month = 12;
 613				curr_year--;
 614			}
 615			tab_date.fromYMDHMS(curr_year, curr_month, curr_day);
 616		}
 617		else if (tab_idx == tabs_cnt - 2) // 1 month and older, low boundary is 6 months
 618		{
 619			tab_date =  LLDate::now();
 620			tab_date.split(&curr_year, &curr_month, &curr_day);
 621			if (curr_month > 6)
 622			{
 623				curr_month -= 6;
 624			}
 625			else
 626			{
 627				curr_month += 6;
 628				curr_year--;
 629			}
 630			tab_date.fromYMDHMS(curr_year, curr_month, curr_day);
 631		}
 632		else // 6 months and older
 633		{
 634			tab_date.secondsSinceEpoch(0);
 635		}
 636	}
 637}
 638
 639// Called to add items, no more, than ADD_LIMIT at time
 640void LLTeleportHistoryPanel::refresh()
 641{
 642	if (!mHistoryAccordion)
 643	{
 644		mDirty = false;
 645		return;
 646	}
 647
 648	const LLTeleportHistoryStorage::slurl_list_t& items = mTeleportHistory->getItems();
 649
 650	// Setting tab_boundary_date to "now", so date from any item would be earlier, than boundary.
 651	// That leads to call to getNextTab to get right tab_idx in first pass
 652	LLDate tab_boundary_date =  LLDate::now();
 653
 654	LLFlatListView* curr_flat_view = NULL;
 655	std::string filter_string = sFilterSubString;
 656	LLStringUtil::toUpper(filter_string);
 657
 658	U32 added_items = 0;
 659	while (mCurrentItem >= 0)
 660	{
 661		// Filtering
 662		if (!filter_string.empty())
 663		{
 664			std::string landmark_title(items[mCurrentItem].mTitle);
 665			LLStringUtil::toUpper(landmark_title);
 666			if( std::string::npos == landmark_title.find(filter_string) )
 667			{
 668				mCurrentItem--;
 669				continue;
 670			}
 671		}
 672
 673		// Checking whether date of item is earlier, than tab_boundary_date.
 674		// In that case, item should be added to another tab
 675		const LLDate &date = items[mCurrentItem].mDate;
 676
 677		if (date < tab_boundary_date)
 678		{
 679			// Getting apropriate tab_idx for this and subsequent items,
 680			// tab_boundary_date would be earliest possible date for this tab
 681			S32 tab_idx = 0;
 682			getNextTab(date, tab_idx, tab_boundary_date);
 683			tab_idx = mItemContainers.size() - 1 - tab_idx;
 684			if (tab_idx >= 0)
 685			{
 686				LLAccordionCtrlTab* tab = mItemContainers.get(tab_idx);
 687				tab->setVisible(true);
 688
 689				// Expand all accordion tabs when filtering
 690				if(!sFilterSubString.empty())
 691				{
 692					//store accordion tab state when filter is not empty
 693					tab->notifyChildren(LLSD().with("action","store_state"));
 694				
 695					tab->setDisplayChildren(true);
 696				}
 697				// Restore each tab's expand state when not filtering
 698				else
 699				{
 700					bool collapsed = isAccordionCollapsedByUser(tab);
 701					tab->setDisplayChildren(!collapsed);
 702			
 703					//restore accordion state after all those accodrion tabmanipulations
 704					tab->notifyChildren(LLSD().with("action","restore_state"));
 705				}
 706
 707				curr_flat_view = getFlatListViewFromTab(tab);
 708			}
 709		}
 710
 711		if (curr_flat_view)
 712		{
 713			LLTeleportHistoryFlatItem* item =
 714				LLTeleportHistoryFlatItemStorage::instance()
 715				.getFlatItemForPersistentItem(&mContextMenu,
 716											  items[mCurrentItem],
 717											  mCurrentItem,
 718											  filter_string);
 719			if ( !curr_flat_view->addItem(item, LLUUID::null, ADD_BOTTOM, false) )
 720				llerrs << "Couldn't add flat item to teleport history." << llendl;
 721			if (mLastSelectedItemIndex == mCurrentItem)
 722				curr_flat_view->selectItem(item, true);
 723		}
 724
 725		mCurrentItem--;
 726
 727		if (++added_items >= ADD_LIMIT)
 728			break;
 729	}
 730
 731	for (S32 n = mItemContainers.size() - 1; n >= 0; --n)
 732	{
 733		LLAccordionCtrlTab* tab = mItemContainers.get(n);
 734		LLFlatListView* fv = getFlatListViewFromTab(tab);
 735		if (fv)
 736		{
 737			fv->notify(LLSD().with("rearrange", LLSD()));
 738		}
 739	}
 740
 741	mHistoryAccordion->setFilterSubString(sFilterSubString);
 742
 743	mHistoryAccordion->arrange();
 744
 745	updateVerbs();
 746
 747	if (mCurrentItem < 0)
 748		mDirty = false;
 749}
 750
 751void LLTeleportHistoryPanel::onTeleportHistoryChange(S32 removed_index)
 752{
 753	mLastSelectedItemIndex = -1;
 754
 755	if (-1 == removed_index)
 756		showTeleportHistory(); // recreate all items
 757	else
 758	{
 759		replaceItem(removed_index); // replace removed item by most recent
 760		updateVerbs();
 761	}
 762}
 763
 764void LLTeleportHistoryPanel::replaceItem(S32 removed_index)
 765{
 766	// Flat list for 'Today' (mItemContainers keeps accordion tabs in reverse order)
 767	LLFlatListView* fv = NULL;
 768	
 769	if (mItemContainers.size() > 0)
 770	{
 771		fv = getFlatListViewFromTab(mItemContainers[mItemContainers.size() - 1]);
 772	}
 773
 774	// Empty flat list for 'Today' means that other flat lists are empty as well,
 775	// so all items from teleport history should be added.
 776	if (!fv || fv->size() == 0)
 777	{
 778		showTeleportHistory();
 779		return;
 780	}
 781
 782	const LLTeleportHistoryStorage::slurl_list_t& history_items = mTeleportHistory->getItems();
 783	LLTeleportHistoryFlatItem* item = LLTeleportHistoryFlatItemStorage::instance()
 784		.getFlatItemForPersistentItem(&mContextMenu,
 785									  history_items[history_items.size() - 1], // Most recent item, it was added instead of removed
 786									  history_items.size(), // index will be decremented inside loop below
 787									  sFilterSubString);
 788
 789	fv->addItem(item, LLUUID::null, ADD_TOP);
 790
 791	// Index of each item, from last to removed item should be decremented
 792	// to point to the right item in LLTeleportHistoryStorage
 793	for (S32 tab_idx = mItemContainers.size() - 1; tab_idx >= 0; --tab_idx)
 794	{
 795		LLAccordionCtrlTab* tab = mItemContainers.get(tab_idx);
 796		if (!tab->getVisible())
 797			continue;
 798
 799		fv = getFlatListViewFromTab(tab);
 800		if (!fv)
 801		{
 802			showTeleportHistory();
 803			return;
 804		}
 805
 806		std::vector<LLPanel*> items;
 807		fv->getItems(items);
 808
 809		S32 items_cnt = items.size();
 810		for (S32 n = 0; n < items_cnt; ++n)
 811		{
 812			LLTeleportHistoryFlatItem *item = (LLTeleportHistoryFlatItem*) items[n];
 813
 814			if (item->getIndex() == removed_index)
 815			{
 816				LLTeleportHistoryFlatItemStorage::instance().removeItem(item);
 817
 818				fv->removeItem(item);
 819
 820				// If flat list becames empty, then accordion tab should be hidden
 821				if (fv->size() == 0)
 822					tab->setVisible(false);
 823
 824				mHistoryAccordion->arrange();
 825
 826				return; // No need to decrement idexes for the rest of items
 827			}
 828
 829			item->setIndex(item->getIndex() - 1);
 830		}
 831	}
 832}
 833
 834void LLTeleportHistoryPanel::showTeleportHistory()
 835{
 836	mDirty = true;
 837
 838	// Starting to add items from last one, in reverse order,
 839	// since TeleportHistory keeps most recent item at the end
 840	if (!mTeleportHistory)
 841	{
 842		mTeleportHistory = LLTeleportHistoryStorage::getInstance();
 843	}
 844
 845	mCurrentItem = mTeleportHistory->getItems().size() - 1;
 846
 847	for (S32 n = mItemContainers.size() - 1; n >= 0; --n)
 848	{
 849		LLAccordionCtrlTab* tab = mItemContainers.get(n);
 850		if (tab)
 851		{
 852			tab->setVisible(false);
 853
 854			LLFlatListView* fv = getFlatListViewFromTab(tab);
 855			if (fv)
 856			{
 857				// Detached panels are managed by LLTeleportHistoryFlatItemStorage
 858				std::vector<LLPanel*> detached_items;
 859				fv->detachItems(detached_items);
 860			}
 861		}
 862	}
 863}
 864
 865void LLTeleportHistoryPanel::handleItemSelect(LLFlatListView* selected)
 866{
 867	mLastSelectedFlatlList = selected;
 868	LLTeleportHistoryFlatItem* item = dynamic_cast<LLTeleportHistoryFlatItem *> (mLastSelectedFlatlList->getSelectedItem());
 869	if (item)
 870		mLastSelectedItemIndex = item->getIndex();
 871
 872	S32 tabs_cnt = mItemContainers.size();
 873
 874	for (S32 n = 0; n < tabs_cnt; n++)
 875	{
 876		LLAccordionCtrlTab* tab = mItemContainers.get(n);
 877
 878		if (!tab->getVisible())
 879			continue;
 880
 881		LLFlatListView *flv = getFlatListViewFromTab(tab);
 882		if (!flv)
 883			continue;
 884
 885		if (flv == selected)
 886			continue;
 887
 888		flv->resetSelection(true);
 889	}
 890
 891	updateVerbs();
 892}
 893
 894void LLTeleportHistoryPanel::onReturnKeyPressed()
 895{
 896	// Teleport to selected region as default action on return key pressed
 897	onTeleport();
 898}
 899
 900void LLTeleportHistoryPanel::onDoubleClickItem()
 901{
 902	// If item got doubleclick, then that item is already selected
 903	onTeleport();
 904}
 905
 906void LLTeleportHistoryPanel::onAccordionTabRightClick(LLView *view, S32 x, S32 y, MASK mask)
 907{
 908	LLAccordionCtrlTab *tab = (LLAccordionCtrlTab *) view;
 909
 910	// If click occurred below the header, don't show this menu
 911	if (y < tab->getRect().getHeight() - tab->getHeaderHeight() - tab->getPaddingBottom())
 912		return;
 913
 914	if (mAccordionTabMenu)
 915	{
 916		//preventing parent (menu holder) from deleting already "dead" context menus on exit
 917		LLView* parent = mAccordionTabMenu->getParent();
 918		if (parent)
 919		{
 920			parent->removeChild(mAccordionTabMenu);
 921		}
 922		delete mAccordionTabMenu;
 923	}
 924
 925	// set up the callbacks for all of the avatar menu items
 926	// (N.B. callbacks don't take const refs as mID is local scope)
 927	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 928
 929	registrar.add("TeleportHistory.TabOpen",	boost::bind(&LLTeleportHistoryPanel::onAccordionTabOpen, this, tab));
 930	registrar.add("TeleportHistory.TabClose",	boost::bind(&LLTeleportHistoryPanel::onAccordionTabClose, this, tab));
 931
 932	// create the context menu from the XUI
 933	mAccordionTabMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
 934		"menu_teleport_history_tab.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
 935
 936	mAccordionTabMenu->setItemVisible("TabOpen", !tab->isExpanded() ? true : false);
 937	mAccordionTabMenu->setItemVisible("TabClose", tab->isExpanded() ? true : false);
 938
 939	mAccordionTabMenu->show(x, y);
 940	LLMenuGL::showPopup(tab, mAccordionTabMenu, x, y);
 941}
 942
 943void LLTeleportHistoryPanel::onAccordionTabOpen(LLAccordionCtrlTab *tab)
 944{
 945	tab->setDisplayChildren(true);
 946	mHistoryAccordion->arrange();
 947}
 948
 949void LLTeleportHistoryPanel::onAccordionTabClose(LLAccordionCtrlTab *tab)
 950{
 951	tab->setDisplayChildren(false);
 952	mHistoryAccordion->arrange();
 953}
 954
 955void LLTeleportHistoryPanel::onExpandAllFolders()
 956{
 957	S32 tabs_cnt = mItemContainers.size();
 958
 959	for (S32 n = 0; n < tabs_cnt; n++)
 960	{
 961		mItemContainers.get(n)->setDisplayChildren(true);
 962	}
 963	mHistoryAccordion->arrange();
 964}
 965
 966void LLTeleportHistoryPanel::onCollapseAllFolders()
 967{
 968	S32 tabs_cnt = mItemContainers.size();
 969
 970	for (S32 n = 0; n < tabs_cnt; n++)
 971	{
 972		mItemContainers.get(n)->setDisplayChildren(false);
 973	}
 974	mHistoryAccordion->arrange();
 975}
 976
 977void LLTeleportHistoryPanel::onClearTeleportHistory()
 978{
 979	LLNotificationsUtil::add("ConfirmClearTeleportHistory", LLSD(), LLSD(), boost::bind(&LLTeleportHistoryPanel::onClearTeleportHistoryDialog, this, _1, _2));
 980}
 981
 982bool LLTeleportHistoryPanel::onClearTeleportHistoryDialog(const LLSD& notification, const LLSD& response)
 983{
 984
 985	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 986
 987	if (0 == option)
 988	{
 989		// order does matter, call this first or teleport history will contain one record(current location)
 990		LLTeleportHistory::getInstance()->purgeItems();
 991
 992		LLTeleportHistoryStorage *th = LLTeleportHistoryStorage::getInstance();
 993		th->purgeItems();
 994		th->save();
 995	}
 996
 997	return false;
 998}
 999
1000LLFlatListView* LLTeleportHistoryPanel::getFlatListViewFromTab(LLAccordionCtrlTab *tab)
1001{
1002	for (child_list_const_iter_t iter = tab->beginChild(); iter != tab->endChild(); iter++)
1003	{
1004		if (dynamic_cast<LLFlatListView*>(*iter))
1005		{
1006			return (LLFlatListView*)*iter; // There should be one scroll list per tab.
1007		}
1008	}
1009
1010	return NULL;
1011}
1012
1013bool LLTeleportHistoryPanel::isActionEnabled(const LLSD& userdata) const
1014{
1015	S32 tabs_cnt = mItemContainers.size();
1016
1017	bool has_expanded_tabs = false;
1018	bool has_collapsed_tabs = false;
1019
1020	for (S32 n = 0; n < tabs_cnt; n++)
1021	{
1022		LLAccordionCtrlTab* tab = mItemContainers.get(n);
1023		if (!tab->getVisible())
1024			continue;
1025
1026		if (tab->getDisplayChildren())
1027		{
1028			has_expanded_tabs = true;
1029		}
1030		else
1031		{
1032			has_collapsed_tabs = true;
1033		}
1034
1035		if (has_expanded_tabs && has_collapsed_tabs)
1036		{
1037			break;
1038		}
1039	}
1040
1041	std::string command_name = userdata.asString();
1042
1043	if (has_expanded_tabs && command_name == "collapse_all")
1044	{
1045		return true;
1046	}
1047
1048	if (has_collapsed_tabs && command_name ==  "expand_all")
1049	{
1050		return true;
1051	}
1052
1053	return false;
1054}
1055
1056void LLTeleportHistoryPanel::setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool collapsed)
1057{
1058	LLSD param = acc_tab->getValue();
1059	param[COLLAPSED_BY_USER] = collapsed;
1060	acc_tab->setValue(param);
1061}
1062
1063bool LLTeleportHistoryPanel::isAccordionCollapsedByUser(LLUICtrl* acc_tab)
1064{
1065	LLSD param = acc_tab->getValue();
1066	if(!param.has(COLLAPSED_BY_USER))
1067	{
1068		return false;
1069	}
1070	return param[COLLAPSED_BY_USER].asBoolean();
1071}
1072
1073void LLTeleportHistoryPanel::onAccordionExpand(LLUICtrl* ctrl, const LLSD& param)
1074{
1075	bool expanded = param.asBoolean();
1076	// Save accordion tab state to restore it in refresh()
1077	setAccordionCollapsedByUser(ctrl, !expanded);
1078
1079	// Reset selection upon accordion being collapsed
1080	// to disable "Teleport" and "Map" buttons for hidden item.
1081	if (!expanded && mLastSelectedFlatlList)
1082	{
1083		mLastSelectedFlatlList->resetSelection();
1084	}
1085}
1086
1087// static
1088void LLTeleportHistoryPanel::confirmTeleport(S32 hist_idx)
1089{
1090	LLSD args;
1091	args["HISTORY_ENTRY"] = LLTeleportHistoryStorage::getInstance()->getItems()[hist_idx].mTitle;
1092	LLNotificationsUtil::add("TeleportToHistoryEntry", args, LLSD(),
1093		boost::bind(&LLTeleportHistoryPanel::onTeleportConfirmation, _1, _2, hist_idx));
1094}
1095
1096// Called when user reacts upon teleport confirmation dialog.
1097// static
1098bool LLTeleportHistoryPanel::onTeleportConfirmation(const LLSD& notification, const LLSD& response, S32 hist_idx)
1099{
1100	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
1101
1102	if (0 == option)
1103	{
1104		// Teleport to given history item.
1105		LLTeleportHistoryStorage::getInstance()->goToItem(hist_idx);
1106	}
1107
1108	return false;
1109}