Anonymous avatar Anonymous committed fe7484b

Merging revisions 2094-2099 of https://svn.aws.productengine.com/secondlife/pe/stable-2 into P:\svn\viewer-2.0.0, respecting ancestry
* Bugs: EXT-1612 EXT-1604 EXT-1163 EXT-1163 EXT-1167
* Dev: EXT-1516

Comments (0)

Files changed (15)

indra/newview/llbottomtray.cpp

 	case LLIMChiclet::TYPE_IM:
 		return getChicletPanel()->createChiclet<LLIMP2PChiclet>(session_id);
 	case LLIMChiclet::TYPE_GROUP:
+		return getChicletPanel()->createChiclet<LLIMGroupChiclet>(session_id);
 	case LLIMChiclet::TYPE_AD_HOC:
-		return getChicletPanel()->createChiclet<LLIMGroupChiclet>(session_id);
+		return getChicletPanel()->createChiclet<LLAdHocChiclet>(session_id);
 	case LLIMChiclet::TYPE_UNKNOWN:
 		break;
 	}

indra/newview/llchiclet.cpp

 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
 
+LLAdHocChiclet::Params::Params()
+: avatar_icon("avatar_icon")
+, unread_notifications("unread_notifications")
+, speaker("speaker")
+, show_speaker("show_speaker")
+{
+	// *TODO Vadim: Get rid of hardcoded values.
+	rect(LLRect(0, 25, 45, 0));
+
+	avatar_icon.name("avatar_icon");
+	avatar_icon.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+
+	// *NOTE dzaporozhan
+	// Changed icon height from 25 to 24 to fix ticket EXT-794.
+	// In some cases(after changing UI scale) 25 pixel height icon was 
+	// drawn incorrectly, i'm not sure why.
+	avatar_icon.rect(LLRect(0, 24, 25, 0));
+	avatar_icon.mouse_opaque(false);
+
+	unread_notifications.name("unread");
+	unread_notifications.rect(LLRect(25, 25, 45, 0));
+	unread_notifications.font(LLFontGL::getFontSansSerif());
+	unread_notifications.font_halign(LLFontGL::HCENTER);
+	unread_notifications.v_pad(5);
+	unread_notifications.text_color(LLColor4::white);
+	unread_notifications.mouse_opaque(false);
+
+	speaker.name("speaker");
+	speaker.rect(LLRect(45, 25, 65, 0));
+
+	show_speaker = false;
+}
+
+LLAdHocChiclet::LLAdHocChiclet(const Params& p)
+: LLIMChiclet(p)
+, mChicletIconCtrl(NULL)
+, mCounterCtrl(NULL)
+, mSpeakerCtrl(NULL)
+, mPopupMenu(NULL)
+{
+	LLChicletAvatarIconCtrl::Params avatar_params = p.avatar_icon;
+	mChicletIconCtrl = LLUICtrlFactory::create<LLChicletAvatarIconCtrl>(avatar_params);
+	addChild(mChicletIconCtrl);
+
+	LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications;
+	mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params);
+	addChild(mCounterCtrl);
+
+	setCounter(getCounter());
+	setShowCounter(getShowCounter());
+
+	LLChicletSpeakerCtrl::Params speaker_params = p.speaker;
+	mSpeakerCtrl = LLUICtrlFactory::create<LLChicletSpeakerCtrl>(speaker_params);
+	addChild(mSpeakerCtrl);
+
+	setShowSpeaker(p.show_speaker);
+}
+
+void LLAdHocChiclet::setSessionId(const LLUUID& session_id)
+{
+	LLChiclet::setSessionId(session_id);
+	LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
+	mChicletIconCtrl->setValue(im_session->mOtherParticipantID);
+}
+
+void LLAdHocChiclet::setCounter(S32 counter)
+{
+	mCounterCtrl->setCounter(counter);
+
+	if(getShowCounter())
+	{
+		LLRect counter_rect = mCounterCtrl->getRect();
+		LLRect required_rect = mCounterCtrl->getRequiredRect();
+		bool needs_resize = required_rect.getWidth() != counter_rect.getWidth();
+
+		if(needs_resize)
+		{
+			counter_rect.mRight = counter_rect.mLeft + required_rect.getWidth();
+			mCounterCtrl->reshape(counter_rect.getWidth(), counter_rect.getHeight());
+			mCounterCtrl->setRect(counter_rect);
+
+			onChicletSizeChanged();
+		}
+	}
+}
+
+LLRect LLAdHocChiclet::getRequiredRect()
+{
+	LLRect rect(0, 0, mChicletIconCtrl->getRect().getWidth(), 0);
+	if(getShowCounter())
+	{
+		rect.mRight += mCounterCtrl->getRequiredRect().getWidth();
+	}
+	if(getShowSpeaker())
+	{
+		rect.mRight += mSpeakerCtrl->getRect().getWidth();
+	}
+	return rect;
+}
+
+BOOL LLAdHocChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+	return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
 LLIMGroupChiclet::Params::Params()
 : group_icon("group_icon")
 {

indra/newview/llchiclet.h

 };
 
 /**
+ * Implements AD-HOC chiclet.
+ */
+class LLAdHocChiclet : public LLIMChiclet
+{
+public:
+	struct Params : public LLInitParam::Block<Params, LLChiclet::Params>
+	{
+		Optional<LLChicletAvatarIconCtrl::Params> avatar_icon;
+
+		Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications;
+
+		Optional<LLChicletSpeakerCtrl::Params> speaker;
+
+		Optional<bool>	show_speaker;
+
+		Params();
+	};
+
+	/**
+	 * Sets session id.
+	 * Session ID for group chat is actually Group ID.
+	 */
+	/*virtual*/ void setSessionId(const LLUUID& session_id);
+
+	/*
+	* Sets number of unread messages. Will update chiclet's width if number text 
+	* exceeds size of counter and notify it's parent about size change.
+	*/
+	/*virtual*/ void setCounter(S32);
+
+	/*
+	* Returns number of unread messages.
+	*/
+	/*virtual*/ S32 getCounter() { return mCounterCtrl->getCounter(); }
+
+	/*
+	* Returns rect, required to display chiclet.
+	* Width is the only valid value.
+	*/
+	/*virtual*/ LLRect getRequiredRect();
+
+protected:
+	LLAdHocChiclet(const Params& p);
+	friend class LLUICtrlFactory;
+
+	/*
+	* Displays popup menu.
+	*/
+	virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+
+private:
+
+	LLChicletAvatarIconCtrl* mChicletIconCtrl;
+	LLChicletNotificationCounterCtrl* mCounterCtrl;
+	LLChicletSpeakerCtrl* mSpeakerCtrl;
+	LLMenuGL* mPopupMenu;
+};
+
+/**
  * Implements Group chat chiclet.
  */
 class LLIMGroupChiclet : public LLIMChiclet, public LLGroupMgrObserver

indra/newview/llfloatercamera.cpp

 
 }
 
+void LLFloaterCamera::onClose(bool app_quitting)
+{
+	//We don't care of camera mode if app is quitting
+	if(!app_quitting)
+		switchMode(CAMERA_CTRL_MODE_ORBIT);
+}
 
 LLFloaterCamera::LLFloaterCamera(const LLSD& val)
 :	LLDockableFloater(NULL, val),

indra/newview/llfloatercamera.h

 	static void updateIfNotInAvatarViewMode();
 
 	virtual void onOpen(const LLSD& key);
+	virtual void onClose(bool app_quitting);
 
 	LLJoystickCameraRotate* mRotate;
 	LLJoystickCameraZoom*	mZoom;

indra/newview/llfolderview.cpp

 	{
 		const LLFolderViewItem* item = *selected_it;
 		const LLFolderViewEventListener* listener = item->getListener();
-		if (!listener || !listener->isItemMovable())
+
+		// *WARKAROUND: it is too many places where the "isItemRemovable" method should be changed with "const" modifier
+		if (!listener || !(const_cast<LLFolderViewEventListener*>(listener))->isItemRemovable())
 		{
 			return FALSE;
 		}

indra/newview/llfolderviewitem.h

 	virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
 	virtual S32 getItemHeight();
 	void setDontShowInHierarchy(bool dont_show) { mDontShowInHierarhy = dont_show; }
+	bool getDontShowInHierarchy() { return mDontShowInHierarhy; }
 
 	// applies filters to control visibility of inventory items
 	virtual void filter( LLInventoryFilter& filter);

indra/newview/llinventorybridge.cpp

 			// BAP - restrictions?
 			is_movable = true;
 		}
+
+		if (mUUID == gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE))
+		{
+			is_movable = FALSE; // It's generally movable but not into Favorites folder. EXT-1604
+		}
+
 		if( is_movable )
 		{
 			gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE );

indra/newview/llpanellandmarks.cpp

 	return false;
 }
 
+bool LLLandmarksPanel::isReceivedFolderSelected() const
+{
+	// Received Folder can be only in Landmarks accordion
+	if (mCurrentSelectedList != mLandmarksInventoryPanel) return false;
+
+	// *TODO: it should be filled with logic when EXT-976 is done.
+
+	llwarns << "Not implemented yet until EXT-976 is done." << llendl;
+
+	return false;
+}
 LLLandmark* LLLandmarksPanel::getCurSelectedLandmark() const
 {
 
 bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const
 {
 	std::string command_name = userdata.asString();
+
+	LLPlacesFolderView* rootFolderView = mCurrentSelectedList ?
+		static_cast<LLPlacesFolderView*>(mCurrentSelectedList->getRootFolder()) : NULL;
+
+	if (NULL == rootFolderView) return false;
+
 	if("category" == command_name)
 	{
 		return mCurrentSelectedList == mLandmarksInventoryPanel; 
 	}
-	else if("paste" == command_name)
+	else if("paste" == command_name || "rename" == command_name || "cut" == command_name || "delete" == command_name)
 	{
-		return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->canPaste() : false;
+		return canSelectedBeModified(command_name);
 	}
 	else if ( "sort_by_date" == command_name)
 	{
 	// do not allow teleport and more info for multi-selections
 	else if ("teleport" == command_name || "more_info" == command_name)
 	{
-		return mCurrentSelectedList ?
-			static_cast<LLPlacesFolderView*>(mCurrentSelectedList->getRootFolder())->getSelectedCount() == 1 : false;
+		return rootFolderView->getSelectedCount() == 1;
 	}
 	// we can add folder, or change item/folder only in Landmarks Accordion
-	else if ("add_folder" == command_name || "rename" == command_name || "delete" == command_name)
+	else if ("add_folder" == command_name)
 	{
 		return mLandmarksInventoryPanel == mCurrentSelectedList;
 	}
+	else
+	{
+		llwarns << "Unprocessed command has come: " << command_name << llendl;
+	}
 
 	return true;
 }
 	}
 }
 
+/*
+Processes such actions: cut/rename/delete/paste actions
+
+Rules:
+ 1. We can't perform any action in Library
+ 2. For Landmarks we can:
+	- cut/rename/delete in any other accordions
+	- paste - only in Favorites, Landmarks accordions
+ 3. For Folders we can: perform any action in Landmarks accordion, except Received folder
+ 4. We can not paste folders from Clipboard (processed by LLFolderView::canPaste())
+ 5. Check LLFolderView/Inventory Bridges rules
+ */
+bool LLLandmarksPanel::canSelectedBeModified(const std::string& command_name) const
+{
+	// validate own rules first
+
+	// nothing can be modified in Library
+	if (mLibraryInventoryPanel == mCurrentSelectedList) return false;
+
+	bool can_be_modified = false;
+
+	// landmarks can be modified in any other accordion...
+	if (isLandmarkSelected())
+	{
+		can_be_modified = true;
+
+		// we can modify landmarks anywhere except paste to My Inventory
+		if ("paste" == command_name)
+		{
+			can_be_modified = (mCurrentSelectedList != mMyInventoryPanel);
+		}
+	}
+	else
+	{
+		// ...folders only in the Landmarks accordion...
+		can_be_modified = mLandmarksInventoryPanel == mCurrentSelectedList;
+
+		// ...except "Received" folder
+		can_be_modified &= !isReceivedFolderSelected();
+	}
+
+	// then ask LLFolderView permissions
+	if (can_be_modified)
+	{
+		if ("cut" == command_name)
+		{
+			can_be_modified = mCurrentSelectedList->getRootFolder()->canCut();
+		}
+		else if ("rename" == command_name)
+		{
+			can_be_modified = getCurSelectedItem()->getListener()->isItemRenameable();
+		}
+		else if ("delete" == command_name)
+		{
+			can_be_modified = getCurSelectedItem()->getListener()->isItemRemovable();
+		}
+		else if("paste" == command_name)
+		{
+			return mCurrentSelectedList->getRootFolder()->canPaste();
+		}
+		else
+		{
+			llwarns << "Unprocessed command has come: " << command_name << llendl;
+		}
+	}
+
+	return can_be_modified;
+}
+
 void LLLandmarksPanel::onPickPanelExit( LLPanelPickEdit* pick_panel, LLView* owner, const LLSD& params)
 {
 	pick_panel->setVisible(FALSE);

indra/newview/llpanellandmarks.h

 	 * @return true - if current selected panel is not null and selected item is a landmark
 	 */
 	bool isLandmarkSelected() const;
+	bool isReceivedFolderSelected() const;
 	LLLandmark* getCurSelectedLandmark() const;
 	LLFolderViewItem* getCurSelectedItem () const;
 	void updateSortOrder(LLInventoryPanel* panel, bool byDate);
 	void onFoldingAction(const LLSD& command_name);
 	bool isActionEnabled(const LLSD& command_name) const;
 	void onCustomAction(const LLSD& command_name);
+
+	/**
+	 * Determines if selected item can be modified via context/gear menu.
+	 *
+	 * It validates Places Landmarks rules first. And then LLFolderView permissions.
+	 * For now it checks cut/rename/delete/paste actions.
+	 */
+	bool canSelectedBeModified(const std::string& command_name) const;
 	void onPickPanelExit( LLPanelPickEdit* pick_panel, LLView* owner, const LLSD& params);
 
 private:

indra/newview/llpanelpicks.cpp

 
 void LLPanelPicks::updateData()
 {
-	LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId());
+	// Send Picks request only when we need to, not on every onOpen(during tab switch).
+	if(isDirty())
+	{
+		mPicksList->clear();
+		LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId());
+	}
 }
 
 void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type)
 				picture->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this));
 			}
 
+			resetDirty();
 			LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this);
 			updateButtons();
 		}
 	// Disable buttons when viewing profile for first time
 	if(getAvatarId() != id)
 	{
-		clear();
-
 		childSetEnabled(XML_BTN_INFO,FALSE);
 		childSetEnabled(XML_BTN_TELEPORT,FALSE);
 		childSetEnabled(XML_BTN_SHOW_ON_MAP,FALSE);
 	if(getAvatarId() != id)
 	{
 		mPicksList->goToTop();
+		// Set dummy value to make panel dirty and make it reload picks
+		setValue(LLSD());
 	}
 
 	LLPanelProfileTab::onOpen(key);

indra/newview/llpanelprofile.cpp

 
 void LLPanelProfile::notifyParent(const LLSD& info)
 {
+	std::string action = info["action"];
 	// lets update Picks list after Pick was saved
-	if("save_new_pick" == info["action"])
+	if("save_new_pick" == action)
 	{
 		onOpen(info);
 		return;

indra/newview/llpanelprofileview.cpp

 
 static std::string PANEL_NOTES = "panel_notes";
 static const std::string PANEL_PROFILE = "panel_profile";
+static const std::string PANEL_PICKS = "panel_picks";
 
 LLPanelProfileView::LLPanelProfileView()
 :	LLPanelProfile()
 
 void LLPanelProfileView::onBackBtnClick()
 {
+	// Set dummy value to make picks panel dirty, 
+	// This will make Picks reload on next open.
+	getTabContainer()[PANEL_PICKS]->setValue(LLSD());
+
 	LLSideTrayPanelContainer* parent = dynamic_cast<LLSideTrayPanelContainer*>(getParent());
 	if(parent)
 	{

indra/newview/skins/default/xui/en/menu_places_gear_folder.xml

         <on_click
          function="Places.LandmarksGear.CopyPaste.Action"
          parameter="cut" />
+        <on_enable
+         function="Places.LandmarksGear.Enable"
+         parameter="cut" />
     </menu_item_call>
     <menu_item_call
      label="Copy"
         <on_click
          function="Places.LandmarksGear.CopyPaste.Action"
          parameter="rename" />
+        <on_enable
+         function="Places.LandmarksGear.Enable"
+         parameter="rename" />
     </menu_item_call>
     <menu_item_call
      label="Delete"
         <on_click
          function="Places.LandmarksGear.CopyPaste.Action"
          parameter="delete" />
+        <on_enable
+         function="Places.LandmarksGear.Enable"
+         parameter="delete" />
     </menu_item_call>
     <menu_item_separator
      layout="topleft" />

indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml

         <on_click
          function="Places.LandmarksGear.CopyPaste.Action"
          parameter="cut" />
+        <on_enable
+         function="Places.LandmarksGear.Enable"
+         parameter="cut" />
     </menu_item_call>
     <menu_item_call
      label="Copy Landmark"
         <on_click
          function="Places.LandmarksGear.CopyPaste.Action"
          parameter="rename" />
+        <on_enable
+         function="Places.LandmarksGear.Enable"
+         parameter="rename" />
     </menu_item_call>
     <menu_item_call
      label="Delete"
         <on_click
          function="Places.LandmarksGear.CopyPaste.Action"
          parameter="delete" />
+        <on_enable
+         function="Places.LandmarksGear.Enable"
+         parameter="delete" />
     </menu_item_call>
     <menu_item_separator
      layout="topleft" />
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.