diff options
| author | Steve Bennetts <steve@lindenlab.com> | 2009-12-02 10:50:17 -0800 | 
|---|---|---|
| committer | Steve Bennetts <steve@lindenlab.com> | 2009-12-02 10:50:17 -0800 | 
| commit | d550339958c14f0d7fd222a8531df639eac49e63 (patch) | |
| tree | 2adfe920202a4304fc37446fca5f9bd368995749 | |
| parent | 6e1f6f015394a3f268608061c00993939c891e54 (diff) | |
| parent | 3c8f89246712d1e5391ab1bdd6bbc36a83f210d8 (diff) | |
Merge from product-engine
56 files changed, 1369 insertions, 356 deletions
| diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index c027aa7bdd..5f3d9d6582 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -985,6 +985,15 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,  		return true;  	}  	replacement = datetime.toHTTPDateString(code); + +	// *HACK: delete leading zero from hour string in case 'hour12' (code = %I) time format +	// to show time without leading zero, e.g. 08:16 -> 8:16 (EXT-2738). +	// We could have used '%l' format instead, but it's not supported by Windows. +	if(code == "%I" && token == "hour12" && replacement.at(0) == '0') +	{ +		replacement = replacement.at(1); +	} +  	return !code.empty();  } diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 36a9e0a650..a63187678e 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1452,6 +1452,7 @@ void LLFloater::onClickTearOff(LLFloater* self)  		gFloaterView->adjustToFitScreen(self, FALSE);  		// give focus to new window to keep continuity for the user  		self->setFocus(TRUE); +		self->setTornOff(true);  	}  	else  //Attach to parent.  	{ @@ -1463,6 +1464,7 @@ void LLFloater::onClickTearOff(LLFloater* self)  			// make sure host is visible  			new_host->openFloater(new_host->getKey());  		} +		self->setTornOff(false);  	}  } diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index d7ec0aac00..b5c835cb47 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -259,6 +259,8 @@ public:  	bool            isDocked() const { return mDocked; }  	virtual void    setDocked(bool docked, bool pop_on_undock = true); +	virtual void    setTornOff(bool torn_off) {} +  	// Return a closeable floater, if any, given the current focus.  	static LLFloater* getClosableFloaterFromFocus();  diff --git a/indra/media_plugins/gstreamer010/CMakeLists.txt b/indra/media_plugins/gstreamer010/CMakeLists.txt index a9f7938b41..3b73e04786 100644 --- a/indra/media_plugins/gstreamer010/CMakeLists.txt +++ b/indra/media_plugins/gstreamer010/CMakeLists.txt @@ -42,12 +42,12 @@ set(media_plugin_gstreamer010_HEADER_FILES      llmediaimplgstreamertriviallogging.h      ) -if (${CXX_VERSION} MATCHES "4[23].") +if (${CXX_VERSION_NUMBER} MATCHES "4[23].")      # Work around a bad interaction between broken gstreamer headers and      # g++ 4.3's increased strictness.      set_source_files_properties(llmediaimplgstreamervidplug.cpp PROPERTIES                                  COMPILE_FLAGS -Wno-write-strings) -endif (${CXX_VERSION} MATCHES "4[23].") +endif (${CXX_VERSION_NUMBER} MATCHES "4[23].")  add_library(media_plugin_gstreamer010      SHARED diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 63e17058e8..6de98642b1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1385,6 +1385,17 @@        <key>Value</key>        <integer>1</integer>      </map> +    <key>ChatWindow</key> +    <map> +      <key>Comment</key> +      <string>Show chat in multiple windows(by default) or in one multi-tabbed window(requires restart)</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>S32</string> +      <key>Value</key> +      <integer>0</integer> +    </map>      <key>CheesyBeacon</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index 8f3eba98a6..44cbbbb6b2 100644 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -303,5 +303,9 @@ void LLAvatarIconCtrl::nameUpdatedCallback(  		{  			setToolTip(mFirstName + " " + mLastName);  		} +		else +		{ +			setToolTip(std::string("")); +		}  	}  } diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 3bee5c353f..59ed391c06 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -42,7 +42,7 @@  #include "llavatariconctrl.h"  #include "llbutton.h" -LLAvatarListItem::LLAvatarListItem() +LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/)  :	LLPanel(),  	mAvatarIcon(NULL),  	mAvatarName(NULL), @@ -55,14 +55,12 @@ LLAvatarListItem::LLAvatarListItem()  	mShowInfoBtn(true),  	mShowProfileBtn(true)  { -	LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml"); -	// Remember avatar icon width including its padding from the name text box, -	// so that we can hide and show the icon again later. - -	mIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft; -	mInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight; -	mProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight; -	mSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight;  +	if (not_from_ui_factory) +	{ +		LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml"); +	} +	// *NOTE: mantipov: do not use any member here. They can be uninitialized here in case instance +	// is created from the UICtrlFactory  }  LLAvatarListItem::~LLAvatarListItem() @@ -87,6 +85,13 @@ BOOL  LLAvatarListItem::postBuild()  	mProfileBtn->setVisible(false);  	mProfileBtn->setClickedCallback(boost::bind(&LLAvatarListItem::onProfileBtnClick, this)); +	// Remember avatar icon width including its padding from the name text box, +	// so that we can hide and show the icon again later. +	mIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft; +	mInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight; +	mProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight; +	mSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight;  +  /*  	if(!p.buttons.profile)  	{ diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index 341f5a6bcf..a7b080098d 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -51,7 +51,16 @@ public:  		virtual void show(LLView* spawning_view, const std::vector<LLUUID>& selected_uuids, S32 x, S32 y) = 0;  	}; -	LLAvatarListItem(); +	/** +	 * Creates an instance of LLAvatarListItem. +	 * +	 * It is not registered with LLDefaultChildRegistry. It is built via LLUICtrlFactory::buildPanel +	 * or via registered LLCallbackMap depend on passed parameter. +	 *  +	 * @param not_from_ui_factory if true instance will be build with LLUICtrlFactory::buildPanel  +	 * otherwise it should be registered via LLCallbackMap before creating. +	 */ +	LLAvatarListItem(bool not_from_ui_factory = true);  	virtual ~LLAvatarListItem();  	virtual BOOL postBuild(); @@ -82,8 +91,19 @@ public:  	void setContextMenu(ContextMenu* menu) { mContextMenu = menu; } +	/** +	 * This method was added to fix EXT-2364 (Items in group/ad-hoc IM participant list (avatar names) should be reshaped when adding/removing the "(Moderator)" label) +	 * But this is a *HACK. The real reason of it was in incorrect logic while hiding profile/info/speaker buttons +	 * *TODO: new reshape method should be provided in lieu of this one to be called when visibility if those buttons is changed +	 */  	void reshapeAvatarName(); +protected: +	/** +	 * Contains indicator to show voice activity.  +	 */ +	LLOutputMonitorCtrl* mSpeakingIndicator; +  private:  	typedef enum e_online_status { @@ -100,7 +120,6 @@ private:  	LLTextBox* mAvatarName;  	LLTextBox* mLastInteractionTime; -	LLOutputMonitorCtrl* mSpeakingIndicator;  	LLButton* mInfoBtn;  	LLButton* mProfileBtn;  	ContextMenu* mContextMenu; diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index c44b0b5331..96c06b1665 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -47,6 +47,7 @@  #include "llsplitbutton.h"  #include "llsyswellwindow.h"  #include "llfloatercamera.h" +#include "lltexteditor.h"  // Build time optimization, generate extern template once in .cpp file  template class LLBottomTray* LLSingleton<class LLBottomTray>::getInstance(); @@ -259,7 +260,9 @@ void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask)  	// We should show BottomTrayContextMenu in last  turn  	if (mBottomTrayContextMenu && !LLMenuGL::sMenuContainer->getVisibleMenu())  	{ -		//there are no other context menu (IM chiclet etc ), so we can show BottomTrayContextMenu +		    //there are no other context menu (IM chiclet etc ), so we can show BottomTrayContextMenu + +		    updateContextMenu(x, y, mask);  			mBottomTrayContextMenu->buildDrawLabels();  			mBottomTrayContextMenu->updateParent(LLMenuGL::sMenuContainer);  			LLMenuGL::showPopup(this, mBottomTrayContextMenu, x, y); @@ -267,6 +270,33 @@ void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask)  	}  } +void LLBottomTray::updateContextMenu(S32 x, S32 y, MASK mask) +{ +	LLUICtrl* edit_box = mNearbyChatBar->getChild<LLUICtrl>("chat_box"); + +	S32 local_x = x - mNearbyChatBar->getRect().mLeft - edit_box->getRect().mLeft; +	S32 local_y = y - mNearbyChatBar->getRect().mBottom - edit_box->getRect().mBottom; + +	bool in_edit_box = edit_box->pointInView(local_x, local_y); + +	LLMenuItemGL* menu_item; +	menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Cut"); +	if(menu_item) +		menu_item->setVisible(in_edit_box); +	menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Copy"); +	if(menu_item) +		menu_item->setVisible(in_edit_box); +	menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Paste"); +	if(menu_item) +		menu_item->setVisible(in_edit_box); +	menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Delete"); +	if(menu_item) +		menu_item->setVisible(in_edit_box); +	menu_item = mBottomTrayContextMenu->findChild<LLMenuItemGL>("NearbyChatBar_Select_All"); +	if(menu_item) +		menu_item->setVisible(in_edit_box); +} +  void LLBottomTray::showGestureButton(BOOL visible)  {  	setTrayButtonVisibleIfPossible(RS_BUTTON_GESTURES, visible); @@ -298,9 +328,14 @@ namespace  BOOL LLBottomTray::postBuild()  { + +	LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("NearbyChatBar.Action", boost::bind(&LLBottomTray::onContextMenuItemClicked, this, _2)); +	LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("NearbyChatBar.EnableMenuItem", boost::bind(&LLBottomTray::onContextMenuItemEnabled, this, _2)); +  	mBottomTrayContextMenu =  LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_bottomtray.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());  	gMenuHolder->addChild(mBottomTrayContextMenu); +  	mNearbyChatBar = getChild<LLNearbyChatBar>("chat_bar");  	mToolbarStack = getChild<LLLayoutStack>("toolbar_stack");  	mMovementPanel = getChild<LLPanel>("movement_panel"); @@ -333,6 +368,62 @@ BOOL LLBottomTray::postBuild()  	return TRUE;  } +bool LLBottomTray::onContextMenuItemEnabled(const LLSD& userdata) +{ +	std::string item = userdata.asString(); +	LLLineEditor* edit_box = mNearbyChatBar->findChild<LLLineEditor>("chat_box"); +	 +	if (item == "can_cut") +	{ +		return edit_box->canCut(); +	} +	else if (item == "can_copy") +	{ +		return edit_box->canCopy(); +	} +	else if (item == "can_paste") +	{ +		return edit_box->canPaste(); +	} +	else if (item == "can_delete") +	{ +		return edit_box->canDoDelete(); +	} +	else if (item == "can_select_all") +	{ +		return edit_box->canSelectAll(); +	} +	return true; +} + + +void LLBottomTray::onContextMenuItemClicked(const LLSD& userdata) +{ +	std::string item = userdata.asString(); +	LLLineEditor* edit_box = mNearbyChatBar->findChild<LLLineEditor>("chat_box"); + +	if (item == "cut") +	{ +		edit_box->cut(); +	} +	else if (item == "copy") +	{ +		edit_box->copy(); +	} +	else if (item == "paste") +	{ +		edit_box->paste(); +	} +	else if (item == "delete") +	{ +		edit_box->doDelete(); +	} +	else if (item == "select_all") +	{ +		edit_box->selectAll(); +	} +} +  void LLBottomTray::log(LLView* panel, const std::string& descr)  {  	if (NULL == panel) return; diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 728a420324..91ec01654b 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -179,6 +179,10 @@ protected:  	static void* createNearbyChatBar(void* userdata); +	void updateContextMenu(S32 x, S32 y, MASK mask); +	void onContextMenuItemClicked(const LLSD& userdata); +	bool onContextMenuItemEnabled(const LLSD& userdata); +  	/**  	 * Creates IM Chiclet based on session type (IM chat or Group chat)  	 */ diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 1b929eca0e..ad59c780f3 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -42,18 +42,50 @@  #include "llspeakers.h" +class LLNonAvatarCaller : public LLAvatarListItem +{ +public: +	LLNonAvatarCaller() : LLAvatarListItem(false) +	{ + +	} +	BOOL postBuild() +	{ +		BOOL rv = LLAvatarListItem::postBuild(); + +		if (rv) +		{ +			setOnline(true); +			showLastInteractionTime(false); +			setShowProfileBtn(false); +			setShowInfoBtn(false); +		} +		return rv; +	} + +	void setSpeakerId(const LLUUID& id) { mSpeakingIndicator->setSpeakerId(id); } +}; + + +static void* create_non_avatar_caller(void*) +{ +	return new LLNonAvatarCaller; +} +  LLCallFloater::LLCallFloater(const LLSD& key)  : LLDockableFloater(NULL, key)  , mSpeakerManager(NULL)  , mPaticipants(NULL)  , mAvatarList(NULL) +, mNonAvatarCaller(NULL)  , mVoiceType(VC_LOCAL_CHAT)  { - +	mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);  }  LLCallFloater::~LLCallFloater()  { +	mChannelChangedConnection.disconnect();  	delete mPaticipants;  	mPaticipants = NULL;  } @@ -63,7 +95,9 @@ BOOL LLCallFloater::postBuild()  {  	LLDockableFloater::postBuild();  	mAvatarList = getChild<LLAvatarList>("speakers_list"); +	childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this)); +	mNonAvatarCaller = getChild<LLNonAvatarCaller>("non_avatar_caller");  	LLView *anchor_panel = LLBottomTray::getInstance()->getChild<LLView>("speak_panel"); @@ -77,7 +111,7 @@ BOOL LLCallFloater::postBuild()  	updateSession();  	// subscribe to to be notified Voice Channel is changed -	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::onCurrentChannelChanged, this, _1)); +	mChannelChangedConnection = LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::onCurrentChannelChanged, this, _1));  	return TRUE;  } @@ -89,6 +123,16 @@ void LLCallFloater::onOpen(const LLSD& /*key*/)  //////////////////////////////////////////////////////////////////////////  /// PRIVATE SECTION  ////////////////////////////////////////////////////////////////////////// + +void LLCallFloater::leaveCall() +{ +	LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel(); +	if (voice_channel && voice_channel->isActive()) +	{ +		voice_channel->deactivate(); +	} +} +  void LLCallFloater::updateSession()  {  	LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel(); @@ -138,25 +182,51 @@ void LLCallFloater::updateSession()  	}  	updateTitle(); +	 +	//hide "Leave Call" button for nearby chat +	bool is_local_chat = mVoiceType == VC_LOCAL_CHAT; +	childSetVisible("leave_btn_panel", !is_local_chat); +	  	refreshPartisipantList();  }  void LLCallFloater::refreshPartisipantList()  {  	delete mPaticipants; +	mPaticipants = NULL;  	mAvatarList->clear(); -	bool do_not_use_context_menu_in_local_chat = LLLocalSpeakerMgr::getInstance() != mSpeakerManager; -	mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList, do_not_use_context_menu_in_local_chat); +	bool non_avatar_caller = false; +	if (VC_PEER_TO_PEER == mVoiceType) +	{ +		LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSpeakerManager->getSessionID()); +		non_avatar_caller = !session->mOtherParticipantIsAvatar; +		if (non_avatar_caller) +		{ +			mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID); +			mNonAvatarCaller->setName(session->mName); +		} +	} + +	mNonAvatarCaller->setVisible(non_avatar_caller); +	mAvatarList->setVisible(!non_avatar_caller); -	if (!do_not_use_context_menu_in_local_chat) +	if (!non_avatar_caller)  	{ -		mAvatarList->setNoItemsCommentText(getString("no_one_near")); +		bool do_not_use_context_menu_in_local_chat = LLLocalSpeakerMgr::getInstance() != mSpeakerManager; +		mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList, do_not_use_context_menu_in_local_chat); + +		if (!do_not_use_context_menu_in_local_chat) +		{ +			mAvatarList->setNoItemsCommentText(getString("no_one_near")); +		}  	}  }  void LLCallFloater::onCurrentChannelChanged(const LLUUID& /*session_id*/)  { +	// Forget speaker manager from the previous session to avoid using it after session was destroyed. +	mSpeakerManager = NULL;  	updateSession();  } @@ -170,15 +240,21 @@ void LLCallFloater::updateTitle()  		title = getString("title_nearby");  		break;  	case VC_PEER_TO_PEER: -		title = voice_channel->getSessionName(); +		{ +			LLStringUtil::format_map_t args; +			args["[NAME]"] = voice_channel->getSessionName(); +			title = getString("title_peer_2_peer", args); +		}  		break;  	case VC_AD_HOC_CHAT:  		title = getString("title_adhoc");  		break;  	case VC_GROUP_CHAT: -		LLStringUtil::format_map_t args; -		args["[GROUP]"] = voice_channel->getSessionName(); -		title = getString("title_group", args); +		{ +			LLStringUtil::format_map_t args; +			args["[GROUP]"] = voice_channel->getSessionName(); +			title = getString("title_group", args); +		}  		break;  	} diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 8a440873ff..b615f57d5b 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -37,6 +37,7 @@  #include "lldockablefloater.h"  class LLAvatarList; +class LLNonAvatarCaller;  class LLParticipantList;  class LLSpeakerMgr; @@ -48,8 +49,8 @@ class LLSpeakerMgr;   * the Resident's own microphone input volume, the audible volume of each of the other participants,   * the Resident's own Voice Morphing settings (if she has subscribed to enable the feature), and Voice Recording.   * - * When the Resident is engaged in Group Voice Chat, the Voice Control Panel also provides an  - * 'End Call' button to allow the Resident to leave that voice channel. + * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel also provides an  + * 'Leave Call' button to allow the Resident to leave that voice channel.   */  class LLCallFloater : public LLDockableFloater  { @@ -69,6 +70,8 @@ private:  		VC_PEER_TO_PEER  	}EVoiceControls; +	void leaveCall(); +  	/**  	 * Updates mSpeakerManager and list according to current Voice Channel  	 * @@ -89,7 +92,10 @@ private:  	LLSpeakerMgr* mSpeakerManager;  	LLParticipantList* mPaticipants;  	LLAvatarList* mAvatarList; +	LLNonAvatarCaller* mNonAvatarCaller;  	EVoiceControls mVoiceType; + +	boost::signals2::connection mChannelChangedConnection;  }; diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 6e1bb961a5..5e17770314 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -267,30 +267,13 @@ protected:  	}  private: -	std::string appendTime(const LLChat& chat) -	{ -		time_t utc_time; -		utc_time = time_corrected(); -		std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" -			+LLTrans::getString("TimeMin")+"] "; - -		LLSD substitution; - -		substitution["datetime"] = (S32) utc_time; -		LLStringUtil::format (timeStr, substitution); - -		return timeStr; -	} -  	void setTimeField(const LLChat& chat)  	{  		LLTextBox* time_box = getChild<LLTextBox>("time_box");  		LLRect rect_before = time_box->getRect(); -		std::string time_value = appendTime(chat); - -		time_box->setValue(time_value); +		time_box->setValue(chat.mTimeStr);  		// set necessary textbox width to fit all text  		time_box->reshapeToFitText(); diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 9aef02c5c8..588855d088 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -595,8 +595,49 @@ void LLAdHocChiclet::setCounter(S32 counter)  	setShowNewMessagesIcon(counter);  } +void LLAdHocChiclet::createPopupMenu() +{ +	if(mPopupMenu) +	{ +		llwarns << "Menu already exists" << llendl; +		return; +	} +	if(getSessionId().isNull()) +	{ +		return; +	} + +	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; +	registrar.add("IMChicletMenu.Action", boost::bind(&LLAdHocChiclet::onMenuItemClicked, this, _2)); + +	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL> +		("menu_imchiclet_adhoc.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +} + +void LLAdHocChiclet::onMenuItemClicked(const LLSD& user_data) +{ +	std::string level = user_data.asString(); +	LLUUID group_id = getSessionId(); + +	if("end" == level) +	{ +		LLGroupActions::endIM(group_id); +	} +} +  BOOL LLAdHocChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)  { +	if(!mPopupMenu) +	{ +		createPopupMenu(); +	} + +	if (mPopupMenu) +	{ +		mPopupMenu->arrangeAndClear(); +		LLMenuGL::showPopup(this, mPopupMenu, x, y); +	} +  	return TRUE;  } @@ -1415,6 +1456,28 @@ void LLChicletGroupIconCtrl::setValue(const LLSD& value )  //////////////////////////////////////////////////////////////////////////  ////////////////////////////////////////////////////////////////////////// +LLChicletInvOfferIconCtrl::LLChicletInvOfferIconCtrl(const Params& p) +: LLChicletAvatarIconCtrl(p) + , mDefaultIcon(p.default_icon) +{ +} + +void LLChicletInvOfferIconCtrl::setValue(const LLSD& value ) +{ +	if(value.asUUID().isNull()) +	{ +		LLIconCtrl::setValue(mDefaultIcon); +	} +	else +	{ +		LLChicletAvatarIconCtrl::setValue(value); +	} +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +  LLChicletSpeakerCtrl::LLChicletSpeakerCtrl(const Params&p)   : LLOutputMonitorCtrl(p)  { @@ -1466,4 +1529,60 @@ BOOL LLScriptChiclet::handleMouseDown(S32 x, S32 y, MASK mask)  	return LLChiclet::handleMouseDown(x, y, mask);  } +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +static const std::string INVENTORY_USER_OFFER	("UserGiveItem"); + +LLInvOfferChiclet::Params::Params() +{ +	// *TODO Vadim: Get rid of hardcoded values. +	rect(CHICLET_RECT); +	icon.rect(CHICLET_ICON_RECT); +} + +LLInvOfferChiclet::LLInvOfferChiclet(const Params&p) + : LLIMChiclet(p) + , mChicletIconCtrl(NULL) +{ +	LLChicletInvOfferIconCtrl::Params icon_params = p.icon; +	mChicletIconCtrl = LLUICtrlFactory::create<LLChicletInvOfferIconCtrl>(icon_params); +	// Let "new message" icon be on top, else it will be hidden behind chiclet icon. +	addChildInBack(mChicletIconCtrl); +} + +void LLInvOfferChiclet::setSessionId(const LLUUID& session_id) +{ +	setShowNewMessagesIcon( getSessionId() != session_id ); + +	LLIMChiclet::setSessionId(session_id); +	LLUUID notification_id = LLScriptFloaterManager::getInstance()->findNotificationId(session_id); +	LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id); +	if(notification) +	{ +		setToolTip(notification->getSubstitutions()["TITLE"].asString()); +	} + +	if ( notification && notification->getName() == INVENTORY_USER_OFFER ) +	{ +		mChicletIconCtrl->setValue(notification->getPayload()["from_id"]); +	} +	else +	{ +		mChicletIconCtrl->setValue(LLUUID::null); +	} +} + +void LLInvOfferChiclet::onMouseDown() +{ +	LLScriptFloaterManager::instance().toggleScriptFloater(getSessionId()); +} + +BOOL LLInvOfferChiclet::handleMouseDown(S32 x, S32 y, MASK mask) +{ +	onMouseDown(); +	return LLChiclet::handleMouseDown(x, y, mask); +} +  // EOF diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 1ea141e6c4..c75ad2b546 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -148,6 +148,39 @@ protected:  };  /** + * Class for displaying icon in inventory offer chiclet. + */ +class LLChicletInvOfferIconCtrl : public LLChicletAvatarIconCtrl +{ +public: + +	struct Params : +		public LLInitParam::Block<Params, LLChicletAvatarIconCtrl::Params> +	{ +		Optional<std::string> default_icon; + +		Params() +		 : default_icon("default_icon", "Generic_Object_Small") +		{ +			avatar_id = LLUUID::null; +		}; +	}; + +	/** +	 * Sets icon, if value is LLUUID::null - default icon will be set. +	 */ +	virtual void setValue(const LLSD& value ); + +protected: + +	LLChicletInvOfferIconCtrl(const Params& p); +	friend class LLUICtrlFactory; + +private: +	std::string mDefaultIcon; +}; + +/**   * Class for displaying of speaker's voice indicator    */  class LLChicletSpeakerCtrl : public LLOutputMonitorCtrl @@ -518,6 +551,17 @@ protected:  	friend class LLUICtrlFactory;  	/** +	 * Creates chiclet popup menu. Will create AdHoc Chat menu  +	 * based on other participant's id. +	 */ +	virtual void createPopupMenu(); + +	/** +	 * Processes clicks on chiclet popup menu. +	 */ +	virtual void onMenuItemClicked(const LLSD& user_data); + +	/**  	 * Displays popup menu.  	 */  	virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); @@ -574,6 +618,45 @@ private:  };  /** + * Chiclet for inventory offer script floaters. + */ +class LLInvOfferChiclet: public LLIMChiclet +{ +public: + +	struct Params : public LLInitParam::Block<Params, LLIMChiclet::Params> +	{ +		Optional<LLChicletInvOfferIconCtrl::Params> icon; + +		Params(); +	}; + +	/*virtual*/ void setSessionId(const LLUUID& session_id); + +	/*virtual*/ void setCounter(S32 counter){} + +	/*virtual*/ S32 getCounter() { return 0; } + +	/** +	 * Toggle script floater +	 */ +	/*virtual*/ void onMouseDown(); + +	/** +	 * Override default handler +	 */ +	/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + + +protected: +	LLInvOfferChiclet(const Params&); +	friend class LLUICtrlFactory; + +private: +	LLChicletInvOfferIconCtrl* mChicletIconCtrl; +}; + +/**   * Implements Group chat chiclet.   */  class LLIMGroupChiclet : public LLIMChiclet, public LLGroupMgrObserver diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 83c784c1f7..5128a7b861 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -106,6 +106,7 @@  #include "llviewermedia.h"  #include "llpluginclassmedia.h"  #include "llteleporthistorystorage.h" +#include "llnearbychat.h"  #include <boost/regex.hpp> @@ -361,6 +362,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)  BOOL LLFloaterPreference::postBuild()  {  	gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); +	 +	gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2));  	LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core");  	if (!tabcontainer->selectTab(gSavedSettings.getS32("LastPrefTab"))) diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 4adefdfcaf..fdb2b886a6 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -100,9 +100,9 @@ public:  		}  		if (tokens[1].asString() == "inspect")  		{ -			LLSD key; -			key["group_id"] = group_id; -			LLFloaterReg::showInstance("inspect_group", key); +			if (group_id.isNull()) +				return true; +			LLGroupActions::show(group_id);  			return true;  		}  		return false; diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 5e9ffdf410..9de0b1f827 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -44,6 +44,7 @@  #include "llchiclet.h"  #include "llfloaterchat.h"  #include "llfloaterreg.h" +#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container  #include "lllineeditor.h"  #include "lllogchat.h"  #include "llpanelimcontrolpanel.h" @@ -55,10 +56,6 @@  #include "lltransientfloatermgr.h"  #include "llinventorymodel.h" -#ifdef USE_IM_CONTAINER -	#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container -#endif -  LLIMFloater::LLIMFloater(const LLUUID& session_id) @@ -263,11 +260,14 @@ BOOL LLIMFloater::postBuild()  	//*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla"  	//see LLFloaterIMPanel for how it is done (IB) -#ifdef USE_IM_CONTAINER -	return LLFloater::postBuild(); -#else -	return LLDockableFloater::postBuild(); -#endif +	if(isChatMultiTab()) +	{ +		return LLFloater::postBuild(); +	} +	else +	{ +		return LLDockableFloater::postBuild(); +	}  }  // virtual @@ -328,59 +328,69 @@ void LLIMFloater::onSlide()  //static  LLIMFloater* LLIMFloater::show(const LLUUID& session_id)  { -#ifdef USE_IM_CONTAINER -	LLIMFloater* target_floater = findInstance(session_id); -	bool not_existed = NULL == target_floater; +	bool not_existed = true; -#else -	//hide all -	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); -	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); -		 iter != inst_list.end(); ++iter) +	if(isChatMultiTab())  	{ -		LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); -		if (floater && floater->isDocked()) +		LLIMFloater* target_floater = findInstance(session_id); +		not_existed = NULL == target_floater; +	} +	else +	{ +		//hide all +		LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); +		for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); +			 iter != inst_list.end(); ++iter)  		{ -			floater->setVisible(false); +			LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); +			if (floater && floater->isDocked()) +			{ +				floater->setVisible(false); +			}  		}  	} -#endif  	LLIMFloater* floater = LLFloaterReg::showTypedInstance<LLIMFloater>("impanel", session_id); -	floater->updateMessages(); -	floater->mInputEditor->setFocus(TRUE); - -#ifdef USE_IM_CONTAINER -	// do not add existed floaters to avoid adding torn off instances -	if (not_existed) +	if(isChatMultiTab())  	{ -		//		LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; -		// TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists -		LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; +		// do not add existed floaters to avoid adding torn off instances +		if (not_existed) +		{ +			//		LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; +			// TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists +			LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; -		LLIMFloaterContainer* floater_container = LLFloaterReg::showTypedInstance<LLIMFloaterContainer>("im_container"); -		floater_container->addFloater(floater, TRUE, i_pt); +			LLIMFloaterContainer* floater_container = LLFloaterReg::showTypedInstance<LLIMFloaterContainer>("im_container"); +			floater_container->addFloater(floater, TRUE, i_pt); +		}  	} -#else -	if (floater->getDockControl() == NULL) +	else  	{ -		LLChiclet* chiclet = -				LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLChiclet>( -						session_id); -		if (chiclet == NULL) -		{ -			llerror("Dock chiclet for LLIMFloater doesn't exists", 0); -		} -		else +		// Docking may move chat window, hide it before moving, or user will see how window "jumps" +		floater->setVisible(false); + +		if (floater->getDockControl() == NULL)  		{ -			LLBottomTray::getInstance()->getChicletPanel()->scrollToChiclet(chiclet); +			LLChiclet* chiclet = +					LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLChiclet>( +							session_id); +			if (chiclet == NULL) +			{ +				llerror("Dock chiclet for LLIMFloater doesn't exists", 0); +			} +			else +			{ +				LLBottomTray::getInstance()->getChicletPanel()->scrollToChiclet(chiclet); +			} + +			floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(), +					LLDockControl::TOP,  boost::bind(&LLIMFloater::getAllowedRect, floater, _1)));  		} -		floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(), -				LLDockControl::TOP,  boost::bind(&LLIMFloater::getAllowedRect, floater, _1))); +		// window is positioned, now we can show it. +		floater->setVisible(true);  	} -#endif  	return floater;  } @@ -397,9 +407,10 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock)  		(LLNotificationsUI::LLChannelManager::getInstance()->  											findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); -#ifndef USE_IM_CONTAINER -	LLTransientDockableFloater::setDocked(docked, pop_on_undock); -#endif +	if(!isChatMultiTab()) +	{ +		LLTransientDockableFloater::setDocked(docked, pop_on_undock); +	}  	// update notification channel state  	if(channel) @@ -408,6 +419,16 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock)  	}  } +void LLIMFloater::setTornOff(bool torn_off) +{ +	// When IM Floater isn't torn off, "close" button should be hidden. +	// This call will just disables it, since there is a hack in LLFloater::updateButton, +	// which prevents hiding of close button in that case. +	setCanClose(torn_off); + +	LLTransientDockableFloater::setTornOff(torn_off); +} +  void LLIMFloater::setVisible(BOOL visible)  {  	LLNotificationsUI::LLScreenChannel* channel = dynamic_cast<LLNotificationsUI::LLScreenChannel*> @@ -420,33 +441,39 @@ void LLIMFloater::setVisible(BOOL visible)  	{  		channel->updateShowToastsState();  	} + +	if (visible && mChatHistory && mInputEditor) +	{ +		//only if floater was construced and initialized from xml +		updateMessages(); +		mInputEditor->setFocus(TRUE); +	}  }  //static  bool LLIMFloater::toggle(const LLUUID& session_id)  { -#ifndef USE_IM_CONTAINER -	LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -	if (floater && floater->getVisible() && floater->isDocked()) -	{ -		// clicking on chiclet to close floater just hides it to maintain existing -		// scroll/text entry state -		floater->setVisible(false); -		return false; -	} -	else if(floater && !floater->isDocked()) -	{ -		floater->setVisible(TRUE); -		floater->setFocus(TRUE); -		return true; -	} -	else -#endif +	if(!isChatMultiTab())  	{ -		// ensure the list of messages is updated when floater is made visible -		show(session_id); -		return true; +		LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); +		if (floater && floater->getVisible() && floater->isDocked()) +		{ +			// clicking on chiclet to close floater just hides it to maintain existing +			// scroll/text entry state +			floater->setVisible(false); +			return false; +		} +		else if(floater && !floater->isDocked()) +		{ +			floater->setVisible(TRUE); +			floater->setFocus(TRUE); +			return true; +		}  	} + +	// ensure the list of messages is updated when floater is made visible +	show(session_id); +	return true;  }  //static @@ -892,3 +919,18 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info)  	}  } +// static +bool LLIMFloater::isChatMultiTab() +{ +	// Restart is required in order to change chat window type. +	static bool is_single_window = gSavedSettings.getS32("ChatWindow") == 1; +	return is_single_window; +} + +// static +void LLIMFloater::initIMFloater() +{ +	// This is called on viewer start up +	// init chat window type before user changed it in preferences +	isChatMultiTab(); +} diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 9e1330ff49..f90bc35c34 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -33,11 +33,6 @@  #ifndef LL_IMFLOATER_H  #define LL_IMFLOATER_H -// This variable is used to show floaters related to chiclets in a Multi Floater Container -// So, this functionality does not require to have IM Floaters as Dockable & Transient -// See EXT-2640. -#define USE_IM_CONTAINER -  #include "lltransientdockablefloater.h"  #include "lllogchat.h"  #include "lltooldraganddrop.h" @@ -68,6 +63,7 @@ public:  	// LLFloater overrides  	/*virtual*/ void onClose(bool app_quitting);  	/*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); +	/*virtual*/ void setTornOff(bool torn_off);  	// Make IM conversion visible and update the message history  	static LLIMFloater* show(const LLUUID& session_id); @@ -105,6 +101,14 @@ public:  							   void *cargo_data, EAcceptance *accept,  							   std::string& tooltip_msg); +	/** +	 * Returns true if chat is displayed in multi tabbed floater +	 *         false if chat is displayed in multiple windows +	 */ +	static bool isChatMultiTab(); + +	static void initIMFloater(); +  private:  	// process focus events to set a currently active session  	/* virtual */ void onFocusLost(); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index be719c0a78..5481ca97cd 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -83,19 +83,16 @@  #include "llfirstuse.h"  #include "llagentui.h" +const static std::string IM_TIME("time"); +const static std::string IM_TEXT("message"); +const static std::string IM_FROM("from"); +const static std::string IM_FROM_ID("from_id"); +  //  // Globals  //  LLIMMgr* gIMMgr = NULL; -// -// Statics -// -// *FIXME: make these all either UIStrings or Strings - -const static std::string IM_SEPARATOR(": "); - -  void toast_callback(const LLSD& msg){  	// do not show toast in busy mode or it goes from agent  	if (gAgent.getBusy() || gAgent.getID() == msg["from_id"]) @@ -193,7 +190,13 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&  	}  	if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) -		LLLogChat::loadHistory(mName, &chatFromLogFile, (void *)this); +	{ +		std::list<LLSD> chat_history; + +		//involves parsing of a chat history +		LLLogChat::loadAllHistory(mName, chat_history); +		addMessagesFromHistory(chat_history); +	}  }  void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) @@ -303,6 +306,30 @@ void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& f  	}  } +void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& history) +{ +	std::list<LLSD>::const_iterator it = history.begin(); +	while (it != history.end()) +	{ +		const LLSD& msg = *it; + +		std::string from = msg[IM_FROM]; +		LLUUID from_id = LLUUID::null; +		if (msg[IM_FROM_ID].isUndefined()) +		{ +			gCacheName->getUUID(from, from_id); +		} + + +		std::string timestamp = msg[IM_TIME]; +		std::string text = msg[IM_TEXT]; + +		addMessage(from, from_id, text, timestamp); + +		it++; +	} +} +  void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata)  {  	if (!userdata) return; @@ -1348,7 +1375,8 @@ void LLIncomingCallDialog::processCallResponse(S32 response)  			session_id = gIMMgr->addP2PSession(  				mPayload["session_name"].asString(),  				mPayload["caller_id"].asUUID(), -				mPayload["session_handle"].asString()); +				mPayload["session_handle"].asString(), +				mPayload["session_uri"].asString());  			if (voice)  			{ diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 40e3a8fb69..66f92c83a5 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -60,6 +60,8 @@ public:  		virtual ~LLIMSession();  		void sessionInitReplyReceived(const LLUUID& new_session_id); + +		void addMessagesFromHistory(const std::list<LLSD>& history);  		void addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time);  		void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state);  		static void chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata); @@ -277,11 +279,16 @@ public:  					  const LLUUID& other_participant_id,  					  const LLDynamicArray<LLUUID>& ids); -	// Creates a P2P session with the requisite handle for responding to voice calls +	/** +	 * Creates a P2P session with the requisite handle for responding to voice calls. +	 *  +	 * @param caller_uri - sip URI of caller. It should be always be passed into the method to avoid +	 * incorrect working of LLVoiceChannel instances. See EXT-2985. +	 */	  	LLUUID addP2PSession(const std::string& name,  					  const LLUUID& other_participant_id,  					  const std::string& voice_session_handle, -					  const std::string& caller_uri = LLStringUtil::null); +					  const std::string& caller_uri);  	/**  	 * Leave the session with session id. Send leave session notification diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 9caa863bd8..33fd3e3bf1 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -32,15 +32,59 @@  #include "llviewerprecompiledheaders.h" +#include "llagent.h" +#include "llagentui.h"  #include "lllogchat.h" -#include "llappviewer.h"  #include "llfloaterchat.h"  #include "lltrans.h"  #include "llviewercontrol.h" -#include "llsdserialize.h" + +#include <boost/algorithm/string/trim.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/regex.hpp> +#include <boost/regex/v4/match_results.hpp>  const S32 LOG_RECALL_SIZE = 2048; +const static std::string IM_TIME("time"); +const static std::string IM_TEXT("message"); +const static std::string IM_FROM("from"); +const static std::string IM_FROM_ID("from_id"); +const static std::string IM_SEPARATOR(": "); + +const static std::string NEW_LINE("\n"); +const static std::string NEW_LINE_SPACE_PREFIX("\n "); +const static std::string TWO_SPACES("  "); +const static std::string MULTI_LINE_PREFIX(" "); + +//viewer 1.23 may have used "You" for Agent's entries +const static std::string YOU("You"); + +/** + *  Chat log lines - timestamp and name are optional but message text is mandatory. + * + *  Typical plain text chat log lines: + * + *  SuperCar: You aren't the owner + *  [2:59]  SuperCar: You aren't the owner + *  [2009/11/20 3:00]  SuperCar: You aren't the owner + *  Katar Ivercourt is Offline + *  [3:00]  Katar Ivercourt is Offline + *  [2009/11/20 3:01]  Corba ProductEngine is Offline + */ +const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); + +/** + *  Regular expression suitable to match names like + *  "You", "Second Life", "Igor ProductEngine", "Object", "Mega House" + */ +const static boost::regex NAME_AND_TEXT("(You:|Second Life:|[^\\s:]+\\s*[:]{1}|\\S+\\s+[^\\s:]+[:]{1})?(\\s*)(.*)"); + +const static int IDX_TIMESTAMP = 1; +const static int IDX_STUFF = 2; +const static int IDX_NAME = 1; +const static int IDX_TEXT = 3; +  //static  std::string LLLogChat::makeLogFileName(std::string filename)  { @@ -118,7 +162,7 @@ void LLLogChat::saveHistory(const std::string& filename,  	item["from_id"]	= from_id;  	item["message"]	= line; -	file << LLSDOStreamer <LLSDNotationFormatter>(item) << std::endl; +	file << LLChatLogFormatter(item) << std::endl;  	file.close();  } @@ -154,9 +198,6 @@ void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLi  			}  		} -		// the parser's destructor is protected so we cannot create in the stack. -		LLPointer<LLSDParser> parser = new LLSDNotationParser(); -  		while ( fgets(buffer, LOG_RECALL_SIZE, fptr)  && !feof(fptr) )   		{  			len = strlen(buffer) - 1;		/*Flawfinder: ignore*/ @@ -167,7 +208,8 @@ void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLi  				LLSD item;  				std::string line(buffer);  				std::istringstream iss(line); -				if (parser->parse(iss, item, line.length()) == LLSDParser::PARSE_FAILURE) +				 +				if (!LLChatLogParser::parse(line, item))  				{  					item["message"]	= line;  					callback(LOG_LINE, item, userdata); @@ -187,3 +229,197 @@ void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLi  		fclose(fptr);  	}  } + +void append_to_last_message(std::list<LLSD>& messages, const std::string& line) +{ +	if (!messages.size()) return; + +	std::string im_text = messages.back()[IM_TEXT].asString(); +	im_text.append(line); +	messages.back()[IM_TEXT] = im_text; +} + +void LLLogChat::loadAllHistory(const std::string& session_name, std::list<LLSD>& messages) +{ +	if (session_name.empty()) +	{ +		llwarns << "Session name is Empty!" << llendl; +		return ; +	} + +	LLFILE* fptr = LLFile::fopen(makeLogFileName(session_name), "r");		/*Flawfinder: ignore*/ +	if (!fptr) return;	//No previous conversation with this name. + +	char buffer[LOG_RECALL_SIZE];		/*Flawfinder: ignore*/ +	char *bptr; +	S32 len; +	bool firstline = TRUE; + +	if (fseek(fptr, (LOG_RECALL_SIZE - 1) * -1  , SEEK_END)) +	{	//File is smaller than recall size.  Get it all. +		firstline = FALSE; +		if (fseek(fptr, 0, SEEK_SET)) +		{ +			fclose(fptr); +			return; +		} +	} + +	while (fgets(buffer, LOG_RECALL_SIZE, fptr)  && !feof(fptr))  +	{ +		len = strlen(buffer) - 1;		/*Flawfinder: ignore*/ +		for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--)	*bptr='\0'; +		 +		if (firstline) +		{ +			firstline = FALSE; +			continue; +		} + +		std::string line(buffer); + +		//updated 1.23 plaint text log format requires a space added before subsequent lines in a multilined message +		if (' ' == line[0]) +		{ +			line.erase(0, MULTI_LINE_PREFIX.length()); +			append_to_last_message(messages, '\n' + line); +		} +		else if (0 == len && ('\n' == line[0] || '\r' == line[0])) +		{ +			//to support old format's multilined messages with new lines used to divide paragraphs +			append_to_last_message(messages, line); +		} +		else +		{ +			LLSD item; +			if (!LLChatLogParser::parse(line, item)) +			{ +				item[IM_TEXT] = line; +			} +			messages.push_back(item); +		} +	} +	fclose(fptr); +} + +//*TODO mark object's names in a special way so that they will be distinguishable form avatar name  +//which are more strict by its nature (only firstname and secondname) +//Example, an object's name can be writen like "Object <actual_object's_name>" +void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const +{ +	if (!im.isMap()) +	{ +		llwarning("invalid LLSD type of an instant message", 0); +		return; +	} + +	if (im[IM_TIME].isDefined()) +	{ +		std::string timestamp = im[IM_TIME].asString(); +		boost::trim(timestamp); +		ostr << '[' << timestamp << ']' << TWO_SPACES; +	} + +	//*TODO mark object's names in a special way so that they will be distinguishable form avatar name  +	//which are more strict by its nature (only firstname and secondname) +	//Example, an object's name can be writen like "Object <actual_object's_name>" +	if (im[IM_FROM].isDefined()) +	{ +		std::string from = im[IM_FROM].asString(); +		boost::trim(from); +		if (from.size()) +		{ +			ostr << from << IM_SEPARATOR; +		} +	} +	 +	if (im[IM_TEXT].isDefined()) +	{ +		std::string im_text = im[IM_TEXT].asString(); + +		//multilined text will be saved with prepended spaces +		boost::replace_all(im_text, NEW_LINE, NEW_LINE_SPACE_PREFIX); +		ostr << im_text; +	} +} + +bool LLChatLogParser::parse(std::string& raw, LLSD& im) +{ +	if (!raw.length()) return false; +	 +	im = LLSD::emptyMap(); + +	//matching a timestamp +	boost::match_results<std::string::const_iterator> matches; +	if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF)) return false; +	 +	bool has_timestamp = matches[IDX_TIMESTAMP].matched; +	if (has_timestamp) +	{ +		//timestamp was successfully parsed +		std::string timestamp = matches[IDX_TIMESTAMP]; +		boost::trim(timestamp); +		timestamp.erase(0, 1); +		timestamp.erase(timestamp.length()-1, 1); +		im[IM_TIME] = timestamp;	 +	} +	else +	{ +		//timestamp is optional +		im[IM_TIME] = ""; +	} + +	bool has_stuff = matches[IDX_STUFF].matched; +	if (!has_stuff) +	{ +		return false;  //*TODO should return false or not? +	} + +	//matching a name and a text +	std::string stuff = matches[IDX_STUFF]; +	boost::match_results<std::string::const_iterator> name_and_text; +	if (!boost::regex_match(stuff, name_and_text, NAME_AND_TEXT)) return false; +	 +	bool has_name = name_and_text[IDX_NAME].matched; +	std::string name = name_and_text[IDX_NAME]; + +	//we don't need a name/text separator +	if (has_name && name.length() && name[name.length()-1] == ':') +	{ +		name.erase(name.length()-1, 1); +	} + +	if (!has_name || name == SYSTEM_FROM) +	{ +		//name is optional too +		im[IM_FROM] = SYSTEM_FROM; +		im[IM_FROM_ID] = LLUUID::null; +	} + +	if (!has_name) +	{ +		//text is mandatory +		im[IM_TEXT] = stuff; +		return true; //parse as a message from Second Life +	} +	 +	bool has_text = name_and_text[IDX_TEXT].matched; +	if (!has_text) return false; + +	//for parsing logs created in very old versions of a viewer +	if (name == "You") +	{ +		std::string agent_name; +		LLAgentUI::buildFullname(agent_name); +		im[IM_FROM] = agent_name; +		im[IM_FROM_ID] = gAgentID; +	} +	else +	{ +		im[IM_FROM] = name; +	} +	 + +	im[IM_TEXT] = name_and_text[IDX_TEXT]; +	return true;  //parsed name and message text, maybe have a timestamp too +} diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index e252cd7d41..3d3f5c4458 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -51,11 +51,66 @@ public:  				const LLUUID& from_id,  				const std::string& line); +	/** @deprecated @see loadAllHistory() */  	static void loadHistory(const std::string& filename,   		                    void (*callback)(ELogLineType, const LLSD&, void*),   							void* userdata); + +	static void loadAllHistory(const std::string& session_name, std::list<LLSD>& messages);  private:  	static std::string cleanFileName(std::string filename);  }; +/** + * Formatter for the plain text chat log files + */ +class LLChatLogFormatter +{ +public: +	LLChatLogFormatter(const LLSD& im) : mIM(im) {} +	virtual ~LLChatLogFormatter() {}; + +	friend std::ostream& operator<<(std::ostream& str, const LLChatLogFormatter& formatter) +	{ +		formatter.format(formatter.mIM, str); +		return str; +	} + +protected: + +	/** +	 * Format an instant message to a stream +	 * Timestamps and sender names are required +	 * New lines of multilined messages are prepended with a space +	 */ +	void format(const LLSD& im, std::ostream& ostr) const; + +	LLSD mIM; +}; + +/** + * Parser for the plain text chat log files + */ +class LLChatLogParser +{ +public: + +	 /** +	 * Parse a line from the plain text chat log file +	 * General plain text log format is like: "[timestamp]  [name]: [message]" +	 * [timestamp] and [name] are optional +	 * Examples of plain text chat log lines: +	 * "[2009/11/20 2:53]  Igor ProductEngine: howdy" +	 * "Igor ProductEngine: howdy" +	 * "Dserduk ProductEngine is Online" +	 * +	 * @return false if failed to parse mandatory data - message text +	 */ +	static bool parse(std::string& raw, LLSD& im); + +protected: +	LLChatLogParser(); +	virtual ~LLChatLogParser() {}; +}; +  #endif diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 8f1dec1431..ee3be0a5e3 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -57,6 +57,7 @@  #include "lltrans.h"  #include "llbottomtray.h"  #include "llnearbychatbar.h" +#include "llfloaterreg.h"  static const S32 RESIZE_BAR_THICKNESS = 3; @@ -145,7 +146,7 @@ std::string appendTime()  	return timeStr;  } -void	LLNearbyChat::addMessage(const LLChat& chat) +void	LLNearbyChat::addMessage(const LLChat& chat,bool archive)  {  	if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)  	{ @@ -207,6 +208,13 @@ void	LLNearbyChat::addMessage(const LLChat& chat)  			mChatHistory->appendMessage(chat,use_plain_text_chat_history);  		}  	} + +	if(archive) +	{ +		mMessageArchive.push_back(chat); +		if(mMessageArchive.size()>200) +			mMessageArchive.erase(mMessageArchive.begin()); +	}  }  void LLNearbyChat::onNearbySpeakers() @@ -256,3 +264,19 @@ void LLNearbyChat::getAllowedRect(LLRect& rect)  {  	rect = gViewerWindow->getWorldViewRectScaled();  } + +void LLNearbyChat::updateChatHistoryStyle() +{ +	mChatHistory->clear(); +	for(std::vector<LLChat>::iterator it = mMessageArchive.begin();it!=mMessageArchive.end();++it) +	{ +		addMessage(*it,false); +	} +} +//static  +void LLNearbyChat::processChatHistoryStyleUpdate(const LLSD& newvalue) +{ +	LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); +	if(nearby_chat) +		nearby_chat->updateChatHistoryStyle(); +} diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index efcaf4263b..1cbc2a3478 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -47,7 +47,7 @@ public:  	~LLNearbyChat();  	BOOL	postBuild			(); -	void	addMessage			(const LLChat& message);	 +	void	addMessage			(const LLChat& message,bool archive = true);	  	void	onNearbyChatContextMenuItemClicked(const LLSD& userdata);  	bool	onNearbyChatCheckContextMenuItem(const LLSD& userdata); @@ -57,6 +57,10 @@ public:  	virtual void setRect		(const LLRect &rect); +	virtual void updateChatHistoryStyle(); + +	static void processChatHistoryStyleUpdate(const LLSD& newvalue); +  private:  	virtual void    applySavedVariables(); @@ -68,6 +72,8 @@ private:  private:  	LLHandle<LLView>	mPopupMenuHandle;  	LLChatHistory*		mChatHistory; + +	std::vector<LLChat> mMessageArchive;  };  #endif diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 05da338513..857b7e9796 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -43,13 +43,14 @@ using namespace LLNotificationsUI;  const static std::string GRANTED_MODIFY_RIGHTS("GrantedModifyRights"),  		REVOKED_MODIFY_RIGHTS("RevokedModifyRights"), OBJECT_GIVE_ITEM(  				"ObjectGiveItem"), OBJECT_GIVE_ITEM_UNKNOWN_USER( -				"ObjectGiveItemUnknownUser"); +				"ObjectGiveItemUnknownUser"), PAYMENT_RECIVED("PaymentRecived");  // static  bool LLHandlerUtil::canLogToIM(const LLNotificationPtr& notification)  {  	return GRANTED_MODIFY_RIGHTS == notification->getName() -			|| REVOKED_MODIFY_RIGHTS == notification->getName(); +			|| REVOKED_MODIFY_RIGHTS == notification->getName() +			|| PAYMENT_RECIVED == notification->getName();  }  // static diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index c179a2cf90..4f353bf6a5 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -39,6 +39,7 @@  #include "llviewerwindow.h"  #include "llnotificationmanager.h"  #include "llnotifications.h" +#include "llscriptfloater.h"  using namespace LLNotificationsUI; @@ -92,24 +93,42 @@ bool LLOfferHandler::processNotification(const LLSD& notify)  	{  		LLHandlerUtil::logToIMP2P(notification); -		LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); - -		LLToast::Params p; -		p.notif_id = notification->getID(); -		p.notification = notification; -		p.panel = notify_box; -		p.on_delete_toast = boost::bind(&LLOfferHandler::onDeleteToast, this, _1); - -		LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel); -		if(channel) -			channel->addToast(p); - -		// send a signal to the counter manager -		mNewNotificationSignal(); +		if( notification->getPayload().has("give_inventory_notification") +			&& !notification->getPayload()["give_inventory_notification"] ) +		{ +			// This is an original inventory offer, so add a script floater +			LLScriptFloaterManager::instance().onAddNotification(notification->getID()); +		} +		else +		{ +			LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); + +			LLToast::Params p; +			p.notif_id = notification->getID(); +			p.notification = notification; +			p.panel = notify_box; +			p.on_delete_toast = boost::bind(&LLOfferHandler::onDeleteToast, this, _1); +			 +			LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel); +			if(channel) +				channel->addToast(p); + +			// send a signal to the counter manager +			mNewNotificationSignal(); +		}  	}  	else if (notify["sigtype"].asString() == "delete")  	{ -		mChannel->killToastByNotificationID(notification->getID()); +		if( notification->getPayload().has("give_inventory_notification") +			&& !notification->getPayload()["give_inventory_notification"] ) +		{ +			// Remove original inventory offer script floater +			LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); +		} +		else +		{ +			mChannel->killToastByNotificationID(notification->getID()); +		}  	}  	return true; diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 97c1e96175..eb9cb10d56 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -254,11 +254,19 @@ void LLPanelAvatarNotes::onCommitRights()  	const LLRelationship* buddy_relationship =  			LLAvatarTracker::instance().getBuddyInfo(getAvatarId());  	bool allow_modify_objects = childGetValue("objects_check").asBoolean(); + +	// if modify objects checkbox clicked  	if (buddy_relationship->isRightGrantedTo(  			LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects)  	{  		confirmModifyRights(allow_modify_objects, rights);  	} +	// only one checkbox can trigger commit, so store the rest of rights +	else +	{ +		LLAvatarPropertiesProcessor::getInstance()->sendFriendRights( +						getAvatarId(), rights); +	}  }  void LLPanelAvatarNotes::processProperties(void* data, EAvatarProcessorType type) @@ -522,20 +530,19 @@ void LLPanelAvatarProfile::processGroupProperties(const LLAvatarGroups* avatar_g  	for(; it_end != it; ++it)  	{  		LLAvatarGroups::LLGroupData group_data = *it; - -		// Check if there is no duplicates for this group -		if (std::find(mGroups.begin(), mGroups.end(), group_data.group_name) == mGroups.end()) -			mGroups.push_back(group_data.group_name); +		mGroups[group_data.group_name] = group_data.group_id;  	}  	// Creating string, containing group list  	std::string groups = ""; -	for (group_list_t::const_iterator it = mGroups.begin(); it != mGroups.end(); ++it) +	for (group_map_t::iterator it = mGroups.begin(); it != mGroups.end(); ++it)  	{  		if (it != mGroups.begin())  			groups += ", "; -		groups += *it; +		 +		std::string group_url="[secondlife:///app/group/" + it->second.asString() + "/about " + it->first + "]"; +		groups += group_url;  	}  	childSetValue("sl_groups", groups); diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h index f54aeee4eb..b19c5cca49 100644 --- a/indra/newview/llpanelavatar.h +++ b/indra/newview/llpanelavatar.h @@ -192,8 +192,8 @@ protected:  private: -	typedef std::list<std::string>	group_list_t; -	group_list_t 			mGroups; +	typedef std::map< std::string,LLUUID>	group_map_t; +	group_map_t 			mGroups;  	LLToggleableMenu*		mProfileMenu;  }; diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index e04c830036..5834c50fbb 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -510,6 +510,9 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg)  	S32 i=0;  	S32 count = msg->getNumberOfBlocks("Data"); + +	mNoticesList->setEnabled(TRUE); +  	for (;i<count;++i)  	{  		msg->getUUID("Data","NoticeID",id,i); diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 3af18bb751..4ce6d14faa 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -67,28 +67,6 @@ static const std::string TRASH_BUTTON_NAME = "trash_btn";  // helper functions  static void filter_list(LLInventorySubTreePanel* inventory_list, const std::string& string); -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLLandmarksPanelObserver -// -// Bridge to support knowing when the inventory has changed to update -// landmarks accordions visibility. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLLandmarksPanelObserver : public LLInventoryObserver -{ -public: -	LLLandmarksPanelObserver(LLLandmarksPanel* lp) : mLP(lp) {} -	virtual ~LLLandmarksPanelObserver() {} -	/*virtual*/ void changed(U32 mask); - -private: -	LLLandmarksPanel* mLP; -}; - -void LLLandmarksPanelObserver::changed(U32 mask) -{ -	mLP->updateFilteredAccordions(); -} -  LLLandmarksPanel::LLLandmarksPanel()  	:	LLPanelPlacesTab()  	,	mFavoritesInventoryPanel(NULL) @@ -99,18 +77,12 @@ LLLandmarksPanel::LLLandmarksPanel()  	,	mListCommands(NULL)  	,	mGearFolderMenu(NULL)  	,	mGearLandmarkMenu(NULL) -	,	mDirtyFilter(false)  { -	mInventoryObserver = new LLLandmarksPanelObserver(this); -	gInventory.addObserver(mInventoryObserver); -  	LLUICtrlFactory::getInstance()->buildPanel(this, "panel_landmarks.xml");  }  LLLandmarksPanel::~LLLandmarksPanel()  { -	if (gInventory.containsObserver(mInventoryObserver)) -		gInventory.removeObserver(mInventoryObserver);  }  BOOL LLLandmarksPanel::postBuild() @@ -137,39 +109,34 @@ BOOL LLLandmarksPanel::postBuild()  // virtual  void LLLandmarksPanel::onSearchEdit(const std::string& string)  { -	static std::string prev_string(""); - -	if (prev_string == string) return; -  	// show all folders in Landmarks Accordion for empty filter -	mLandmarksInventoryPanel->setShowFolderState(string.empty() ? -		LLInventoryFilter::SHOW_ALL_FOLDERS : -		LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS -		); - -	filter_list(mFavoritesInventoryPanel, string); -	filter_list(mLandmarksInventoryPanel, string); -	filter_list(mMyInventoryPanel, string); -	filter_list(mLibraryInventoryPanel, string); - -	prev_string = string; -	mDirtyFilter = true; +	if (mLandmarksInventoryPanel->getFilter()) +	{ +		mLandmarksInventoryPanel->setShowFolderState(string.empty() ? +			LLInventoryFilter::SHOW_ALL_FOLDERS : +			LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS +			); +	}  	// give FolderView a chance to be refreshed. So, made all accordions visible  	for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter)  	{  		LLAccordionCtrlTab* tab = *iter; -		tab->setVisible(true); +		if (tab && !tab->getVisible()) +			tab->setVisible(TRUE);  		// expand accordion to see matched items in each one. See EXT-2014.  		tab->changeOpenClose(false); -		// refresh all accordions to display their contents in case of less restrictive filter  		LLInventorySubTreePanel* inventory_list = dynamic_cast<LLInventorySubTreePanel*>(tab->getAccordionView());  		if (NULL == inventory_list) continue; -		LLFolderView* fv = inventory_list->getRootFolder(); -		fv->refresh(); + +		if (inventory_list->getFilter()) +			filter_list(inventory_list, string);  	} + +	if (sFilterSubString != string) +		sFilterSubString = string;  }  // virtual @@ -255,34 +222,6 @@ void LLLandmarksPanel::onSelectorButtonClicked()  	}  } -void LLLandmarksPanel::updateFilteredAccordions() -{ -	LLInventoryPanel* inventory_list = NULL; -	LLAccordionCtrlTab* accordion_tab = NULL; -	for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter) -	{ -		accordion_tab = *iter; -		inventory_list = dynamic_cast<LLInventorySubTreePanel*> (accordion_tab->getAccordionView()); -		if (NULL == inventory_list) continue; -		// This doesn't seem to work correctly.  Disabling for now. -Seraph -		/* -		LLFolderView* fv = inventory_list->getRootFolder(); -		bool has_descendants = fv->hasFilteredDescendants(); - -		accordion_tab->setVisible(has_descendants); -		*/ -		accordion_tab->setVisible(TRUE); -	} - -	// we have to arrange accordion tabs for cases when filter string is less restrictive but -	// all items are still filtered. -	static LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("landmarks_accordion"); -	accordion->arrange(); - -	// now filter state is applied to accordion tabs -	mDirtyFilter = false; -} -  //////////////////////////////////////////////////////////////////////////  // PROTECTED METHODS  ////////////////////////////////////////////////////////////////////////// @@ -394,7 +333,8 @@ void LLLandmarksPanel::initLandmarksInventoryPanel()  	initLandmarksPanel(mLandmarksInventoryPanel); -	mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); +	if (mLandmarksInventoryPanel->getFilter()) +		mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS);  	// subscribe to have auto-rename functionality while creating New Folder  	mLandmarksInventoryPanel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, mLandmarksInventoryPanel, _1, _2)); @@ -422,6 +362,9 @@ void LLLandmarksPanel::initLibraryInventoryPanel()  void LLLandmarksPanel::initLandmarksPanel(LLInventorySubTreePanel* inventory_list)  { +	if (!inventory_list->getFilter()) +		return; +  	inventory_list->setFilterTypes(0x1 << LLInventoryType::IT_LANDMARK);  	inventory_list->setSelectCallback(boost::bind(&LLLandmarksPanel::onSelectionChange, this, inventory_list, _1, _2)); @@ -435,6 +378,8 @@ void LLLandmarksPanel::initLandmarksPanel(LLInventorySubTreePanel* inventory_lis  		root_folder->setupMenuHandle(LLInventoryType::IT_LANDMARK, mGearLandmarkMenu->getHandle());  	} +	root_folder->setParentLandmarksPanel(this); +  	// save initial folder state to avoid incorrect work while switching between Landmarks & Teleport History tabs  	// See EXT-1609.  	inventory_list->saveFolderState(); @@ -443,6 +388,9 @@ void LLLandmarksPanel::initLandmarksPanel(LLInventorySubTreePanel* inventory_lis  void LLLandmarksPanel::initAccordion(const std::string& accordion_tab_name, LLInventorySubTreePanel* inventory_list)  {  	LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>(accordion_tab_name); +	if (!accordion_tab) +		return; +  	mAccordionTabs.push_back(accordion_tab);  	accordion_tab->setDropDownStateChangedCallback(  		boost::bind(&LLLandmarksPanel::onAccordionExpandedCollapsed, this, _2, inventory_list)); @@ -787,6 +735,46 @@ void LLLandmarksPanel::onCustomAction(const LLSD& userdata)  	}  } +void LLLandmarksPanel::updateFilteredAccordions() +{ +	LLInventoryPanel* inventory_list = NULL; +	LLAccordionCtrlTab* accordion_tab = NULL; +//	bool needs_arrange = false; + +	for (accordion_tabs_t::const_iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter) +	{ +		accordion_tab = *iter; +		if (accordion_tab && !accordion_tab->getVisible()) +			accordion_tab->setVisible(TRUE); + +		inventory_list = dynamic_cast<LLInventorySubTreePanel*> (accordion_tab->getAccordionView()); +		if (NULL == inventory_list) continue; + +		// This doesn't seem to work correctly.  Disabling for now. -Seraph +		/* +		LLFolderView* fv = inventory_list->getRootFolder(); + +		// arrange folder view contents to draw its descendants if it has any +		fv->arrangeFromRoot(); + +		bool has_descendants = fv->hasFilteredDescendants(); +		if (!has_descendants) +			needs_arrange = true; + +		accordion_tab->setVisible(has_descendants); +		*/ +		accordion_tab->setVisible(TRUE); +	} + +	// we have to arrange accordion tabs for cases when filter string is less restrictive but +	// all items are still filtered. +//	if (needs_arrange) +//	{ +		static LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("landmarks_accordion"); +		accordion->arrange(); +//	} +} +  /*  Processes such actions: cut/rename/delete/paste actions @@ -899,12 +887,7 @@ bool LLLandmarksPanel::handleDragAndDropToTrash(BOOL drop, EDragAndDropType carg  void LLLandmarksPanel::doIdle(void* landmarks_panel)  {  	LLLandmarksPanel* panel = (LLLandmarksPanel* ) landmarks_panel; - -	if (panel->mDirtyFilter) -	{ -		panel->updateFilteredAccordions(); -	} - +	panel->updateFilteredAccordions();  }  void LLLandmarksPanel::doShowOnMap(LLLandmark* landmark) diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index c65abc178b..bee141d051 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -46,7 +46,6 @@ class LLAccordionCtrlTab;  class LLFolderViewItem;  class LLMenuGL;  class LLInventoryPanel; -class LLInventoryObserver;  class LLInventorySubTreePanel;  class LLLandmarksPanel : public LLPanelPlacesTab, LLRemoteParcelInfoObserver @@ -63,13 +62,10 @@ public:  	void onSelectionChange(LLInventorySubTreePanel* inventory_list, const std::deque<LLFolderViewItem*> &items, BOOL user_action);  	void onSelectorButtonClicked(); - -	/** -	 * Updates accordions according to filtered items in lists. -	 * -	 * It hides accordion for empty lists -	 */ -	void updateFilteredAccordions(); +	void setCurrentSelectedList(LLInventorySubTreePanel* inventory_list) +	{ +		mCurrentSelectedList = inventory_list; +	}  protected:  	/** @@ -111,6 +107,13 @@ private:  	void onCustomAction(const LLSD& command_name);  	/** +	 * Updates accordions according to filtered items in lists. +	 * +	 * It hides accordion for empty lists +	 */ +	void updateFilteredAccordions(); + +	/**  	 * Determines if selected item can be modified via context/gear menu.  	 *  	 * It validates Places Landmarks rules first. And then LLFolderView permissions. @@ -148,11 +151,9 @@ private:  	LLMenuGL*					mGearFolderMenu;  	LLMenuGL*					mMenuAdd;  	LLInventorySubTreePanel*	mCurrentSelectedList; -	LLInventoryObserver*		mInventoryObserver;  	LLPanel*					mListCommands;  	bool 						mSortByDate; -	bool						mDirtyFilter;  	typedef	std::vector<LLAccordionCtrlTab*> accordion_tabs_t;  	accordion_tabs_t			mAccordionTabs; diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index da0c8d5020..541361324a 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -558,6 +558,11 @@ void LLPanelPickEdit::initTexturePickerMouseEvents()  	text_icon = getChild<LLIconCtrl>(XML_BTN_ON_TXTR);  	mSnapshotCtrl->setMouseEnterCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseEnter, this, _1));  	mSnapshotCtrl->setMouseLeaveCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseLeave, this, _1)); +	 +	// *WORKAROUND: Needed for EXT-1625: enabling save button each time when picker is opened, even if  +	// texture wasn't changed (see Steve's comment). +	mSnapshotCtrl->setMouseDownCallback(boost::bind(&LLPanelPickEdit::enableSaveButton, this, true)); +	  	text_icon->setVisible(FALSE);  } diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index 4abb60dded..cc6e88a9d2 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -61,7 +61,6 @@ static const std::string XML_BTN_DELETE = "trash_btn";  static const std::string XML_BTN_INFO = "info_btn";  static const std::string XML_BTN_TELEPORT = "teleport_btn";  static const std::string XML_BTN_SHOW_ON_MAP = "show_on_map_btn"; -static const std::string XML_BTN_OVERFLOW = "overflow_btn";  static const std::string PICK_ID("pick_id");  static const std::string PICK_CREATOR_ID("pick_creator_id"); @@ -113,7 +112,6 @@ LLPanelPicks::LLPanelPicks()  	mClassifiedsList(NULL),  	mPanelPickInfo(NULL),  	mPanelPickEdit(NULL), -	mOverflowMenu(NULL),  	mPlusMenu(NULL),  	mPicksAccTab(NULL),  	mClassifiedsAccTab(NULL), @@ -273,7 +271,6 @@ BOOL LLPanelPicks::postBuild()  	childSetAction(XML_BTN_TELEPORT, boost::bind(&LLPanelPicks::onClickTeleport, this));  	childSetAction(XML_BTN_SHOW_ON_MAP, boost::bind(&LLPanelPicks::onClickMap, this));  	childSetAction(XML_BTN_INFO, boost::bind(&LLPanelPicks::onClickInfo, this)); -	childSetAction(XML_BTN_OVERFLOW, boost::bind(&LLPanelPicks::onOverflowButtonClicked, this));  	mPicksAccTab = getChild<LLAccordionCtrlTab>("tab_picks");  	mPicksAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelPicks::onAccordionStateChanged, this, mPicksAccTab)); @@ -291,10 +288,6 @@ BOOL LLPanelPicks::postBuild()  	registar.add("Pick.Delete", boost::bind(&LLPanelPicks::onClickDelete, this));  	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>("menu_picks.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); -	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar overflow_registar; -	overflow_registar.add("PicksList.Overflow", boost::bind(&LLPanelPicks::onOverflowMenuItemClicked, this, _2)); -	mOverflowMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_picks_overflow.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); -  	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar plus_registar;  	plus_registar.add("Picks.Plus.Action", boost::bind(&LLPanelPicks::onPlusMenuItemClicked, this, _2));  	mPlusMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_picks_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); @@ -302,24 +295,6 @@ BOOL LLPanelPicks::postBuild()  	return TRUE;  } -void LLPanelPicks::onOverflowMenuItemClicked(const LLSD& param) -{ -	std::string value = param.asString(); - -	if("info" == value) -	{ -		onClickInfo(); -	} -	else if("teleport" == value) -	{ -		onClickTeleport(); -	} -	else if("map" == value) -	{ -		onClickMap(); -	} -} -  void LLPanelPicks::onPlusMenuItemClicked(const LLSD& param)  {  	std::string value = param.asString(); @@ -348,23 +323,6 @@ void LLPanelPicks::onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab)  	updateButtons();  } -void LLPanelPicks::onOverflowButtonClicked() -{ -	if (!mOverflowMenu->toggleVisibility()) -		return; - -	LLView* btn = getChild<LLView>(XML_BTN_OVERFLOW); - -	if (mOverflowMenu->getButtonRect().isEmpty()) -	{ -		mOverflowMenu->setButtonRect(btn); -	} -	mOverflowMenu->updateParent(LLMenuGL::sMenuContainer); - -	LLRect rect = btn->getRect(); -	LLMenuGL::showPopup(this, mOverflowMenu, rect.mRight, rect.mTop); -} -  void LLPanelPicks::onOpen(const LLSD& key)  {  	const LLUUID id(key.asUUID()); @@ -568,7 +526,6 @@ void LLPanelPicks::updateButtons()  	childSetEnabled(XML_BTN_INFO, has_selected);  	childSetEnabled(XML_BTN_TELEPORT, has_selected);  	childSetEnabled(XML_BTN_SHOW_ON_MAP, has_selected); -	childSetEnabled(XML_BTN_OVERFLOW, has_selected);  }  void LLPanelPicks::setProfilePanel(LLPanelProfile* profile_panel) diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h index b21b1c64b1..21794d56b2 100644 --- a/indra/newview/llpanelpicks.h +++ b/indra/newview/llpanelpicks.h @@ -94,8 +94,6 @@ private:  	void onClickTeleport();  	void onClickMap(); -	void onOverflowMenuItemClicked(const LLSD& param); -	void onOverflowButtonClicked();  	void onPlusMenuItemClicked(const LLSD& param);  	void onListCommit(const LLFlatListView* f_list); @@ -149,7 +147,6 @@ private:  	LLPanelClassifiedInfo* mPanelClassifiedInfo;  	LLPanelClassifiedEdit* mPanelClassifiedEdit;  	LLPanelPickEdit* mPanelPickEdit; -	LLToggleableMenu* mOverflowMenu;  	LLToggleableMenu* mPlusMenu;  	LLAccordionCtrlTab* mPicksAccTab; diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 839b2ec45e..cd4bcb6c0a 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -118,7 +118,6 @@ static LLRegisterPanelClassWrapper<LLPanelPlaces> t_places("panel_places");  LLPanelPlaces::LLPanelPlaces()  	:	LLPanel(), -		mFilterSubString(LLStringUtil::null),  		mActivePanel(NULL),  		mFilterEditor(NULL),  		mPlaceProfile(NULL), @@ -385,16 +384,16 @@ void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark)  void LLPanelPlaces::onFilterEdit(const std::string& search_string, bool force_filter)  { -	if (force_filter || mFilterSubString != search_string) +	if (force_filter || LLPanelPlacesTab::sFilterSubString != search_string)  	{ -		mFilterSubString = search_string; +		std::string string = search_string;  		// Searches are case-insensitive -		LLStringUtil::toUpper(mFilterSubString); -		LLStringUtil::trimHead(mFilterSubString); +		LLStringUtil::toUpper(string); +		LLStringUtil::trimHead(string);  		if (mActivePanel) -			mActivePanel->onSearchEdit(mFilterSubString); +			mActivePanel->onSearchEdit(string);  	}  } @@ -404,7 +403,7 @@ void LLPanelPlaces::onTabSelected()  	if (!mActivePanel)  		return; -	onFilterEdit(mFilterSubString, true); +	onFilterEdit(LLPanelPlacesTab::sFilterSubString, true);  	mActivePanel->updateVerbs();  } @@ -815,7 +814,7 @@ void LLPanelPlaces::changedInventory(U32 mask)  	// Filter applied to show all items.  	if (mActivePanel) -		mActivePanel->onSearchEdit(mFilterSubString); +		mActivePanel->onSearchEdit(LLPanelPlacesTab::sFilterSubString);  	// we don't need to monitor inventory changes anymore,  	// so remove the observer diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 0d97353b66..5f9aed6357 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -120,10 +120,6 @@ private:  	// be available (hence zero)  	LLVector3d					mPosGlobal; -	// Search string for filtering landmarks and teleport -	// history locations -	std::string					mFilterSubString; -  	// Information type currently shown in Place Information panel  	std::string					mPlaceInfoType; diff --git a/indra/newview/llpanelplacestab.cpp b/indra/newview/llpanelplacestab.cpp index b7669fd63d..9806b8c64d 100644 --- a/indra/newview/llpanelplacestab.cpp +++ b/indra/newview/llpanelplacestab.cpp @@ -33,14 +33,17 @@  #include "llpanelplacestab.h" -#include "llwindow.h" - +#include "llbutton.h"  #include "llnotificationsutil.h" -#include "llbutton.h" +#include "llwindow.h" + +#include "llpanelplaces.h"  #include "llslurl.h"  #include "llworldmap.h" +std::string LLPanelPlacesTab::sFilterSubString = LLStringUtil::null; +  bool LLPanelPlacesTab::isTabVisible()  {  	LLUICtrl* parent = getParentUICtrl(); diff --git a/indra/newview/llpanelplacestab.h b/indra/newview/llpanelplacestab.h index 458694d766..b4d839452e 100644 --- a/indra/newview/llpanelplacestab.h +++ b/indra/newview/llpanelplacestab.h @@ -34,7 +34,7 @@  #include "llpanel.h" -#include "llpanelplaces.h" +class LLPanelPlaces;  class LLPanelPlacesTab : public LLPanel  { @@ -55,6 +55,11 @@ public:  										const std::string& url,  										const LLUUID& snapshot_id,  										bool teleport); + +public: +	// Search string for filtering landmarks and teleport history locations +	static std::string		sFilterSubString; +  protected:  	LLButton*				mTeleportBtn;  	LLButton*				mShowOnMapBtn; diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 327048d4f3..523487fa14 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -220,7 +220,6 @@ void LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard()  LLTeleportHistoryPanel::LLTeleportHistoryPanel()  	:	LLPanelPlacesTab(), -		mFilterSubString(LLStringUtil::null),  		mDirty(true),  		mCurrentItem(0),  		mTeleportHistory(NULL), @@ -317,9 +316,9 @@ void LLTeleportHistoryPanel::draw()  // virtual  void LLTeleportHistoryPanel::onSearchEdit(const std::string& string)  { -	if (mFilterSubString != string) +	if (sFilterSubString != string)  	{ -		mFilterSubString = string; +		sFilterSubString = string;  		showTeleportHistory();  	}  } @@ -482,8 +481,8 @@ void LLTeleportHistoryPanel::refresh()  		std::string landmark_title = items[mCurrentItem].mTitle;  		LLStringUtil::toUpper(landmark_title); -		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; +		std::string::size_type match_offset = sFilterSubString.size() ? landmark_title.find(sFilterSubString) : std::string::npos; +		bool passed = sFilterSubString.size() == 0 || match_offset != std::string::npos;  		if (!passed)  		{ diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 07a1214b4f..13f195a1be 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -64,10 +64,10 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av  	mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator");  	mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData")); -	mAvatarList->setDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList)); -	mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2)); +	mAvatarListDoubleClickConnection = mAvatarList->setDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList)); +	mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2));      // Set onAvatarListDoubleClicked as default on_return action. -	mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList)); +	mAvatarListReturnConnection = mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList));  	if (use_context_menu)  	{ @@ -99,6 +99,10 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av  LLParticipantList::~LLParticipantList()  { +	mAvatarListDoubleClickConnection.disconnect(); +	mAvatarListRefreshConnection.disconnect(); +	mAvatarListReturnConnection.disconnect(); +  	delete mParticipantListMenu;  	mParticipantListMenu = NULL;  } diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 460cf4b9ef..86b38f5f1e 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -151,4 +151,9 @@ class LLParticipantList  		LLParticipantListMenu*    mParticipantListMenu;  		EParticipantSortOrder	mSortOrder; + +		// boost::connections +		boost::signals2::connection mAvatarListDoubleClickConnection; +		boost::signals2::connection mAvatarListRefreshConnection; +		boost::signals2::connection mAvatarListReturnConnection;  }; diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp index c58caf9c60..155172128b 100644 --- a/indra/newview/llscriptfloater.cpp +++ b/indra/newview/llscriptfloater.cpp @@ -41,6 +41,7 @@  #include "llscreenchannel.h"  #include "lltoastnotifypanel.h"  #include "llviewerwindow.h" +#include "llimfloater.h"  //////////////////////////////////////////////////////////////////////////  ////////////////////////////////////////////////////////////////////////// @@ -211,7 +212,18 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id)  	LLNotificationData nd = {notification_id};  	mNotifications.insert(std::make_pair(object_id, nd)); -	LLBottomTray::getInstance()->getChicletPanel()->createChiclet<LLScriptChiclet>(object_id); +	// Create inventory offer chiclet for offer type notifications +	LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id); +	if( notification && notification->getType() == "offer" ) +	{ +		LLBottomTray::instance().getChicletPanel()->createChiclet<LLInvOfferChiclet>(object_id); +	} +	else +	{ +		LLBottomTray::getInstance()->getChicletPanel()->createChiclet<LLScriptChiclet>(object_id); +	} + +	toggleScriptFloater(object_id);  }  void LLScriptFloaterManager::onRemoveNotification(const LLUUID& notification_id) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 50c20bc98f..58df2ffb19 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -59,6 +59,7 @@  #include "llfloaterreg.h"  #include "llfocusmgr.h"  #include "llhttpsender.h" +#include "llimfloater.h"  #include "lllocationhistory.h"  #include "llimageworker.h"  #include "llloginflags.h" @@ -2085,6 +2086,8 @@ bool idle_startup()  		LLAgentPicksInfo::getInstance()->requestNumberOfPicks(); +		LLIMFloater::initIMFloater(); +  		return TRUE;  	} diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index d0ae5d1e38..f9e1a94def 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -509,7 +509,10 @@ bool LLViewerInventoryCategory::fetchDescendents()  		// This comes from LLInventoryFilter from llfolderview.h  		U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; -		std::string url = gAgent.getRegion()->getCapability("WebFetchInventoryDescendents"); +		// *NOTE +		// Temporary workaround for bug EXT-2879, see ticket for details. +		// Commented gAgent.getRegion()->getCapability in order to use the old system. +		std::string url;//= gAgent.getRegion()->getCapability("WebFetchInventoryDescendents");  		if (!url.empty()) //Capability found.  Build up LLSD and use it.  		{ diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 9fc818e1ff..23d02af73d 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1240,6 +1240,10 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&  		gInventory.addObserver(opener);  	} +	// Remove script dialog because there is no need in it no more. +	LLUUID object_id = notification["payload"]["object_id"].asUUID(); +	LLScriptFloaterManager::instance().removeNotificationByObjectId(object_id); +  	delete this;  	return false;  } @@ -1414,7 +1418,11 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const  	{  		gInventory.addObserver(opener);  	} -	 + +	// Remove script dialog because there is no need in it no more. +	LLUUID object_id = notification["payload"]["object_id"].asUUID(); +	LLScriptFloaterManager::instance().removeNotificationByObjectId(object_id); +  	delete this;  	return false;  } @@ -1502,7 +1510,18 @@ void inventory_offer_handler(LLOfferInfo* info)  		}  	} +	// If mObjectID is null then generate the object_id based on msg to prevent +	// multiple creation of chiclets for same object. +	LLUUID object_id = info->mObjectID; +	if (object_id.isNull()) +		object_id.generate(msg); +  	payload["from_id"] = info->mFromID; +	// Needed by LLScriptFloaterManager to bind original notification with  +	// faked for toast one. +	payload["object_id"] = object_id; +	// Flag indicating that this notification is faked for toast. +	payload["give_inventory_notification"] = FALSE;  	args["OBJECTFROMNAME"] = info->mFromName;  	args["NAME"] = info->mFromName;  	args["NAME_SLURL"] = LLSLURL::buildCommand("agent", info->mFromID, "about"); @@ -1543,9 +1562,16 @@ void inventory_offer_handler(LLOfferInfo* info)  		// In viewer 2 we're now auto receiving inventory offers and messaging as such (not sending reject messages).  		info->send_auto_receive_response();  	} -	 +  	// Pop up inv offer notification and let the user accept (keep), or reject (and silently delete) the inventory. -	LLNotifications::instance().add(p); +	 LLNotifications::instance().add(p); + +	// Inform user that there is a script floater via toast system +	{ +		payload["give_inventory_notification"] = TRUE; +		LLNotificationPtr notification = LLNotifications::instance().add(p.payload(payload));  +		LLScriptFloaterManager::getInstance()->setNotificationToastId(object_id, notification->getID()); +	}  }  bool lure_callback(const LLSD& notification, const LLSD& response) @@ -4347,7 +4373,28 @@ void process_money_balance_reply( LLMessageSystem* msg, void** )  		// *TODO: Translate  		LLSD args;  		args["MESSAGE"] = desc; -		LLNotificationsUtil::add("SystemMessage", args); + +		// this is a marker to retrieve avatar name from server message: +		// "<avatar name> paid you L$" +		const std::string marker = "paid you L$"; + +		// extract avatar name from system message +		std::string name = desc.substr(0, desc.find(marker, 0)); +		LLStringUtil::trim(name); + +		// if name extracted and name cache contains avatar id send loggable notification +		LLUUID from_id; +		if(name.size() > 0 && gCacheName->getUUID(name, from_id)) +		{ +			args["NAME"] = name; +			LLSD payload; +			payload["from_id"] = from_id; +			LLNotificationsUtil::add("PaymentRecived", args, payload); +		} +		else +		{ +			LLNotificationsUtil::add("SystemMessage", args); +		}  		// Once the 'recent' container gets large enough, chop some  		// off the beginning. diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 3554528d19..a0396bc790 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -750,6 +750,8 @@ LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string  		mReceivedCall(FALSE)  {  	// make sure URI reflects encoded version of other user's agent id +	// *NOTE: in case of Avaline call generated SIP URL will be incorrect. +	// But it will be overridden in LLVoiceChannelP2P::setSessionHandle() called when agent accepts call  	setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id));  } @@ -867,6 +869,10 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s  	}  	else  	{ +		LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL; +		// In case of incoming AvaLine call generated URI will be differ from original one. +		// This is because Avatar-2-Avatar URI is based on avatar UUID but Avaline is not. +		// See LLVoiceClient::sessionAddedEvent() -> setUUIDFromStringHash()  		setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID));  	} diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index cf6a4e45bd..cd297af99d 100644 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -1,7 +1,6 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
  <multi_floater
 -background_visible="true"
 -bg_color="yellow" 
 + can_minimize="false"
   can_resize="true"
   height="390"
   layout="topleft"
 diff --git a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml index d8534bfe0b..0856049374 100644 --- a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml @@ -22,13 +22,15 @@               bg_readonly_color="ChatHistoryBgColor"               bg_writeable_color="ChatHistoryBgColor"               follows="all" -	    left="1" +	         left="5"               top="20"               layout="topleft" -	    height="280" +	         height="275"               name="chat_history"               parse_highlights="true"               text_color="ChatHistoryTextColor"               text_readonly_color="ChatHistoryTextColor" -             width="320" /> +             right_widget_pad="5" +             left_widget_pad="0" +             width="315" />  </floater> diff --git a/indra/newview/skins/default/xui/en/floater_voice_controls.xml b/indra/newview/skins/default/xui/en/floater_voice_controls.xml index 04696ca2e7..1ebc51f504 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_controls.xml @@ -1,8 +1,10 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <floater   can_resize="true" - height="300" + height="270"   layout="topleft" + min_height="146" + min_width="190"     name="floater_voice_controls"   title="Voice Controls"   save_visibility="true" @@ -21,6 +23,10 @@          Conference Call      </string>      <string +     name="title_peer_2_peer"> +        Call with [NAME] +    </string> +    <string       name="no_one_near">          No one near      </string> @@ -74,6 +80,7 @@               width="20" />          </panel>          <layout_stack +         animate="false"           bottom="10"           clip="false"           follows="left|right|top" @@ -82,9 +89,11 @@           orientation="horizontal"           width="262">              <layout_panel +             auto_resize="false"                follows="left"               layout="topleft"               min_width="24" +             name="microphone_icon_panel"                top="0"               user_resize="false"               width="24"> @@ -98,9 +107,10 @@              </layout_panel>              <layout_panel               layout="topleft" +             name="volume_slider_panel"               top="0"               user_resize="false" -             width="258"> +             width="138">                  <slider_bar                   control_name="AudioLevelMic"                   follows="left|right|top" @@ -113,7 +123,25 @@                   tool_tip="Master Volume"                   top="0"                   value="0.75" -                 width="258" /> +                 width="138" /> +            </layout_panel> +            <layout_panel +             auto_resize="false" +             layout="topleft" +             min_width="100"  +             name="leave_btn_panel"  +             top="0" +             user_resize="false" +             visible="false"  +             width="100"> +                <button +                 follows="left|right|top" +                 height="24" +                 label="Leave Call" +                 left="0"  +                 name="leave_call_btn" +                 top="0"  +                 width="100" />              </layout_panel>          </layout_stack>      </panel> @@ -126,4 +154,13 @@       multi_select="true"       name="speakers_list"       width="282" /> +    <panel +     filename="panel_avatar_list_item.xml" +     follows="left|right|top" +     height="24" +     layout="topleft" +     left="0" +     name="non_avatar_caller" +     top="70" +     width="282" />  </floater> diff --git a/indra/newview/skins/default/xui/en/menu_bottomtray.xml b/indra/newview/skins/default/xui/en/menu_bottomtray.xml index a7abb223ba..7ef91a1d85 100644 --- a/indra/newview/skins/default/xui/en/menu_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/menu_bottomtray.xml @@ -52,4 +52,57 @@               function="CheckControl"               parameter="ShowSnapshotButton" />      </menu_item_check>         +    <menu_item_separator +     name="Separator" /> +    <menu_item_call +     label="Cut" +     name="NearbyChatBar_Cut"> +        <menu_item_call.on_click +         function="NearbyChatBar.Action" +         parameter="cut" /> +        <menu_item_call.on_enable +         function="NearbyChatBar.EnableMenuItem" +         parameter="can_cut" /> +    </menu_item_call> +    <menu_item_call +     label="Copy" +     name="NearbyChatBar_Copy"> +        <menu_item_call.on_click +         function="NearbyChatBar.Action" +         parameter="copy" /> +        <menu_item_call.on_enable +         function="NearbyChatBar.EnableMenuItem" +         parameter="can_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste" +     name="NearbyChatBar_Paste"> +        <menu_item_call.on_click +         function="NearbyChatBar.Action" +         parameter="paste" /> +        <menu_item_call.on_enable +         function="NearbyChatBar.EnableMenuItem" +         parameter="can_paste" /> +    </menu_item_call> +    <menu_item_call +     label="Delete" +     name="NearbyChatBar_Delete"> +        <menu_item_call.on_click +         function="NearbyChatBar.Action" +         parameter="delete" /> +        <menu_item_call.on_enable +         function="NearbyChatBar.EnableMenuItem" +         parameter="can_delete" /> +    </menu_item_call> +    <menu_item_call +     label="Select All" +     name="NearbyChatBar_Select_All"> +        <menu_item_call.on_click +         function="NearbyChatBar.Action" +         parameter="select_all" /> +        <menu_item_call.on_enable +         function="NearbyChatBar.EnableMenuItem" +         parameter="can_select_all" /> +    </menu_item_call> +  </menu> diff --git a/indra/newview/skins/default/xui/en/menu_imchiclet_adhoc.xml b/indra/newview/skins/default/xui/en/menu_imchiclet_adhoc.xml new file mode 100644 index 0000000000..eb5e31b57d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_imchiclet_adhoc.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu + height="101" + layout="topleft" + left="100" + mouse_opaque="false" + name="IMChiclet AdHoc Menu" + top="724" + visible="false" + width="128"> +    <menu_item_call +     label="End Session" +     layout="topleft" +     name="End Session"> +        <menu_item_call.on_click +         function="IMChicletMenu.Action" +         parameter="end" /> +    </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 895df62926..f48cc6d4bf 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4598,6 +4598,13 @@ Please select at least one type of content to search (PG, Mature, or Adult).    <notification     icon="notify.tga" +   name="PaymentRecived" +   type="notify"> +[MESSAGE] +  </notification> +   +  <notification +   icon="notify.tga"     name="EventNotification"     type="notify">  Event Notification: diff --git a/indra/newview/skins/default/xui/en/panel_chat_header.xml b/indra/newview/skins/default/xui/en/panel_chat_header.xml index 95d8b9cb1e..3e6ea84bf2 100644 --- a/indra/newview/skins/default/xui/en/panel_chat_header.xml +++ b/indra/newview/skins/default/xui/en/panel_chat_header.xml @@ -8,7 +8,7 @@       label="im_header"  	 layout="topleft"       name="im_header" -     width="300"> +     width="310">  		<avatar_icon           follows="left"           height="18" diff --git a/indra/newview/skins/default/xui/en/panel_picks.xml b/indra/newview/skins/default/xui/en/panel_picks.xml index 4f0d155876..4c2bd67337 100644 --- a/indra/newview/skins/default/xui/en/panel_picks.xml +++ b/indra/newview/skins/default/xui/en/panel_picks.xml @@ -162,16 +162,5 @@           tab_stop="false"           top="0"           width="50" /> -        <button -         enabled="false" -         follows="bottom|right" -         height="25" -         label="▼" -         layout="topleft" -         name="overflow_btn" -         right="-10" -         tab_stop="false" -         top="0" -         width="30" />          </panel>  </panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index fac0d5c60f..3aa5d3fae4 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -318,4 +318,37 @@       name="plain_text_chat_history"       top_pad="5"       width="400" /> +    <text +     left="30" +     height="20" +     width="300" +     top_pad="20"> +     Show IMs in: +    </text> +    <radio_group +     height="30" +     layout="topleft" +     left="30" +     control_name="ChatWindow" +     name="chat_window" +     top_pad="10" +     tool_tip="Show chat in multiple windows(by default) or in one multi-tabbed window (requires restart)" +     width="331"> +     <radio_item +      height="16" +      label="Multiple windows" +      layout="topleft" +      left="0" +      name="radio" +      top="0" +      width="150" /> +     <radio_item +      height="16" +      label="One window" +      layout="topleft" +      left_delta="145" +      name="radio2" +      top_delta="0" +      width="150" /> +    </radio_group>  </panel> | 
