text: /**
   * @file llpaneltopinfobar.cpp
   * @brief Coordinates and Parcel Settings information panel definition
   *
   * $LicenseInfo:firstyear=2010&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 "llpaneltopinfobar.h"
  
  #include "llagent.h"
  #include "llagentui.h"
  #include "llclipboard.h"
  #include "llfloatersidepanelcontainer.h"
  #include "lllandmarkactions.h"
  #include "lllocationinputctrl.h"
  #include "llnotificationsutil.h"
  #include "llparcel.h"
  #include "llslurl.h"
  #include "llstatusbar.h"
  #include "lltrans.h"
  #include "llviewercontrol.h"
  #include "llviewerinventory.h"
  #include "llviewermenu.h"
  #include "llviewerparcelmgr.h"
  #include "llviewerregion.h"
  // [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
  #include "rlvhandler.h"
  // [/RLVa:KB]
  
  #include "llfloaterreg.h"
  
  class LLPanelTopInfoBar::LLParcelChangeObserver : public LLParcelObserver
  {
  public:
  	LLParcelChangeObserver(LLPanelTopInfoBar* topInfoBar) : mTopInfoBar(topInfoBar) {}
  
  private:
  	/*virtual*/ void changed()
  	{
  		if (mTopInfoBar)
  		{
  			mTopInfoBar->updateParcelIcons();
  		}
  	}
  
  	LLPanelTopInfoBar* mTopInfoBar;
  };
  
  LLPanelTopInfoBar::LLPanelTopInfoBar(): mParcelChangedObserver(0)
  {
  	buildFromFile( "panel_topinfo_bar.xml");
  }
  
  LLPanelTopInfoBar::~LLPanelTopInfoBar()
  {
  	if (mParcelChangedObserver)
  	{
  		LLViewerParcelMgr::getInstance()->removeObserver(mParcelChangedObserver);
  		delete mParcelChangedObserver;
  	}
  
  	if (mParcelPropsCtrlConnection.connected())
  	{
  		mParcelPropsCtrlConnection.disconnect();
  	}
  
  	if (mParcelMgrConnection.connected())
  	{
  		mParcelMgrConnection.disconnect();
  	}
  
  	if (mShowCoordsCtrlConnection.connected())
  	{
  		mShowCoordsCtrlConnection.disconnect();
  	}
  }
  
  void LLPanelTopInfoBar::initParcelIcons()
  {
  	mParcelIcon[VOICE_ICON] = getChild<LLIconCtrl>("voice_icon");
  	mParcelIcon[FLY_ICON] = getChild<LLIconCtrl>("fly_icon");
  	mParcelIcon[PUSH_ICON] = getChild<LLIconCtrl>("push_icon");
  	mParcelIcon[BUILD_ICON] = getChild<LLIconCtrl>("build_icon");
  	mParcelIcon[SCRIPTS_ICON] = getChild<LLIconCtrl>("scripts_icon");
  	mParcelIcon[DAMAGE_ICON] = getChild<LLIconCtrl>("damage_icon");
  	mParcelIcon[SEE_AVATARS_ICON] = getChild<LLIconCtrl>("see_avatars_icon");
  
  	mParcelIcon[VOICE_ICON]->setToolTip(LLTrans::getString("LocationCtrlVoiceTooltip"));
  	mParcelIcon[FLY_ICON]->setToolTip(LLTrans::getString("LocationCtrlFlyTooltip"));
  	mParcelIcon[PUSH_ICON]->setToolTip(LLTrans::getString("LocationCtrlPushTooltip"));
  	mParcelIcon[BUILD_ICON]->setToolTip(LLTrans::getString("LocationCtrlBuildTooltip"));
  	mParcelIcon[SCRIPTS_ICON]->setToolTip(LLTrans::getString("LocationCtrlScriptsTooltip"));
  	mParcelIcon[DAMAGE_ICON]->setToolTip(LLTrans::getString("LocationCtrlDamageTooltip"));
  	mParcelIcon[SEE_AVATARS_ICON]->setToolTip(LLTrans::getString("LocationCtrlSeeAVsTooltip"));
  
  	mParcelIcon[VOICE_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, VOICE_ICON));
  	mParcelIcon[FLY_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, FLY_ICON));
  	mParcelIcon[PUSH_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, PUSH_ICON));
  	mParcelIcon[BUILD_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, BUILD_ICON));
  	mParcelIcon[SCRIPTS_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, SCRIPTS_ICON));
  	mParcelIcon[DAMAGE_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, DAMAGE_ICON));
  	mParcelIcon[SEE_AVATARS_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, SEE_AVATARS_ICON));
  
  	mDamageText->setText(LLStringExplicit("100%"));
  }
  
  void LLPanelTopInfoBar::handleLoginComplete()
  {
  	// An agent parcel update hasn't occurred yet, so
  	// we have to manually set location and the icons.
  	update();
  }
  
  BOOL LLPanelTopInfoBar::handleRightMouseDown(S32 x, S32 y, MASK mask)
  {
  	if(!LLUICtrl::CommitCallbackRegistry::getValue("TopInfoBar.Action"))
  	{
  		LLUICtrl::CommitCallbackRegistry::currentRegistrar()
  				.add("TopInfoBar.Action", boost::bind(&LLPanelTopInfoBar::onContextMenuItemClicked, this, _2));
  	}
  	show_topinfobar_context_menu(this, x, y);
  	return TRUE;
  }
  
  BOOL LLPanelTopInfoBar::postBuild()
  {
  	mInfoBtn = getChild<LLButton>("place_info_btn");
  	mInfoBtn->setClickedCallback(boost::bind(&LLPanelTopInfoBar::onInfoButtonClicked, this));
  	mInfoBtn->setToolTip(LLTrans::getString("LocationCtrlInfoBtnTooltip"));
  
  	mParcelInfoText = getChild<LLTextBox>("parcel_info_text");
  	mDamageText = getChild<LLTextBox>("damage_text");
  
  	initParcelIcons();
  
  	mParcelChangedObserver = new LLParcelChangeObserver(this);
  	LLViewerParcelMgr::getInstance()->addObserver(mParcelChangedObserver);
  
  	// Connecting signal for updating parcel icons on "Show Parcel Properties" setting change.
  	LLControlVariable* ctrl = gSavedSettings.getControl("NavBarShowParcelProperties").get();
  	if (ctrl)
  	{
  		mParcelPropsCtrlConnection = ctrl->getSignal()->connect(boost::bind(&LLPanelTopInfoBar::updateParcelIcons, this));
  	}
  
  	// Connecting signal for updating parcel text on "Show Coordinates" setting change.
  	ctrl = gSavedSettings.getControl("NavBarShowCoordinates").get();
  	if (ctrl)
  	{
  		mShowCoordsCtrlConnection = ctrl->getSignal()->connect(boost::bind(&LLPanelTopInfoBar::onNavBarShowParcelPropertiesCtrlChanged, this));
  	}
  
  	mParcelMgrConnection = gAgent.addParcelChangedCallback(
  			boost::bind(&LLPanelTopInfoBar::onAgentParcelChange, this));
  
  	setVisibleCallback(boost::bind(&LLPanelTopInfoBar::onVisibilityChanged, this, _2));
  
  	return TRUE;
  }
  
  void LLPanelTopInfoBar::onNavBarShowParcelPropertiesCtrlChanged()
  {
  	std::string new_text;
  
  	// don't need to have separate show_coords variable; if user requested the coords to be shown
  	// they will be added during the next call to the draw() method.
  	buildLocationString(new_text, false);
  	setParcelInfoText(new_text);
  }
  
  // when panel is shown, all minimized floaters should be shifted downwards to prevent overlapping of
  // PanelTopInfoBar. See EXT-7951.
  void LLPanelTopInfoBar::onVisibilityChanged(const LLSD& show)
  {
  	// this height is used as a vertical offset for ALREADY MINIMIZED floaters
  	// when PanelTopInfoBar visibility changes
  	S32 height = getRect().getHeight();
  
  	// this vertical offset is used for a start minimize position of floaters that
  	// are NOT MIMIMIZED YET
  	S32 minimize_pos_offset = 0;
  
  	if (show.asBoolean())
  	{
  		height = minimize_pos_offset = -height;
  	}
  
  	gFloaterView->shiftFloaters(0, height);
  	gFloaterView->setMinimizePositionVerticalOffset(minimize_pos_offset);
  }
  
  boost::signals2::connection LLPanelTopInfoBar::setResizeCallback( const resize_signal_t::slot_type& cb )
  {
  	return mResizeSignal.connect(cb);
  }
  
  void LLPanelTopInfoBar::draw()
  {
  	updateParcelInfoText();
  	updateHealth();
  
  	LLPanel::draw();
  }
  
  void LLPanelTopInfoBar::buildLocationString(std::string& loc_str, bool show_coords)
  {
  	LLAgentUI::ELocationFormat format =
  		(show_coords ? LLAgentUI::LOCATION_FORMAT_FULL : LLAgentUI::LOCATION_FORMAT_NO_COORDS);
  
  	if (!LLAgentUI::buildLocationString(loc_str, format))
  	{
  		loc_str = "???";
  	}
  }
  
  void LLPanelTopInfoBar::setParcelInfoText(const std::string& new_text)
  {
  	LLRect old_rect = getRect();
  	const LLFontGL* font = mParcelInfoText->getFont();
  	S32 new_text_width = font->getWidth(new_text);
  
  	//<FS:TS> Avoid processing the parcel string every frame if it
  	// hasn't changed.
  	static std::string old_text = "";
  	if (new_text == old_text)
  	{
  		return;
  	}
  	old_text = new_text;
  	//</FS:TS>
  
  	mParcelInfoText->setText(new_text);
  
  	LLRect rect = mParcelInfoText->getRect();
  	rect.setOriginAndSize(rect.mLeft, rect.mBottom, new_text_width, rect.getHeight());
  
  	mParcelInfoText->reshape(rect.getWidth(), rect.getHeight(), TRUE);
  	mParcelInfoText->setRect(rect);
  	layoutParcelIcons();
  
  	if (old_rect != getRect())
  	{
  		mResizeSignal();
  	}
  }
  
  void LLPanelTopInfoBar::update()
  {
  	std::string new_text;
  
  	// don't need to have separate show_coords variable; if user requested the coords to be shown
  	// they will be added during the next call to the draw() method.
  	buildLocationString(new_text, false);
  	setParcelInfoText(new_text);
  
  	updateParcelIcons();
  }
  
  void LLPanelTopInfoBar::updateParcelInfoText()
  {
  	static LLUICachedControl<bool> show_coords("NavBarShowCoordinates", false);
  
  	if (show_coords)
  	{
  		std::string new_text;
  
  		buildLocationString(new_text, show_coords);
  		setParcelInfoText(new_text);
  	}
  }
  
  void LLPanelTopInfoBar::updateParcelIcons()
  {
  	LLViewerParcelMgr* vpm = LLViewerParcelMgr::getInstance();
  
  	LLViewerRegion* agent_region = gAgent.getRegion();
  	LLParcel* agent_parcel = vpm->getAgentParcel();
  	if (!agent_region || !agent_parcel)
  		return;
  
  	if (gSavedSettings.getBOOL("NavBarShowParcelProperties"))
  	{
  		LLParcel* current_parcel;
  		LLViewerRegion* selection_region = vpm->getSelectionRegion();
  		LLParcel* selected_parcel = vpm->getParcelSelection()->getParcel();
  
  		// If agent is in selected parcel we use its properties because
  		// they are updated more often by LLViewerParcelMgr than agent parcel properties.
  		// See LLViewerParcelMgr::processParcelProperties().
  		// This is needed to reflect parcel restrictions changes without having to leave
  		// the parcel and then enter it again. See EXT-2987
  		if (selected_parcel && selected_parcel->getLocalID() == agent_parcel->getLocalID()
  				&& selection_region == agent_region)
  		{
  			current_parcel = selected_parcel;
  		}
  		else
  		{
  			current_parcel = agent_parcel;
  		}
  
  		bool allow_voice	= vpm->allowAgentVoice(agent_region, current_parcel);
  		bool allow_fly		= vpm->allowAgentFly(agent_region, current_parcel);
  		bool allow_push		= vpm->allowAgentPush(agent_region, current_parcel);
  		bool allow_build	= vpm->allowAgentBuild(current_parcel); // true when anyone is allowed to build. See EXT-4610.
  		bool allow_scripts	= vpm->allowAgentScripts(agent_region, current_parcel);
  		bool allow_damage	= vpm->allowAgentDamage(agent_region, current_parcel);
  		bool see_avs        = current_parcel->getSeeAVs();
  
  		// Most icons are "block this ability"
  		mParcelIcon[VOICE_ICON]->setVisible(   !allow_voice );
  		mParcelIcon[FLY_ICON]->setVisible(     !allow_fly );
  		mParcelIcon[PUSH_ICON]->setVisible(    !allow_push );
  		mParcelIcon[BUILD_ICON]->setVisible(   !allow_build );
  		mParcelIcon[SCRIPTS_ICON]->setVisible( !allow_scripts );
  		mParcelIcon[DAMAGE_ICON]->setVisible(  allow_damage );
  		mDamageText->setVisible(allow_damage);
  		mParcelIcon[SEE_AVATARS_ICON]->setVisible( !see_avs );
  
  		layoutParcelIcons();
  	}
  	else
  	{
  		for (S32 i = 0; i < ICON_COUNT; ++i)
  		{
  			mParcelIcon[i]->setVisible(false);
  		}
  		mDamageText->setVisible(false);
  	}
  }
  
  void LLPanelTopInfoBar::updateHealth()
  {
  	static LLUICachedControl<bool> show_icons("NavBarShowParcelProperties", false);
  
  	// *FIXME: Status bar owns health information, should be in agent
  	if (show_icons && gStatusBar)
  	{
  		static S32 last_health = -1;
  		S32 health = gStatusBar->getHealth();
  		if (health != last_health)
  		{
  			std::string text = llformat("%d%%", health);
  			mDamageText->setText(text);
  			last_health = health;
  		}
  	}
  }
  
  void LLPanelTopInfoBar::layoutParcelIcons()
  {
  	LLRect old_rect = getRect();
  
  	// TODO: remove hard-coded values and read them as xml parameters
  	static const int FIRST_ICON_HPAD = 32;
  	static const int LAST_ICON_HPAD = 11;
  
  	S32 left = mParcelInfoText->getRect().mRight + FIRST_ICON_HPAD;
  
  	left = layoutWidget(mDamageText, left);
  
  	for (int i = ICON_COUNT - 1; i >= 0; --i)
  	{
  		left = layoutWidget(mParcelIcon[i], left);
  	}
  
  	LLRect rect = getRect();
  	rect.set(rect.mLeft, rect.mTop, left + LAST_ICON_HPAD, rect.mBottom);
  	setRect(rect);
  
  	if (old_rect != getRect())
  	{
  		mResizeSignal();
  	}
  }
  
  S32 LLPanelTopInfoBar::layoutWidget(LLUICtrl* ctrl, S32 left)
  {
  	// TODO: remove hard-coded values and read them as xml parameters
  	static const int ICON_HPAD = 2;
  
  	if (ctrl->getVisible())
  	{
  		LLRect rect = ctrl->getRect();
  		rect.mRight = left + rect.getWidth();
  		rect.mLeft = left;
  
  		ctrl->setRect(rect);
  		left += rect.getWidth() + ICON_HPAD;
  	}
  
  	return left;
  }
  
  void LLPanelTopInfoBar::onParcelIconClick(EParcelIcon icon)
  {
  	switch (icon)
  	{
  	case VOICE_ICON:
  		LLNotificationsUtil::add("NoVoice");
  		break;
  	case FLY_ICON:
  		LLNotificationsUtil::add("NoFly");
  		break;
  	case PUSH_ICON:
  		LLNotificationsUtil::add("PushRestricted");
  		break;
  	case BUILD_ICON:
  		LLNotificationsUtil::add("NoBuild");
  		break;
  	case SCRIPTS_ICON:
  	{
  		LLViewerRegion* region = gAgent.getRegion();
  		if(region && region->getRegionFlag(REGION_FLAGS_ESTATE_SKIP_SCRIPTS))
  		{
  			LLNotificationsUtil::add("ScriptsStopped");
  		}
  		else if(region && region->getRegionFlag(REGION_FLAGS_SKIP_SCRIPTS))
  		{
  			LLNotificationsUtil::add("ScriptsNotRunning");
  		}
  		else
  		{
  			LLNotificationsUtil::add("NoOutsideScripts");
  		}
  		break;
  	}
  	case DAMAGE_ICON:
  		LLNotificationsUtil::add("NotSafe");
  		break;
  	case SEE_AVATARS_ICON:
  		LLNotificationsUtil::add("SeeAvatars");
  		break;
  	case ICON_COUNT:
  		break;
  	// no default to get compiler warning when a new icon gets added
  	}
  }
  
  void LLPanelTopInfoBar::onAgentParcelChange()
  {
  	update();
  }
  
  void LLPanelTopInfoBar::onContextMenuItemClicked(const LLSD::String& item)
  {
  	if (item == "landmark")
  	{
  // [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
  		if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
  		{
  // [/RLVa:KB]
  			LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos();
  
  			if(landmark == NULL)
  			{
  				// <FS:Ansariel> FIRE-817: Separate place details floater
  				//LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "create_landmark"));
  				if (gSavedSettings.getBOOL("FSUseStandalonePlaceDetailsFloater"))
  				{
  					LLFloaterReg::showInstance("fs_placedetails", LLSD().with("type", "create_landmark"));
  				}
  				else
  				{
  					LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "create_landmark"));
  				}
  				// </FS:Ansariel>
  			}
  			else
  			{
  				// <FS:Ansariel> FIRE-817: Separate place details floater
  				//LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "landmark").with("id",landmark->getUUID()));
  				if (gSavedSettings.getBOOL("FSUseStandalonePlaceDetailsFloater"))
  				{
  					LLFloaterReg::showInstance("fs_placedetails", LLSD().with("type", "landmark").with("id",landmark->getUUID()));
  				}
  				else
  				{
  					LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "landmark").with("id",landmark->getUUID()));
  				}
  				// </FS:Ansariel>
  			}
  // [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
  		}
  // [/RLVa:KB]
  	}
  	else if (item == "copy")
  	{
  // [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
  		if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
  		{
  // [/RLVa:KB]
  			LLSLURL slurl;
  			LLAgentUI::buildSLURL(slurl, false);
  			LLUIString location_str(slurl.getSLURLString());
  
  			LLClipboard::instance().copyToClipboard(location_str,0,location_str.length());
  // [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
  		}
  // [/RLVa:KB]
  	}
  }
  
  void LLPanelTopInfoBar::onInfoButtonClicked()
  {
  // [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
  	if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
  		return;
  // [/RLVa:KB]
  
  	// <FS:Ansariel> FIRE-817: Separate place details floater
  	//LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "agent"));
  	//if (gSavedSettings.getBOOL("FSUseStandalonePlaceDetailsFloater"))
  	//{
  	//	LLFloaterReg::showInstance("fs_placedetails", LLSD().with("type", "agent"));
  	//}
  	//else
  	//{
  	//	LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "agent"));
  	//}
  	// </FS:Ansariel>
  	LLFloaterReg::showInstance("about_land");
  }
  