diff options
26 files changed, 5370 insertions, 39 deletions
| diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 0a54163644..674b5f9241 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -75,10 +75,11 @@ add_subdirectory(${VIEWER_PREFIX}test)  # viewer media plugins  add_subdirectory(${LIBS_OPEN_PREFIX}media_plugins) -# llplugin testbed code (is this the right way to include it?) -if (LL_TESTS AND NOT LINUX) -  add_subdirectory(${VIEWER_PREFIX}test_apps/llplugintest) -endif (LL_TESTS AND NOT LINUX) +  # llplugin testbed code (is this the right way to include it?) +  if (LL_TESTS AND NOT LINUX) +    add_subdirectory(${VIEWER_PREFIX}test_apps/llplugintest) +    add_subdirectory(${VIEWER_PREFIX}test_apps/llfbconnecttest) +  endif (LL_TESTS AND NOT LINUX)  if (LINUX)    add_subdirectory(${VIEWER_PREFIX}linux_crash_logger) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4f7ce88165..6b7fa7d842 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -457,6 +457,9 @@ set(viewer_SOURCE_FILES      llpathfindingobjectlist.cpp      llpathfindingpathtool.cpp      llpersistentnotificationstorage.cpp +    llpersonfolderview.cpp +    llpersonmodelcommon.cpp +    llpersontabview.cpp      llphysicsmotion.cpp      llphysicsshapebuilderutil.cpp      llpipelinelistener.cpp @@ -499,6 +502,7 @@ set(viewer_SOURCE_FILES      llsidetraypanelcontainer.cpp      llsky.cpp      llslurl.cpp +    llsociallist.cpp      llspatialpartition.cpp      llspeakers.cpp      llspeakingindicatormanager.cpp @@ -1025,6 +1029,9 @@ set(viewer_HEADER_FILES      llpathfindingobjectlist.h      llpathfindingpathtool.h      llpersistentnotificationstorage.h +    llpersonfolderview.h +    llpersonmodelcommon.h +    llpersontabview.h      llphysicsmotion.h      llphysicsshapebuilderutil.h      llpipelinelistener.h @@ -1068,6 +1075,7 @@ set(viewer_HEADER_FILES      llsidetraypanelcontainer.h      llsky.h      llslurl.h +    llsociallist.h      llspatialpartition.h      llspeakers.h      llspeakingindicatormanager.h diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 3adf956ae3..861717669f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2598,20 +2598,38 @@ bool LLAppViewer::initConfiguration()      // What can happen is that someone can use IE (or potentially       // other browsers) and do the rough equivalent of command       // injection and steal passwords. Phoenix. SL-55321 + +	LLSLURL option_slurl; +      if(clp.hasOption("url"))      { -		LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); +		option_slurl = LLSLURL(clp.getOption("url")[0]); +		LLStartUp::setStartSLURL(option_slurl);  		if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION)   		{    			LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); -			 -		}   +		}      }      else if(clp.hasOption("slurl"))      { -		LLSLURL start_slurl(clp.getOption("slurl")[0]); -		LLStartUp::setStartSLURL(start_slurl); +		option_slurl = LLSLURL(clp.getOption("slurl")[0]); +		LLStartUp::setStartSLURL(option_slurl);      } +	 +	//RN: if we received a URL, hand it off to the existing instance. +	// don't call anotherInstanceRunning() when doing URL handoff, as +	// it relies on checking a marker file which will not work when running +	// out of different directories + +	if (option_slurl.isValid() && +		(gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) +	{ +		if (sendURLToOtherInstance(option_slurl.getSLURLString())) +		{ +			// successfully handed off URL to existing instance, exit +			return false; +		} +	}  	const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent");  	if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) @@ -2702,21 +2720,6 @@ bool LLAppViewer::initConfiguration()  #endif  	LLStringUtil::truncate(gWindowTitle, 255); -	//RN: if we received a URL, hand it off to the existing instance. -	// don't call anotherInstanceRunning() when doing URL handoff, as -	// it relies on checking a marker file which will not work when running -	// out of different directories - -	if (LLStartUp::getStartSLURL().isValid() && -		(gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) -	{ -		if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString())) -		{ -			// successfully handed off URL to existing instance, exit -			return false; -		} -	} -  	if (!gSavedSettings.getBOOL("AllowMultipleViewers"))  	{  	    // diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index b88888da3b..21ba3a444b 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -346,8 +346,11 @@ void LLFloaterIMContainer::onStubCollapseButtonClicked()  void LLFloaterIMContainer::onSpeakButtonClicked()  { -	LLAgent::toggleMicrophone("speak"); -	updateSpeakBtnState(); +	//LLAgent::toggleMicrophone("speak"); +	//updateSpeakBtnState(); + +	LLParticipantList* session_model = dynamic_cast<LLParticipantList*>(mConversationsItems[LLUUID(NULL)]); +	session_model->addTestAvatarAgents();  }  void LLFloaterIMContainer::onExpandCollapseButtonClicked()  { diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 4138558bad..03135ce580 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -28,6 +28,8 @@  // libs  #include "llavatarname.h" +#include "llconversationview.h" +#include "llfloaterimcontainer.h"  #include "llfloaterreg.h"  #include "llfloatersidepanelcontainer.h"  #include "llmenubutton.h" @@ -48,7 +50,10 @@  #include "llavataractions.h"  #include "llavatarlist.h"  #include "llavatarlistitem.h" +#include "llavatarnamecache.h"  #include "llcallingcard.h"			// for LLAvatarTracker +#include "llcallbacklist.h" +#include "llerror.h"  #include "llfloateravatarpicker.h"  //#include "llfloaterminiinspector.h"  #include "llfriendcard.h" @@ -57,25 +62,65 @@  #include "llinventoryobserver.h"  #include "llnetmap.h"  #include "llpanelpeoplemenus.h" +#include "llparticipantlist.h" +#include "llpersonfolderview.h" +#include "llpersonmodelcommon.h" +#include "llpersontabview.h"  #include "llsidetraypanelcontainer.h"  #include "llrecentpeople.h"  #include "llviewercontrol.h"		// for gSavedSettings  #include "llviewermenu.h"			// for gMenuHolder  #include "llvoiceclient.h"  #include "llworld.h" +#include "llsociallist.h"  #include "llspeakers.h" +#include "llfloaterwebcontent.h" +#include "llurlaction.h" +#include "llcommandhandler.h"  #define FRIEND_LIST_UPDATE_TIMEOUT	0.5  #define NEARBY_LIST_UPDATE_INTERVAL 1 +#define FBCTEST_LIST_UPDATE_INTERVAL 0.25  static const std::string NEARBY_TAB_NAME	= "nearby_panel";  static const std::string FRIENDS_TAB_NAME	= "friends_panel";  static const std::string GROUP_TAB_NAME		= "groups_panel";  static const std::string RECENT_TAB_NAME	= "recent_panel";  static const std::string BLOCKED_TAB_NAME	= "blocked_panel"; // blocked avatars - +static const std::string FBCTEST_TAB_NAME	= "fbctest_panel"; +static const std::string FBCTESTTWO_TAB_NAME	= "fbctesttwo_panel";  static const std::string COLLAPSED_BY_USER  = "collapsed_by_user"; +class LLFacebookConnectHandler : public LLCommandHandler +{ +public: +	LLFacebookConnectHandler() : LLCommandHandler("fbc", UNTRUSTED_THROTTLE), mPanelPeople(NULL) { } + +	LLPanelPeople* mPanelPeople; + +	bool handle(const LLSD& tokens, const LLSD& query_map, +				LLMediaCtrl* web) +	{ +		if (tokens.size() > 0) +		{ +			if (tokens[0].asString() == "connect") +			{ +				if (query_map.has("code")) +				{ +					if (mPanelPeople) +					{ +						mPanelPeople->connectToFacebook(query_map["code"]); +						mPanelPeople = NULL; +					} +				} +				return true; +			} +		} +		return false; +	} +}; +LLFacebookConnectHandler gFacebookConnectHandler; +  /** Comparator for comparing avatar items by last interaction date */  class LLAvatarItemRecentComparator : public LLAvatarItemComparator  { @@ -489,10 +534,52 @@ public:  	}  }; +/** + * Periodically updates the FBC test list after a login is initiated. + *  + * The period is defined by FBCTEST_LIST_UPDATE_INTERVAL constant. + */ +class LLFbcTestListUpdater : public LLAvatarListUpdater +{ +	LOG_CLASS(LLFbcTestListUpdater); + +public: +	LLFbcTestListUpdater(callback_t cb) +	:	LLAvatarListUpdater(cb, FBCTEST_LIST_UPDATE_INTERVAL) +	{ +		setActive(false); +	} + +	/*virtual*/ void setActive(bool val) +	{ +		if (val) +		{ +			// update immediately and start regular updates +			update(); +			mEventTimer.start();  +		} +		else +		{ +			// stop regular updates +			mEventTimer.stop(); +		} +	} + +	/*virtual*/ BOOL tick() +	{ +		update(); +		return FALSE; +	} +private: +}; +  //=============================================================================  LLPanelPeople::LLPanelPeople()  	:	LLPanel(), +		mConnectedToFbc(false), +		mPersonFolderView(NULL), +		mTryToConnectToFbc(true),  		mTabContainer(NULL),  		mOnlineFriendList(NULL),  		mAllFriendList(NULL), @@ -504,8 +591,15 @@ LLPanelPeople::LLPanelPeople()  	mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList,	this));  	mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList,	this));  	mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList,	this)); +	mFbcTestListUpdater = new LLFbcTestListUpdater(boost::bind(&LLPanelPeople::updateFbcTestList,	this));  	mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this)); +	mCommitCallbackRegistrar.add("People.loginFBC", boost::bind(&LLPanelPeople::onLoginFbcButtonClicked, this)); +	mCommitCallbackRegistrar.add("People.requestFBC", boost::bind(&LLPanelPeople::onFacebookAppRequestClicked, this)); +	mCommitCallbackRegistrar.add("People.sendFBC", boost::bind(&LLPanelPeople::onFacebookAppSendClicked, this)); +	mCommitCallbackRegistrar.add("People.testaddFBC", boost::bind(&LLPanelPeople::onFacebookTestAddClicked, this)); +	mCommitCallbackRegistrar.add("People.testaddFBCFolderView", boost::bind(&LLPanelPeople::addTestParticipant, this)); +  	mCommitCallbackRegistrar.add("People.AddFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this));  	mCommitCallbackRegistrar.add("People.AddFriendWizard",	boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked,	this));  	mCommitCallbackRegistrar.add("People.DelFriend",		boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked,	this)); @@ -532,11 +626,14 @@ LLPanelPeople::~LLPanelPeople()  	delete mNearbyListUpdater;  	delete mFriendListUpdater;  	delete mRecentListUpdater; +	delete mFbcTestListUpdater;  	if(LLVoiceClient::instanceExists())  	{  		LLVoiceClient::getInstance()->removeObserver(this);  	} + +	if (mFbcTestBrowserHandle.get()) mFbcTestBrowserHandle.get()->die();  }  void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list) @@ -571,6 +668,7 @@ BOOL LLPanelPeople::postBuild()  	getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));  	getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));  	getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); +	getChild<LLFilterEditor>("fbc_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));  	mTabContainer = getChild<LLTabContainer>("tabs");  	mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); @@ -616,6 +714,74 @@ BOOL LLPanelPeople::postBuild()  	mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);  	mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); +	LLPanel * social_tab = getChild<LLPanel>(FBCTEST_TAB_NAME); +	mFacebookFriends = social_tab->getChild<LLSocialList>("facebook_friends"); +	social_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFbcTestListUpdater, _2)); + +	//===Test START======================================================================== + +	LLPanel * socialtwo_tab = getChild<LLPanel>(FBCTESTTWO_TAB_NAME); + +	//Create folder view +	LLPersonModelCommon* base_item = new LLPersonModelCommon(mPersonFolderViewModel); + +	LLPersonFolderView::Params folder_view_params(LLUICtrlFactory::getDefaultParams<LLPersonFolderView>()); +     +	folder_view_params.parent_panel = socialtwo_tab; +	folder_view_params.listener = base_item; +	folder_view_params.view_model = &mPersonFolderViewModel; +	folder_view_params.root = NULL; +	folder_view_params.use_ellipses = false; +	folder_view_params.options_menu = "menu_conversation.xml"; +	folder_view_params.name = "fbcfolderview"; +	mPersonFolderView = LLUICtrlFactory::create<LLPersonFolderView>(folder_view_params); + +	//Create scroller +	LLRect scroller_view_rect = socialtwo_tab->getRect(); +	scroller_view_rect.mTop -= 2+27; // 27 is the height of the top toolbar +	scroller_view_rect.mRight -= 4; +	scroller_view_rect.mLeft += 2; +	LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>()); +	scroller_params.rect(scroller_view_rect); + +	LLScrollContainer* scroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); +	socialtwo_tab->addChildInBack(scroller); +	scroller->addChild(mPersonFolderView); +	scroller->setFollowsAll(); +	mPersonFolderView->setScrollContainer(scroller); +	mPersonFolderView->setFollowsAll(); + +	//Create a person tab +	LLPersonTabModel* item = new LLPersonTabModel("Facebook Friends", mPersonFolderViewModel); +	LLPersonTabView::Params params; +	params.name = item->getDisplayName(); +	params.root = mPersonFolderView; +	params.listener = item; +	params.tool_tip = params.name; +	LLPersonTabView * widget = LLUICtrlFactory::create<LLPersonTabView>(params); +	widget->addToFolder(mPersonFolderView); + +	mPersonFolderView->mPersonFolderModelMap[item->getID()] = item; +	mPersonFolderView->mPersonFolderViewMap[item->getID()] = widget; + +	//Create a person tab +	item = new LLPersonTabModel("Facebook Friends Tab Two", mPersonFolderViewModel); +	params.name = item->getDisplayName(); +	params.root = mPersonFolderView; +	params.listener = item; +	params.tool_tip = params.name; +	widget = LLUICtrlFactory::create<LLPersonTabView>(params); +	widget->addToFolder(mPersonFolderView); + +	mPersonFolderView->mPersonFolderModelMap[item->getID()] = item; +	mPersonFolderView->mPersonFolderViewMap[item->getID()] = widget; +	 +	gIdleCallbacks.addFunction(idle, this); + +	//===Test END======================================================================== + + +  	setSortOrder(mRecentList,		(ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"),	false);  	setSortOrder(mAllFriendList,	(ESortOrder)gSavedSettings.getU32("FriendsSortOrder"),		false);  	setSortOrder(mNearbyList,		(ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"),	false); @@ -664,6 +830,15 @@ BOOL LLPanelPeople::postBuild()  	// Must go after setting commit callback and initializing all pointers to children.  	mTabContainer->selectTabByName(NEARBY_TAB_NAME); +	mFBCGearButton = getChild<LLMenuButton>("fbc_options_btn"); + +	LLToggleableMenu* fbc_menu  = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_gear_fbc.xml",  gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +	if(fbc_menu) +	{ +		mFBCMenuHandle = fbc_menu->getHandle(); +		mFBCGearButton->setMenu(fbc_menu); +	} +  	LLVoiceClient::getInstance()->addObserver(this);  	// call this method in case some list is empty and buttons can be in inconsistent state @@ -686,6 +861,12 @@ void LLPanelPeople::onChange(EStatusType status, const std::string &channelURI,  	updateButtons();  } +void LLPanelPeople::idle(void * user_data) +{ +	LLPanelPeople * self = static_cast<LLPanelPeople *>(user_data); +	self->mPersonFolderView->update(); +} +  void LLPanelPeople::updateFriendListHelpText()  {  	// show special help text for just created account to help finding friends. EXT-4836 @@ -786,6 +967,21 @@ void LLPanelPeople::updateRecentList()  	mRecentList->setDirty();  } +void LLPanelPeople::updateFbcTestList() +{ +	if (mTryToConnectToFbc) +	{	 +		// try to reconnect to facebook! +		tryToReconnectToFacebook(); + +		// don't try again +		mTryToConnectToFbc = false; +		 +		// stop updating +		mFbcTestListUpdater->setActive(false); +	} +} +  void LLPanelPeople::updateButtons()  {  	std::string cur_tab		= getActiveTabName(); @@ -870,6 +1066,13 @@ LLUUID LLPanelPeople::getCurrentItemID() const  	if (cur_tab == BLOCKED_TAB_NAME)  		return LLUUID::null; // FIXME? +	 +	if (cur_tab == FBCTEST_TAB_NAME) +		return LLUUID::null; + +	if (cur_tab == FBCTESTTWO_TAB_NAME) +		return LLUUID::null; +  	llassert(0 && "unknown tab selected");  	return LLUUID::null; @@ -893,6 +1096,10 @@ void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const  		mGroupList->getSelectedUUIDs(selected_uuids);  	else if (cur_tab == BLOCKED_TAB_NAME)  		selected_uuids.clear(); // FIXME? +	else if (cur_tab == FBCTEST_TAB_NAME) +		return; +	else if (cur_tab == FBCTESTTWO_TAB_NAME) +		return;  	else  		llassert(0 && "unknown tab selected"); @@ -989,23 +1196,23 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string)  	{  		// store accordion tabs opened/closed state before any manipulation with accordion tabs  		if (!saved_filter.empty()) -	{ -		notifyChildren(LLSD().with("action","store_state")); -	} +        { +            notifyChildren(LLSD().with("action","store_state")); +        }  		mOnlineFriendList->setNameFilter(filter);  		mAllFriendList->setNameFilter(filter); -	setAccordionCollapsedByUser("tab_online", false); -	setAccordionCollapsedByUser("tab_all", false); -	showFriendsAccordionsIfNeeded(); +        setAccordionCollapsedByUser("tab_online", false); +        setAccordionCollapsedByUser("tab_all", false); +        showFriendsAccordionsIfNeeded();  		// restore accordion tabs state _after_ all manipulations  		if(saved_filter.empty()) -	{ -		notifyChildren(LLSD().with("action","restore_state")); -	} -} +        { +            notifyChildren(LLSD().with("action","restore_state")); +        } +    }  	else if (cur_tab == GROUP_TAB_NAME)  	{  		mGroupList->setNameFilter(filter); @@ -1014,6 +1221,10 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string)  	{  		mRecentList->setNameFilter(filter);  	} +    else if (cur_tab == FBCTESTTWO_TAB_NAME) +    { +        mPersonFolderViewModel.getFilter().setFilterSubString(filter); +    }  }  void LLPanelPeople::onTabSelected(const LLSD& param) @@ -1225,7 +1436,7 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata)  		mAllFriendList->showPermissions(show_permissions);  		mOnlineFriendList->showPermissions(show_permissions);  	} -} +	}  void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata)  { @@ -1446,4 +1657,265 @@ bool LLPanelPeople::isAccordionCollapsedByUser(const std::string& name)  	return isAccordionCollapsedByUser(getChild<LLUICtrl>(name));  } +void LLPanelPeople::openFacebookWeb(std::string url) +{ +	gFacebookConnectHandler.mPanelPeople = this; +	LLUrlAction::openURLExternal(url); +} + +void LLPanelPeople::showFacebookFriends(const LLSD& friends) +{ +	mFacebookFriends->clear(); + +	for (LLSD::map_const_iterator i = friends.beginMap(); i != friends.endMap(); ++i) +	{ +		std::string name = i->second["name"].asString(); +		LLUUID agent_id = i->second.has("agent_id") ? i->second["agent_id"].asUUID() : LLUUID(NULL); +		 +		//add to avatar list +		mFacebookFriends->addNewItem(agent_id, name, false); + +		//Add to folder view +		LLPersonTabModel * session_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderView->mPersonFolderModelMap.begin()->second); +		if(session_model) +		{ +			addParticipantToModel(session_model, agent_id, name); +		} +	} +} + +void LLPanelPeople::addTestParticipant() +{ +    std::string suffix("Aa"); +    std::string prefix("Test Name"); +	for(int i = 0; i < 300; ++i) +	{ +		LLPersonTabModel * person_folder_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderView->mPersonFolderModelMap.begin()->second); +        std::string name = prefix + " " + suffix; +		addParticipantToModel(person_folder_model, gAgent.getID(), name); +        // Next suffix : Aa, Ab, Ac ... Az, Ba, Bb, Bc ... Bz, Ca, Cb ... +        suffix[1]+=1; +        if (suffix[1]=='{') +        { +            suffix[1]='a'; +            suffix[0]+=1; +            if (suffix[0]=='[') +                suffix[0]='A'; +        } +	} +} + +void LLPanelPeople::addParticipantToModel(LLPersonTabModel * person_folder_model, const LLUUID& agent_id, const std::string& name) +{ +	LLPersonModel* person_model = NULL; + +	LLAvatarName avatar_name; +	bool avatar_name_exists = LLAvatarNameCache::get(agent_id, &avatar_name); + +	std::string aggregated_name = avatar_name_exists ? name + " (" + avatar_name.getDisplayName() + ") " : name; + +	person_model = new LLPersonModel(agent_id, aggregated_name, mPersonFolderViewModel); +	person_folder_model->addParticipant(person_model); +} + +void LLPanelPeople::hideFacebookFriends() +{ +	mFacebookFriends->clear(); +} + +class FacebookConnectResponder : public LLHTTPClient::Responder +{ +public: + +	LLPanelPeople * mPanelPeople; + +	FacebookConnectResponder(LLPanelPeople * panel_people) : mPanelPeople(panel_people) {} + +	/*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << content << llendl; +			 +			// grab some graph data now that we are connected +			mPanelPeople->mConnectedToFbc = true; +			mPanelPeople->loadFacebookFriends(); +		} +		else +		{ +			llinfos << "failed to get response. reason: " << reason << " status: " << status << llendl; +		} +	} + +	/*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (status == 302) +		{ +			mPanelPeople->openFacebookWeb(content["location"]); +		} +	} +}; + +class FacebookDisconnectResponder : public LLHTTPClient::Responder +{ +public: + +	LLPanelPeople * mPanelPeople; + +	FacebookDisconnectResponder(LLPanelPeople * panel_people) : mPanelPeople(panel_people) {} + +	/*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << content << llendl; +			 +			// hide all the facebook stuff +			mPanelPeople->mConnectedToFbc = false; +			mPanelPeople->hideFacebookFriends(); +		} +		else +		{ +			llinfos << "failed to get response. reason: " << reason << " status: " << status << llendl; +		} +	} +}; + +class FacebookConnectedResponder : public LLHTTPClient::Responder +{ +public: + +	LLPanelPeople * mPanelPeople; +	bool mShowLoginIfNotConnected; + +	FacebookConnectedResponder(LLPanelPeople * panel_people, bool show_login_if_not_connected) : mPanelPeople(panel_people), mShowLoginIfNotConnected(show_login_if_not_connected) {} + +	/*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << content << llendl; + +			// grab some graph data if already connected +			mPanelPeople->mConnectedToFbc = true; +			mPanelPeople->loadFacebookFriends(); +		} +		else +		{ +			llinfos << "failed to get response. reason: " << reason << " status: " << status << llendl; + +			// show the facebook login page if not connected yet +			if (status == 404 && mShowLoginIfNotConnected) +			{ +				mPanelPeople->connectToFacebook(); +			} +		} +	} +}; + +class FacebookFriendsResponder : public LLHTTPClient::Responder +{ +public: + +	LLPanelPeople * mPanelPeople; + +	FacebookFriendsResponder(LLPanelPeople * panel_people) : mPanelPeople(panel_people) {} + +	/*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << content << llendl; + +			// display the list of friends +			mPanelPeople->showFacebookFriends(content); +		} +		else +		{ +			llinfos << "failed to get response. reason: " << reason << " status: " << status << llendl; +		} +	} + +	/*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (status == 302) +		{ +			mPanelPeople->openFacebookWeb(content["location"]); +		} +	} +}; + +void LLPanelPeople::loadFacebookFriends() +{ +	LLHTTPClient::get(getFacebookConnectURL("/friend"), new FacebookFriendsResponder(this)); +} + +void LLPanelPeople::tryToReconnectToFacebook() +{ +	if (!mConnectedToFbc) +	{ +		LLHTTPClient::get(getFacebookConnectURL("/connection"), new FacebookConnectedResponder(this, false)); +	} +} + +void LLPanelPeople::connectToFacebook(const std::string& auth_code) +{ +	LLSD body; +	if (!auth_code.empty()) +		body["code"] = auth_code; + +	LLHTTPClient::put(getFacebookConnectURL("/connection"), body, new FacebookConnectResponder(this)); +} + +void LLPanelPeople::disconnectFromFacebook() +{ +	LLHTTPClient::del(getFacebookConnectURL("/connection"), new FacebookDisconnectResponder(this)); +} + +std::string LLPanelPeople::getFacebookConnectURL(const std::string& route) +{ +	static std::string sFacebookConnectUrl = gAgent.getRegion()->getCapability("FacebookConnect"); +	std::string url = sFacebookConnectUrl + route; +	llinfos << url << llendl; +	return url; +} + +void LLPanelPeople::onLoginFbcButtonClicked() +{ +	if (mConnectedToFbc) +	{ +		disconnectFromFacebook(); +	} +	else +	{ +		LLHTTPClient::get(getFacebookConnectURL("/connection"), new FacebookConnectedResponder(this, true)); +	} +} + +void LLPanelPeople::onFacebookAppRequestClicked() +{ +} + +void LLPanelPeople::onFacebookAppSendClicked() +{ +} + +static LLFastTimer::DeclareTimer FTM_AVATAR_LIST_TEST("avatar list test"); + +void LLPanelPeople::onFacebookTestAddClicked() +{ +	LLFastTimer _(FTM_AVATAR_LIST_TEST); + +	mFacebookFriends->clear(); + +	LL_INFOS("LLPanelPeople") << "start adding 300 users" << LL_ENDL; + +	for(int i = 0; i < 300; ++i) +	{ +		mFacebookFriends->addNewItem(LLUUID(), "Test", false); +	} + +	LL_INFOS("LLPanelPeople") << "finished adding 300 users" << LL_ENDL; +} +  // EOF diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 4740964dee..943d84ac1d 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -22,7 +22,7 @@   *    * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA   * $/LicenseInfo$ - */ + */   #ifndef LL_LLPANELPEOPLE_H  #define LL_LLPANELPEOPLE_H @@ -30,12 +30,17 @@  #include <llpanel.h>  #include "llcallingcard.h" // for avatar tracker +#include "llpersonmodelcommon.h" +#include "llfloaterwebcontent.h"  #include "llvoiceclient.h"  class LLAvatarList; +class LLAvatarListSocial;  class LLAvatarName;  class LLFilterEditor;  class LLGroupList; +class LLPersonFolderView; +class LLSocialList;  class LLMenuButton;  class LLTabContainer; @@ -55,6 +60,23 @@ public:  	// when voice is available  	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); +	static void idle(void * user_data); + +	void openFacebookWeb(std::string url); +	void showFacebookFriends(const LLSD& friends); +	void addTestParticipant(); +	void addParticipantToModel(LLPersonTabModel * session_model, const LLUUID& agent_id, const std::string& name); +	void hideFacebookFriends(); +	void loadFacebookFriends(); +	void tryToReconnectToFacebook(); +	void connectToFacebook(const std::string& auth_code = ""); +	void disconnectFromFacebook(); +	 +	std::string getFacebookConnectURL(const std::string& route = ""); +	 +	bool mConnectedToFbc; +	bool mTryToConnectToFbc; +  	// internals  	class Updater; @@ -75,6 +97,7 @@ private:  	void					updateFriendList();  	void					updateNearbyList();  	void					updateRecentList(); +	void					updateFbcTestList();  	bool					isItemsFreeOfFriends(const uuid_vec_t& uuids); @@ -106,6 +129,11 @@ private:  	void					onGroupsViewSortMenuItemClicked(const LLSD& userdata);  	void					onRecentViewSortMenuItemClicked(const LLSD& userdata); +	void					onLoginFbcButtonClicked(); +	void					onFacebookAppRequestClicked(); +	void					onFacebookAppSendClicked(); +	void					onFacebookTestAddClicked(); +  	bool					onFriendsViewSortMenuItemCheck(const LLSD& userdata);  	bool					onRecentViewSortMenuItemCheck(const LLSD& userdata);  	bool					onNearbyViewSortMenuItemCheck(const LLSD& userdata); @@ -132,16 +160,24 @@ private:  	LLAvatarList*			mNearbyList;  	LLAvatarList*			mRecentList;  	LLGroupList*			mGroupList; +	LLSocialList*			mFacebookFriends;  	LLNetMap*				mMiniMap;  	std::vector<std::string> mSavedOriginalFilters;  	std::vector<std::string> mSavedFilters; +	LLHandle<LLView>		mFBCMenuHandle; +	LLHandle<LLFloater>		mFbcTestBrowserHandle;  	Updater*				mFriendListUpdater;  	Updater*				mNearbyListUpdater;  	Updater*				mRecentListUpdater; +	Updater*				mFbcTestListUpdater;  	Updater*				mButtonsUpdater; +	LLMenuButton*			mFBCGearButton;      LLHandle< LLFloater >	mPicker; + +	LLPersonFolderViewModel mPersonFolderViewModel; +	LLPersonFolderView* mPersonFolderView;  };  #endif //LL_LLPANELPEOPLE_H diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index c53760bca1..b5c9f4a310 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -27,6 +27,7 @@  #include "llviewerprecompiledheaders.h"  #include "llavatarnamecache.h" +#include "llerror.h"  #include "llimview.h"  #include "llfloaterimcontainer.h"  #include "llparticipantlist.h" @@ -401,6 +402,23 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)  	adjustParticipant(avatar_id);  } +static LLFastTimer::DeclareTimer FTM_FOLDERVIEW_TEST("add test avatar agents"); + + +void LLParticipantList::addTestAvatarAgents() +{ +	LLFastTimer _(FTM_FOLDERVIEW_TEST); + +	LL_INFOS("LLParticipantList") << "start adding 300 users" << LL_ENDL; + +	for(int i = 0; i < 300; ++i) +	{ +		addAvatarIDExceptAgent(LLUUID().generateNewID()); +	} + +	LL_INFOS("LLParticipantList") << "finished adding 300 users" << LL_ENDL; +} +  void LLParticipantList::adjustParticipant(const LLUUID& speaker_id)  {  	LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id); diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 3a3ae76604..936e289c08 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -50,6 +50,7 @@ public:  	 * @param[in] avatar_id - Avatar UUID to be added into the list  	 */  	void addAvatarIDExceptAgent(const LLUUID& avatar_id); +	void addTestAvatarAgents();  	/**  	 * Refreshes the participant list. diff --git a/indra/newview/llpersonfolderview.cpp b/indra/newview/llpersonfolderview.cpp new file mode 100644 index 0000000000..ba1e9d20eb --- /dev/null +++ b/indra/newview/llpersonfolderview.cpp @@ -0,0 +1,114 @@ +/**  +* @file llpersonfolderview.cpp +* @brief Implementation of llpersonfolderview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llpersonfolderview.h" + +#include "llpersontabview.h" +#include "llpersonmodelcommon.h" + + +LLPersonFolderView::LLPersonFolderView(const Params &p) :  +LLFolderView(p), +	mConversationsEventStream("ConversationsEventsTwo") +{ +	mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLPersonFolderView::onConversationModelEvent, this, _1)); +} + +LLPersonFolderView::~LLPersonFolderView() +{ +	mConversationsEventStream.stopListening("ConversationsRefresh"); +} + +BOOL LLPersonFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) +{ +	LLFolderViewItem * item = getCurSelectedItem(); + +	//Will disable highlight on tab +	if(item) +	{ +		LLPersonTabView * person_tab= dynamic_cast<LLPersonTabView *>(item); +		if(person_tab) +		{ +			person_tab->highlight = false; +		} +		else +		{ +			person_tab = dynamic_cast<LLPersonTabView *>(item->getParent()); +			person_tab->highlight = false; +		} +	} + +	mKeyboardSelection = FALSE;  +	mSearchString.clear(); + +	LLEditMenuHandler::gEditMenuHandler = this; + +	return LLView::handleMouseDown( x, y, mask ); +} + +bool LLPersonFolderView::onConversationModelEvent(const LLSD &event) +{ +	std::string type = event.get("type").asString(); +	LLUUID folder_id = event.get("folder_id").asUUID(); +	LLUUID person_id = event.get("person_id").asUUID(); + +	if(type == "add_participant") +	{ +		LLPersonTabModel * person_folder_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderModelMap[folder_id]); +		LLPersonTabView * person_folder_view = dynamic_cast<LLPersonTabView *>(mPersonFolderViewMap[folder_id]); + +		if(person_folder_model) +		{ +			LLPersonModel * person_model = person_folder_model->findParticipant(person_id); + +			if(person_model) +			{ +				LLPersonView * participant_view = createConversationViewParticipant(person_model); +				participant_view->addToFolder(person_folder_view); +			} +		} +	} + +	return false; +} + +LLPersonView * LLPersonFolderView::createConversationViewParticipant(LLPersonModel * item) +{ +	LLPersonView::Params params; + +	params.name = item->getDisplayName(); +	params.root = this; +	params.listener = item; + +	//24 should be loaded from .xml somehow +	params.rect = LLRect (0, 24, getRect().getWidth(), 0); +	params.tool_tip = params.name; + +	return LLUICtrlFactory::create<LLPersonView>(params); +} diff --git a/indra/newview/llpersonfolderview.h b/indra/newview/llpersonfolderview.h new file mode 100644 index 0000000000..74e9142a7c --- /dev/null +++ b/indra/newview/llpersonfolderview.h @@ -0,0 +1,64 @@ +/**  +* @file   llpersonfolderview.h +* @brief  Header file for llpersonfolderview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLPERSONFOLDERVIEW_H +#define LL_LLPERSONFOLDERVIEW_H + +class LLPersonTabModel; +class LLPersonTabView; +class LLPersonView; +class LLPersonModel; + +typedef std::map<LLUUID, LLPersonTabModel *> person_folder_model_map; +typedef std::map<LLUUID, LLPersonTabView *> person_folder_view_map; + +#include "llevents.h" +#include "llfolderview.h" + +class LLPersonFolderView : public LLFolderView +{ +public: +	struct Params : public LLInitParam::Block<Params, LLFolderView::Params> +	{ +		Params() +		{} +	}; + +	LLPersonFolderView(const Params &p); +	~LLPersonFolderView(); + +	bool onConversationModelEvent(const LLSD &event); +	LLPersonView * createConversationViewParticipant(LLPersonModel * item); + +	BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + +	person_folder_model_map mPersonFolderModelMap; +	person_folder_view_map mPersonFolderViewMap; +	LLEventStream mConversationsEventStream; +}; + +#endif // LL_LLPERSONFOLDERVIEW_H + diff --git a/indra/newview/llpersonmodelcommon.cpp b/indra/newview/llpersonmodelcommon.cpp new file mode 100644 index 0000000000..9660432b80 --- /dev/null +++ b/indra/newview/llpersonmodelcommon.cpp @@ -0,0 +1,281 @@ +/**  +* @file llavatarfolder.cpp +* @brief Implementation of llavatarfolder +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llpersonmodelcommon.h" + +#include "llevents.h" +#include "llsdutil.h" +#include "llstring.h" + +// +// LLPersonModelCommon +//  + +LLPersonModelCommon::LLPersonModelCommon(std::string display_name, LLFolderViewModelInterface& root_view_model) : +    LLFolderViewModelItemCommon(root_view_model), +	mID(LLUUID().generateNewID()) +{ +    renameItem(display_name); +} + +LLPersonModelCommon::LLPersonModelCommon(LLFolderViewModelInterface& root_view_model) : +    LLFolderViewModelItemCommon(root_view_model), +	mName(""), +    mSearchableName(""), +	mID(LLUUID().generateNewID()) +{ +} + +LLPersonModelCommon::~LLPersonModelCommon() +{ + +} + +BOOL LLPersonModelCommon::renameItem(const std::string& new_name) +{ +    mName = new_name; +    mSearchableName = new_name; +    LLStringUtil::toUpper(mSearchableName); +    return TRUE; +} + +void LLPersonModelCommon::postEvent(const std::string& event_type, LLPersonTabModel* folder, LLPersonModel* person) +{ +	LLUUID folder_id = folder->getID(); +	LLUUID person_id = person->getID(); +	LLSD event(LLSDMap("type", event_type)("folder_id", folder_id)("person_id", person_id)); +	LLEventPumps::instance().obtain("ConversationsEventsTwo").post(event); +} + +// Virtual action callbacks +void LLPersonModelCommon::performAction(LLInventoryModel* model, std::string action) +{ +} + +void LLPersonModelCommon::openItem( void ) +{ +} + +void LLPersonModelCommon::closeItem( void ) +{ +} + +void LLPersonModelCommon::previewItem( void ) +{ +} + +void LLPersonModelCommon::showProperties(void) +{ +} + +bool LLPersonModelCommon::filter( LLFolderViewFilter& filter) +{ +    // See LLFolderViewModelItemInventory::filter() +/* +    if (!filter.isModified()) +    { +        llinfos << "Merov : LLPersonModelCommon::filter, exit, no modif" << llendl; +        return true; +    } +*/         +    if (!mChildren.empty()) +    { +        //llinfos << "Merov : LLPersonModelCommon::filter, filtering folder = " << getDisplayName() << llendl; +        setPassedFilter(1, -1, filter.getStringMatchOffset(this), filter.getFilterStringSize()); +        for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); +            iter != end_iter; +            ++iter) +        { +            // LLFolderViewModelItem +            LLPersonModelCommon* item = dynamic_cast<LLPersonModelCommon*>(*iter); +            item->filter(filter); +        } +    } +    else +    { +        const bool passed_filter = filter.check(this); +        setPassedFilter(passed_filter, -1, filter.getStringMatchOffset(this), filter.getFilterStringSize()); +    } +     +    filter.clearModified(); +    return true; +} + +// +// LLPersonTabModel +//  + +LLPersonTabModel::LLPersonTabModel(std::string display_name, LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(display_name,root_view_model) +{ + +} + +LLPersonTabModel::LLPersonTabModel(LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(root_view_model) +{ + +} + +void LLPersonTabModel::addParticipant(LLPersonModel* participant) +{ +	addChild(participant); +	postEvent("add_participant", this, participant); +} + +void LLPersonTabModel::removeParticipant(LLPersonModel* participant) +{ +	removeChild(participant); +	postEvent("remove_participant", this, participant); +} + +void LLPersonTabModel::removeParticipant(const LLUUID& participant_id) +{ +	LLPersonModel* participant = findParticipant(participant_id); +	if (participant) +	{ +		removeParticipant(participant); +	} +} + +void LLPersonTabModel::clearParticipants() +{ +	clearChildren(); +} + +LLPersonModel* LLPersonTabModel::findParticipant(const LLUUID& person_id) +{ +	LLPersonModel * person_model = NULL; +	child_list_t::iterator iter; + +	for(iter = mChildren.begin(); iter != mChildren.end(); ++iter) +	{ +		person_model = static_cast<LLPersonModel *>(*iter); + +		if(person_model->getID() == person_id) +		{ +			break; +		} +	} + +	return iter == mChildren.end() ? NULL : person_model; +} + +// +// LLPersonModel +//  + +LLPersonModel::LLPersonModel(const LLUUID& agent_id, const std::string display_name, LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(display_name,root_view_model), +mAgentID(agent_id) +{ +} + +LLPersonModel::LLPersonModel(LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(root_view_model), +mAgentID(LLUUID(NULL)) +{ +} + +LLUUID LLPersonModel::getAgentID() +{ +	return mAgentID; +} + +// +// LLPersonViewFilter +// + +LLPersonViewFilter::LLPersonViewFilter() : +    mEmptyLookupMessage(""), +    mFilterSubString(""), +    mName(""), +    mFilterModified(FILTER_NONE) +{ +} + +void LLPersonViewFilter::setFilterSubString(const std::string& string) +{ +	std::string filter_sub_string_new = string; +	LLStringUtil::trimHead(filter_sub_string_new); +	LLStringUtil::toUpper(filter_sub_string_new); +     +	if (mFilterSubString != filter_sub_string_new) +	{ +        // *TODO : Add logic to support more and less restrictive filtering +        setModified(FILTER_RESTART); +		mFilterSubString = filter_sub_string_new; +	} +} + +bool LLPersonViewFilter::showAllResults() const +{ +	return mFilterSubString.size() > 0; +} + +bool LLPersonViewFilter::check(const LLFolderViewModelItem* item) +{ +	return (mFilterSubString.size() ? (item->getSearchableName().find(mFilterSubString) != std::string::npos) : true); +} + +std::string::size_type LLPersonViewFilter::getStringMatchOffset(LLFolderViewModelItem* item) const +{ +	return mFilterSubString.size() ? item->getSearchableName().find(mFilterSubString) : std::string::npos; +} + +std::string::size_type LLPersonViewFilter::getFilterStringSize() const +{ +	return mFilterSubString.size(); +} + +bool LLPersonViewFilter::isActive() const +{ +	return mFilterSubString.size(); +} + +bool LLPersonViewFilter::isModified() const +{ +	return mFilterModified != FILTER_NONE; +} + +void LLPersonViewFilter::clearModified() +{ +    mFilterModified = FILTER_NONE; +} + +void LLPersonViewFilter::setEmptyLookupMessage(const std::string& message) +{ +	mEmptyLookupMessage = message; +} + +std::string LLPersonViewFilter::getEmptyLookupMessage() const +{ +	return mEmptyLookupMessage; +} + diff --git a/indra/newview/llpersonmodelcommon.h b/indra/newview/llpersonmodelcommon.h new file mode 100644 index 0000000000..ffd145b549 --- /dev/null +++ b/indra/newview/llpersonmodelcommon.h @@ -0,0 +1,244 @@ +/**  +* @file   llavatarfolder.h +* @brief  Header file for llavatarfolder +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLPERSONMODELCOMMON_H +#define LL_LLPERSONMODELCOMMON_H + +#include "../llui/llfolderviewitem.h" +#include "../llui/llfolderviewmodel.h" + +class LLPersonTabModel; +class LLPersonModel; + +// Conversation items: we hold a list of those and create an LLFolderViewItem widget for each   +// that we tuck into the mConversationsListPanel.  +class LLPersonModelCommon : public LLFolderViewModelItemCommon +{ +public: + +	LLPersonModelCommon(std::string name, LLFolderViewModelInterface& root_view_model); +	LLPersonModelCommon(LLFolderViewModelInterface& root_view_model); +	virtual ~LLPersonModelCommon(); + +	// Stub those things we won't really be using in this conversation context +	virtual const std::string& getName() const { return mName; } +	virtual const std::string& getDisplayName() const { return mName; } +	virtual const std::string& getSearchableName() const { return mSearchableName; } + +	virtual LLPointer<LLUIImage> getIcon() const { return NULL; } +	virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } +	virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } +	virtual std::string getLabelSuffix() const { return LLStringUtil::null; } +	virtual BOOL isItemRenameable() const { return TRUE; } +	virtual BOOL renameItem(const std::string& new_name); +	virtual BOOL isItemMovable( void ) const { return FALSE; } +	virtual BOOL isItemRemovable( void ) const { return FALSE; } +	virtual BOOL isItemInTrash( void) const { return FALSE; } +	virtual BOOL removeItem() { return FALSE; } +	virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) { } +	virtual void move( LLFolderViewModelItem* parent_listener ) { } +	virtual BOOL isItemCopyable() const { return FALSE; } +	virtual BOOL copyToClipboard() const { return FALSE; } +	virtual BOOL cutToClipboard() const { return FALSE; } +	virtual BOOL isClipboardPasteable() const { return FALSE; } +	virtual void pasteFromClipboard() { } +	virtual void pasteLinkFromClipboard() { } +	virtual void buildContextMenu(LLMenuGL& menu, U32 flags) { } +	virtual BOOL isUpToDate() const { return TRUE; } +	virtual bool hasChildren() const { return FALSE; } + +	virtual bool potentiallyVisible() { return true; } +     +	virtual bool filter( LLFolderViewFilter& filter); + +	virtual bool descendantsPassedFilter(S32 filter_generation = -1) { return true; } +//	virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) { } +	virtual bool passedFilter(S32 filter_generation = -1) { return mPassedFilter; } + +	// The action callbacks +	virtual void performAction(LLInventoryModel* model, std::string action); +	virtual void openItem( void ); +	virtual void closeItem( void ); +	virtual void previewItem( void ); +	virtual void selectItem(void) { }  +	virtual void showProperties(void); + +	// This method will be called to determine if a drop can be +	// performed, and will set drop to TRUE if a drop is +	// requested.  +	// Returns TRUE if a drop is possible/happened, FALSE otherwise. +	virtual BOOL dragOrDrop(MASK mask, BOOL drop, +		EDragAndDropType cargo_type, +		void* cargo_data, +		std::string& tooltip_msg) { return FALSE; } + +	const LLUUID& getID() {return mID;} +	void postEvent(const std::string& event_type, LLPersonTabModel* session, LLPersonModel* participant); + +protected: + +	std::string mName;              // Name of the person +	std::string mSearchableName;	// Name used in string matching for this person +	LLUUID mID; +};	 + +class LLPersonTabModel : public LLPersonModelCommon +{ +public: +	LLPersonTabModel(std::string display_name, LLFolderViewModelInterface& root_view_model); +	LLPersonTabModel(LLFolderViewModelInterface& root_view_model); + +	LLPointer<LLUIImage> getIcon() const { return NULL; } +	void addParticipant(LLPersonModel* participant); +	void removeParticipant(LLPersonModel* participant); +	void removeParticipant(const LLUUID& participant_id); +	void clearParticipants(); +	LLPersonModel* findParticipant(const LLUUID& person_id); + +private: +}; + +class LLPersonModel : public LLPersonModelCommon +{ +public: +	LLPersonModel(const LLUUID& agent_id, const std::string display_name, LLFolderViewModelInterface& root_view_model); +	LLPersonModel(LLFolderViewModelInterface& root_view_model); + +	LLUUID getAgentID(); + +private: +	LLUUID mAgentID; +}; + +// Filtering functional object + +class LLPersonViewFilter : public LLFolderViewFilter +{ +public: + +	enum ESortOrderType +	{ +		SO_NAME = 0,						// Sort by name +		SO_ONLINE_STATUS = 0x1				// Sort by online status (i.e. online or not) +	}; +	// Default sort order is by name +	static const U32 SO_DEFAULT = SO_NAME; + +	LLPersonViewFilter(); +	~LLPersonViewFilter() {} + +	// +-------------------------------------------------------------------+ +	// + Execution And Results +	// +-------------------------------------------------------------------+ +	bool 				check(const LLFolderViewModelItem* item); +	bool				checkFolder(const LLFolderViewModelItem* folder) const { return true; } +     +	void 				setEmptyLookupMessage(const std::string& message); +	std::string			getEmptyLookupMessage() const; +     +	bool				showAllResults() const; +     +	std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const; +	std::string::size_type getFilterStringSize() const; + + 	// +-------------------------------------------------------------------+ +	// + Status +	// +-------------------------------------------------------------------+ + 	bool 				isActive() const; +	bool 				isModified() const; +	void 				clearModified(); +	const std::string& 	getName() const { return mName; } +	const std::string& 	getFilterText() { return mName; } +	void 				setModified(EFilterModified behavior = FILTER_RESTART) { mFilterModified = behavior; } +     +	// +-------------------------------------------------------------------+ +	// + Count +	// +-------------------------------------------------------------------+ +    // Note : we currently filter the whole person list at once, no need to count then. +	//void 				setFilterCount(S32 count) { } +	//S32 				getFilterCount() const { return 1; } +	//void 				decrementFilterCount() { } + +	// +-------------------------------------------------------------------+ +	// + Time +	// +-------------------------------------------------------------------+ +    // Note : we currently filter the whole person list at once, no need to timeout then. +	void 				resetTime(S32 timeout) { } +    bool                isTimedOut() { return false; } +     +	// +-------------------------------------------------------------------+ +	// + Default +	// +-------------------------------------------------------------------+ +    // Note : we don't support runtime default setting for person filter +	bool 				isDefault() const  { return !isActive(); } +	bool 				isNotDefault() const { return isActive(); } +	void 				markDefault() { } +	void 				resetDefault() { setModified(); } +     +	// +-------------------------------------------------------------------+ +	// + Generation +	// +-------------------------------------------------------------------+ +    // Note : unclear if we have to take tab on generation at that point +	S32 				getCurrentGeneration() const { return 0; } +	S32 				getFirstSuccessGeneration() const { return 0; } +	S32 				getFirstRequiredGeneration() const { return 0; } + +    // Non Virtual Methods (i.e. specific to this class) +	void 				setFilterSubString(const std::string& string); +    +private: +	std::string         mName; +	std::string         mEmptyLookupMessage; +	std::string			mFilterSubString; +	EFilterModified 	mFilterModified; +}; + +class LLPersonViewSort +{ +public: +	LLPersonViewSort(U32 order = LLPersonViewFilter::SO_DEFAULT) : mSortOrder(order) { } + +	bool operator()(const LLPersonModelCommon* const& a, const LLPersonModelCommon* const& b) const {return false;} +	operator U32() const { return mSortOrder; } +private: +	// Note: we're treating this value as a sort order bitmask as done in other places in the code (e.g. inventory) +	U32  mSortOrder; +}; + + +class LLPersonFolderViewModel +	: public  LLFolderViewModel<LLPersonViewSort, LLPersonModelCommon, LLPersonModelCommon, LLPersonViewFilter> +{ +public: +	typedef LLFolderViewModel<LLPersonViewSort, LLPersonModelCommon, LLPersonModelCommon, LLPersonViewFilter> base_t; + +	void sort(LLFolderViewFolder* folder) { base_t::sort(folder);} +	bool startDrag(std::vector<LLFolderViewModelItem*>& items) { return false; } // We do not allow drag of conversation items +}; + + +#endif // LL_LLPERSONMODELCOMMON_H + diff --git a/indra/newview/llpersontabview.cpp b/indra/newview/llpersontabview.cpp new file mode 100644 index 0000000000..fdc024beb8 --- /dev/null +++ b/indra/newview/llpersontabview.cpp @@ -0,0 +1,398 @@ +/**  +* @file llpersontabview.cpp +* @brief Implementation of llpersontabview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llpersontabview.h" + +#include "llavataractions.h" +#include "llfloaterreg.h" +#include "llpersonmodelcommon.h" + +static LLDefaultChildRegistry::Register<LLPersonTabView> r_person_tab_view("person_tab_view"); + +const LLColor4U DEFAULT_WHITE(255, 255, 255); + +LLPersonTabView::Params::Params() +{} + +LLPersonTabView::LLPersonTabView(const LLPersonTabView::Params& p) : +LLFolderViewFolder(p), +highlight(false), +mImageHeader(LLUI::getUIImage("Accordion_Off")), +mImageHeaderOver(LLUI::getUIImage("Accordion_Over")), +mImageHeaderFocused(LLUI::getUIImage("Accordion_Selected")) +{ +} + +S32 LLPersonTabView::getLabelXPos() +{  +	return getIndentation() + mArrowSize + 15;//Should be a .xml variable but causes crash; +} + +LLPersonTabView::~LLPersonTabView() +{ + +} + +BOOL LLPersonTabView::handleMouseDown( S32 x, S32 y, MASK mask ) +{ +	bool selected_item = LLFolderViewFolder::handleMouseDown(x, y, mask); + +	if(selected_item) +	{ +		gFocusMgr.setKeyboardFocus( this ); +		highlight = true; +	} + +	return selected_item; +} + +void LLPersonTabView::draw() +{ +	static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); +	static const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLPersonTabView>(); + +	const LLFontGL * font = LLFontGL::getFontSansSerif(); +	F32 text_left = (F32)getLabelXPos(); +	F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; +	LLColor4 color = sFgColor; +	F32 right_x  = 0; + +	drawHighlight(); +	updateLabelRotation(); +	drawOpenFolderArrow(default_params, sFgColor); + +	drawLabel(font, text_left, y, color, right_x); + +	LLView::draw(); +} + +void LLPersonTabView::drawHighlight() +{ +	S32 width = getRect().getWidth(); +	S32 height = mItemHeight; +	S32 x = 1; +	S32 y = getRect().getHeight() - mItemHeight; + +	if(highlight) +	{ +		mImageHeaderFocused->draw(x,y,width,height); +	} +	else +	{ +		mImageHeader->draw(x,y,width,height); +	} + +	if(mIsMouseOverTitle) +	{ +		mImageHeaderOver->draw(x,y,width,height); +	} + +} + +// +// LLPersonView +//  + +static LLDefaultChildRegistry::Register<LLPersonView> r_person_view("person_view"); + +bool LLPersonView::sChildrenWidthsInitialized = false; +ChildWidthVec LLPersonView::mChildWidthVec; + +LLPersonView::Params::Params() : +avatar_icon("avatar_icon"), +last_interaction_time_textbox("last_interaction_time_textbox"), +permission_edit_theirs_icon("permission_edit_theirs_icon"), +permission_edit_mine_icon("permission_edit_mine_icon"), +permission_map_icon("permission_map_icon"), +permission_online_icon("permission_online_icon"), +info_btn("info_btn"), +profile_btn("profile_btn"), +output_monitor("output_monitor") +{} + +LLPersonView::LLPersonView(const LLPersonView::Params& p) : +LLFolderViewItem(p), +mImageOver(LLUI::getUIImage("ListItem_Over")), +mImageSelected(LLUI::getUIImage("ListItem_Select")), +mAvatarIcon(NULL), +mLastInteractionTimeTextbox(NULL), +mPermissionEditTheirsIcon(NULL), +mPermissionEditMineIcon(NULL), +mPermissionMapIcon(NULL), +mPermissionOnlineIcon(NULL), +mInfoBtn(NULL), +mProfileBtn(NULL), +mOutputMonitorCtrl(NULL) +{ +} + +S32 LLPersonView::getLabelXPos() +{ +	return getIndentation() + mAvatarIcon->getRect().getWidth() + mIconPad; +} + +void LLPersonView::addToFolder(LLFolderViewFolder * person_folder_view) +{ +	LLFolderViewItem::addToFolder(person_folder_view); +	//Added item to folder, could change folder's mHasVisibleChildren flag so call arrange +	person_folder_view->requestArrange(); +} + +LLPersonView::~LLPersonView() +{ + +} + +BOOL LLPersonView::postBuild() +{ +	if(!sChildrenWidthsInitialized) +	{ +		initChildrenWidthVec(this); +		sChildrenWidthsInitialized = true; +	} +	 +	initChildVec(); +	updateChildren(); + +	LLPersonModel * person_model = static_cast<LLPersonModel *>(getViewModelItem()); + +	mAvatarIcon->setValue(person_model->getAgentID()); +	mInfoBtn->setClickedCallback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", person_model->getAgentID()), FALSE)); +	mProfileBtn->setClickedCallback(boost::bind(&LLAvatarActions::showProfile, person_model->getAgentID())); + +	return LLFolderViewItem::postBuild(); +} + +void LLPersonView::onMouseEnter(S32 x, S32 y, MASK mask) +{ +	mInfoBtn->setVisible(TRUE); +	mProfileBtn->setVisible(TRUE); +	updateChildren(); +	LLFolderViewItem::onMouseEnter(x, y, mask); +} + +void LLPersonView::onMouseLeave(S32 x, S32 y, MASK mask) +{ +	mInfoBtn->setVisible(FALSE); +	mProfileBtn->setVisible(FALSE); +	updateChildren(); +	LLFolderViewItem::onMouseLeave(x, y, mask); +} + +BOOL LLPersonView::handleMouseDown( S32 x, S32 y, MASK mask) +{ +	if(!LLView::childrenHandleMouseDown(x, y, mask)) +	{ +		gFocusMgr.setMouseCapture( this ); +	} + +	if (!mIsSelected) +	{ +		if(mask & MASK_CONTROL) +		{ +			getRoot()->changeSelection(this, !mIsSelected); +		} +		else if (mask & MASK_SHIFT) +		{ +			getParentFolder()->extendSelectionTo(this); +		} +		else +		{ +			getRoot()->setSelection(this, FALSE); +		} +		make_ui_sound("UISndClick"); +	} +	else +	{ +		// If selected, we reserve the decision of deselecting/reselecting to the mouse up moment. +		// This is necessary so we maintain selection consistent when starting a drag. +		mSelectPending = TRUE; +	} + +	mDragStartX = x; +	mDragStartY = y; +	return TRUE; +} + +void LLPersonView::draw() +{ +	static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); +	static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); + +	const LLFontGL * font = LLFontGL::getFontSansSerifSmall(); +	F32 text_left = (F32)getLabelXPos(); +	F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; +	LLColor4 color = mIsSelected ? sHighlightFgColor : sFgColor; +	F32 right_x  = 0; + +	drawHighlight(); +	drawLabel(font, text_left, y, color, right_x); + +	LLView::draw(); +} + +void LLPersonView::drawHighlight() +{ +	static LLUIColor outline_color = LLUIColorTable::instance().getColor("EmphasisColor", DEFAULT_WHITE); + +	S32 width = getRect().getWidth(); +	S32 height = mItemHeight; +	S32 x = 1; +	S32 y = 0; + +	if(mIsSelected) +	{ +		mImageSelected->draw(x, y, width, height); +		//Draw outline +		gl_rect_2d(x,  +			height,  +			width, +			y, +			outline_color, FALSE); +	} + +	if(mIsMouseOverTitle) +	{ +		mImageOver->draw(x, y, width, height); +	} +} + +void LLPersonView::initFromParams(const LLPersonView::Params & params) +{ +	LLAvatarIconCtrl::Params avatar_icon_params(params.avatar_icon()); +	applyXUILayout(avatar_icon_params, this); +	mAvatarIcon = LLUICtrlFactory::create<LLAvatarIconCtrl>(avatar_icon_params); +	addChild(mAvatarIcon); +	 +	LLTextBox::Params last_interaction_time_textbox(params.last_interaction_time_textbox()); +	applyXUILayout(last_interaction_time_textbox, this); +	mLastInteractionTimeTextbox = LLUICtrlFactory::create<LLTextBox>(last_interaction_time_textbox); +	addChild(mLastInteractionTimeTextbox); + +	LLIconCtrl::Params permission_edit_theirs_icon(params.permission_edit_theirs_icon()); +	applyXUILayout(permission_edit_theirs_icon, this); +	mPermissionEditTheirsIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_edit_theirs_icon); +	addChild(mPermissionEditTheirsIcon); + +	LLIconCtrl::Params permission_edit_mine_icon(params.permission_edit_mine_icon()); +	applyXUILayout(permission_edit_mine_icon, this); +	mPermissionEditMineIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_edit_mine_icon); +	addChild(mPermissionEditMineIcon); + +	LLIconCtrl::Params permission_map_icon(params.permission_map_icon()); +	applyXUILayout(permission_map_icon, this); +	mPermissionMapIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_map_icon); +	addChild(mPermissionMapIcon); + +	LLIconCtrl::Params permission_online_icon(params.permission_online_icon()); +	applyXUILayout(permission_online_icon, this); +	mPermissionOnlineIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_online_icon); +	addChild(mPermissionOnlineIcon); + +	LLButton::Params info_btn(params.info_btn()); +	applyXUILayout(info_btn, this); +	mInfoBtn = LLUICtrlFactory::create<LLButton>(info_btn); +	addChild(mInfoBtn); + +	LLButton::Params profile_btn(params.profile_btn()); +	applyXUILayout(profile_btn, this); +	mProfileBtn = LLUICtrlFactory::create<LLButton>(profile_btn); +	addChild(mProfileBtn); +	 +	LLOutputMonitorCtrl::Params output_monitor(params.output_monitor()); +	applyXUILayout(output_monitor, this); +	mOutputMonitorCtrl = LLUICtrlFactory::create<LLOutputMonitorCtrl>(output_monitor); +	addChild(mOutputMonitorCtrl); +} + +void LLPersonView::initChildrenWidthVec(LLPersonView* self) +{ +	S32 output_monitor_width = self->getRect().getWidth() - self->mOutputMonitorCtrl->getRect().mLeft; +	S32 profile_btn_width = self->mOutputMonitorCtrl->getRect().mLeft - self->mProfileBtn->getRect().mLeft; +	S32 info_btn_width = self->mProfileBtn->getRect().mLeft - self->mInfoBtn->getRect().mLeft; +	S32 permission_online_icon_width = self->mInfoBtn->getRect().mLeft - self->mPermissionOnlineIcon->getRect().mLeft; +	S32 permissions_map_icon_width = self->mPermissionOnlineIcon->getRect().mLeft - self->mPermissionMapIcon->getRect().mLeft; +	S32 permission_edit_mine_icon_width = self->mPermissionMapIcon->getRect().mLeft - self->mPermissionEditMineIcon->getRect().mLeft; +	S32 permission_edit_theirs_icon_width = self->mPermissionEditMineIcon->getRect().mLeft - self->mPermissionEditTheirsIcon->getRect().mLeft; +	S32 last_interaction_time_textbox_width = self->mPermissionEditTheirsIcon->getRect().mLeft - self->mLastInteractionTimeTextbox->getRect().mLeft; + +	self->mChildWidthVec.push_back(output_monitor_width); +	self->mChildWidthVec.push_back(profile_btn_width); +	self->mChildWidthVec.push_back(info_btn_width); +	self->mChildWidthVec.push_back(permission_online_icon_width); +	self->mChildWidthVec.push_back(permissions_map_icon_width); +	self->mChildWidthVec.push_back(permission_edit_mine_icon_width); +	self->mChildWidthVec.push_back(permission_edit_theirs_icon_width); +	self->mChildWidthVec.push_back(last_interaction_time_textbox_width); +} + +void LLPersonView::initChildVec() +{ +	mChildVec.push_back(mOutputMonitorCtrl); +	mChildVec.push_back(mProfileBtn); +	mChildVec.push_back(mInfoBtn); +	mChildVec.push_back(mPermissionOnlineIcon); +	mChildVec.push_back(mPermissionMapIcon); +	mChildVec.push_back(mPermissionEditMineIcon); +	mChildVec.push_back(mPermissionEditTheirsIcon); +	mChildVec.push_back(mLastInteractionTimeTextbox); +} + +void LLPersonView::updateChildren() +{ +	mLabelPaddingRight = 0; +	LLView * control; +	S32 control_width; +	LLRect control_rect; + +	llassert(mChildWidthVec.size() == mChildVec.size()); + +	for(S32 i = 0; i < mChildWidthVec.size(); ++i) +	{ +		control = mChildVec[i]; + +		if(!control->getVisible()) +		{ +			continue; +		} + +		control_width = mChildWidthVec[i]; +		mLabelPaddingRight += control_width; + +		control_rect = control->getRect(); +		control_rect.setLeftTopAndSize( +			getLocalRect().getWidth() - mLabelPaddingRight, +			control_rect.mTop, +			control_rect.getWidth(), +			control_rect.getHeight()); + +		control->setShape(control_rect); + +	} +} diff --git a/indra/newview/llpersontabview.h b/indra/newview/llpersontabview.h new file mode 100644 index 0000000000..9cce615167 --- /dev/null +++ b/indra/newview/llpersontabview.h @@ -0,0 +1,146 @@ +/**  +* @file   llpersontabview.h +* @brief  Header file for llpersontabview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLPERSONTABVIEW_H +#define LL_LLPERSONTABVIEW_H + +#include "llavatariconctrl.h" +#include "llbutton.h" +#include "llfolderviewitem.h" +#include "lloutputmonitorctrl.h" +#include "lltextbox.h" + +class LLPersonTabView : public LLFolderViewFolder +{ + +public: + +	struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params> +	{ +		Params(); +	}; + +	LLPersonTabView(const LLPersonTabView::Params& p); +	virtual ~LLPersonTabView(); + +	S32 getLabelXPos(); +	bool highlight; + +	BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + +protected:	 +	 void draw(); +	 void drawHighlight(); + +private: + +	// Background images +	LLPointer<LLUIImage> mImageHeader; +	LLPointer<LLUIImage> mImageHeaderOver; +	LLPointer<LLUIImage> mImageHeaderFocused; + +}; + +typedef std::vector<S32> ChildWidthVec; +typedef std::vector<LLView *> ChildVec; + +class LLPersonView : public LLFolderViewItem +{ + +public: + +	struct Params : public LLInitParam::Block<Params, LLFolderViewItem::Params> +	{ +		Params(); +		Optional<LLAvatarIconCtrl::Params> avatar_icon; +		Optional<LLTextBox::Params> last_interaction_time_textbox; +		Optional<LLIconCtrl::Params> permission_edit_theirs_icon; +		Optional<LLIconCtrl::Params> permission_edit_mine_icon; +		Optional<LLIconCtrl::Params> permission_map_icon; +		Optional<LLIconCtrl::Params> permission_online_icon; +		Optional<LLButton::Params> info_btn; +		Optional<LLButton::Params> profile_btn; +		Optional<LLOutputMonitorCtrl::Params> output_monitor; +	}; + +	LLPersonView(const LLPersonView::Params& p); +	virtual ~LLPersonView(); + +	S32 getLabelXPos(); +	void addToFolder(LLFolderViewFolder * person_folder_view); +	void initFromParams(const LLPersonView::Params & params); +	BOOL postBuild(); +	void onMouseEnter(S32 x, S32 y, MASK mask); +	void onMouseLeave(S32 x, S32 y, MASK mask); +	BOOL handleMouseDown( S32 x, S32 y, MASK mask); + +protected:	 +	 +	void draw(); +	void drawHighlight(); + +private: + +	LLPointer<LLUIImage> mImageOver; +	LLPointer<LLUIImage> mImageSelected; + +	LLAvatarIconCtrl* mAvatarIcon; +	LLTextBox * mLastInteractionTimeTextbox; +	LLIconCtrl * mPermissionEditTheirsIcon; +	LLIconCtrl * mPermissionEditMineIcon; +	LLIconCtrl * mPermissionMapIcon; +	LLIconCtrl * mPermissionOnlineIcon; +	LLButton * mInfoBtn; +	LLButton * mProfileBtn; +	LLOutputMonitorCtrl * mOutputMonitorCtrl; + + + +	typedef enum e_avatar_item_child { +		ALIC_SPEAKER_INDICATOR, +		ALIC_PROFILE_BUTTON, +		ALIC_INFO_BUTTON, +		ALIC_PERMISSION_ONLINE, +		ALIC_PERMISSION_MAP, +		ALIC_PERMISSION_EDIT_MINE, +		ALIC_PERMISSION_EDIT_THEIRS, +		ALIC_INTERACTION_TIME, +		ALIC_COUNT, +	} EAvatarListItemChildIndex; + +	//Widths of controls are same for every instance so can be static +	static ChildWidthVec mChildWidthVec; +	//Control pointers are different for each instance so non-static +	ChildVec mChildVec; + +	static bool	sChildrenWidthsInitialized; +	static void initChildrenWidthVec(LLPersonView* self); +	void initChildVec(); +	void updateChildren(); +}; + +#endif // LL_LLPERSONTABVIEW_H + diff --git a/indra/newview/llsociallist.cpp b/indra/newview/llsociallist.cpp new file mode 100644 index 0000000000..9f827cf04f --- /dev/null +++ b/indra/newview/llsociallist.cpp @@ -0,0 +1,154 @@ +/**  +* @file llsociallist.cpp +* @brief Implementation of llsociallist +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + + +#include "llviewerprecompiledheaders.h" + +#include "llsociallist.h" + +#include "llavataractions.h" +#include "llfloaterreg.h" +#include "llavatariconctrl.h" +#include "llavatarnamecache.h" +#include "lloutputmonitorctrl.h" +#include "lltextutil.h" + +static LLDefaultChildRegistry::Register<LLSocialList> r("social_list"); + +LLSocialList::LLSocialList(const Params&p) : LLFlatListViewEx(p) +{ + +} + +LLSocialList::~LLSocialList() +{ + +} + +void LLSocialList::draw() +{ +	LLFlatListView::draw(); +} + +void LLSocialList::refresh() +{ + +} + +void LLSocialList::addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos) +{ +	LLSocialListItem * item = new LLSocialListItem(); +	LLAvatarName avatar_name; +	bool has_avatar_name = id.notNull() && LLAvatarNameCache::get(id, &avatar_name); + +	item->mAvatarId = id; +	if(id.notNull()) +	{ +		item->mIcon->setValue(id); +	} + +	item->setName(has_avatar_name ? name + " (" + avatar_name.getDisplayName() + ")" : name, mNameFilter); +	addItem(item, id, pos); +} + +LLSocialListItem::LLSocialListItem() +{ +	buildFromFile("panel_avatar_list_item.xml"); +} + +LLSocialListItem::~LLSocialListItem() +{ + +} + +BOOL LLSocialListItem::postBuild() +{ +	mIcon = getChild<LLAvatarIconCtrl>("avatar_icon"); +	mLabelTextBox = getChild<LLTextBox>("avatar_name"); + +	mLastInteractionTime = getChild<LLTextBox>("last_interaction"); +	mIconPermissionOnline = getChild<LLIconCtrl>("permission_online_icon"); +	mIconPermissionMap = getChild<LLIconCtrl>("permission_map_icon"); +	mIconPermissionEditMine = getChild<LLIconCtrl>("permission_edit_mine_icon"); +	mIconPermissionEditTheirs = getChild<LLIconCtrl>("permission_edit_theirs_icon"); +	mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator"); +	mInfoBtn = getChild<LLButton>("info_btn"); +	mProfileBtn = getChild<LLButton>("profile_btn"); + +	mLastInteractionTime->setVisible(false); +	mIconPermissionOnline->setVisible(false); +	mIconPermissionMap->setVisible(false); +	mIconPermissionEditMine->setVisible(false); +	mIconPermissionEditTheirs->setVisible(false); +	mSpeakingIndicator->setVisible(false); +	mInfoBtn->setVisible(false); +	mProfileBtn->setVisible(false); + +	mInfoBtn->setClickedCallback(boost::bind(&LLSocialListItem::onInfoBtnClick, this)); +	mProfileBtn->setClickedCallback(boost::bind(&LLSocialListItem::onProfileBtnClick, this)); + +	return TRUE; +} + +void LLSocialListItem::setName(const std::string& name, const std::string& highlight) +{ +	mLabel = name; +	LLTextUtil::textboxSetHighlightedVal(mLabelTextBox, mLabelTextBoxStyle, name, highlight); +} + +void LLSocialListItem::setValue(const LLSD& value) +{ +	getChildView("selected_icon")->setVisible( value["selected"]); +} + +void LLSocialListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ +	getChildView("hovered_icon")->setVisible( true); +	mInfoBtn->setVisible(true); +	mProfileBtn->setVisible(true); + +	LLPanel::onMouseEnter(x, y, mask); +} + +void LLSocialListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ +	getChildView("hovered_icon")->setVisible( false); +	mInfoBtn->setVisible(false); +	mProfileBtn->setVisible(false); + +	LLPanel::onMouseLeave(x, y, mask); +} + +void LLSocialListItem::onInfoBtnClick() +{ +	LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarId)); +} + +void LLSocialListItem::onProfileBtnClick() +{ +	LLAvatarActions::showProfile(mAvatarId); +} diff --git a/indra/newview/llsociallist.h b/indra/newview/llsociallist.h new file mode 100644 index 0000000000..bc667fc400 --- /dev/null +++ b/indra/newview/llsociallist.h @@ -0,0 +1,102 @@ +/**  +* @file   llsociallist.h +* @brief  Header file for llsociallist +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLSOCIALLIST_H +#define LL_LLSOCIALLIST_H + +#include "llflatlistview.h" +#include "llstyle.h" + + +/** + * Generic list of avatars. + *  + * Updates itself when it's dirty, using optional name filter. + * To initiate update, modify the UUID list and call setDirty(). + *  + * @see getIDs() + * @see setDirty() + * @see setNameFilter() + */ + +class LLAvatarIconCtrl; +class LLIconCtrl; +class LLOutputMonitorCtrl; + +class LLSocialList : public LLFlatListViewEx +{ +public: + +	struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> +	{ +	}; + +	LLSocialList(const Params&p); +	virtual	~LLSocialList(); + +	virtual void draw(); +	void refresh(); +	void addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos = ADD_BOTTOM); + + + +	std::string mNameFilter; +}; + +class LLSocialListItem : public LLPanel +{ +	public: +	LLSocialListItem(); +	~LLSocialListItem(); + +	BOOL postBuild(); +	void setName(const std::string& name, const std::string& highlight = LLStringUtil::null); +	void setValue(const LLSD& value); +	void onMouseEnter(S32 x, S32 y, MASK mask); +	void onMouseLeave(S32 x, S32 y, MASK mask); +	void onInfoBtnClick(); +	void onProfileBtnClick(); + +	LLUUID mAvatarId; + +	LLTextBox * mLabelTextBox; +	std::string mLabel; +	LLStyle::Params mLabelTextBoxStyle; + + +	LLAvatarIconCtrl * mIcon; +	LLTextBox * mLastInteractionTime; +	LLIconCtrl * mIconPermissionOnline; +	LLIconCtrl * mIconPermissionMap; +	LLIconCtrl * mIconPermissionEditMine; +	LLIconCtrl * mIconPermissionEditTheirs; +	LLOutputMonitorCtrl * mSpeakingIndicator; +	LLButton * mInfoBtn; +	LLButton * mProfileBtn; +};	 + + +#endif // LL_LLSOCIALLIST_H diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b8b53aa6e4..fba835f642 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1585,6 +1585,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)  	capabilityNames.append("EnvironmentSettings");  	capabilityNames.append("EstateChangeInfo");  	capabilityNames.append("EventQueueGet"); +	capabilityNames.append("FacebookConnect"); +	//capabilityNames.append("FacebookRedirect");  	if (gSavedSettings.getBOOL("UseHTTPInventory"))  	{	 diff --git a/indra/newview/skins/default/xui/en/menu_gear_fbc.xml b/indra/newview/skins/default/xui/en/menu_gear_fbc.xml new file mode 100644 index 0000000000..d73cee344b --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_gear_fbc.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu  +     name="menu_group_plus" +     left="0" bottom="0" visible="false" +     mouse_opaque="false"> +  <menu_item_check +   label="Facebook App Settings" +   layout="topleft" +   name="Facebook App Settings"> +      <menu_item_check.on_click +       function="Advanced.WebContentTest" +       parameter="http://www.facebook.com/settings?tab=applications" /> +  </menu_item_check> +  <menu_item_check +   label="Facebook App Request" +   layout="topleft" +   name="Facebook App Request"> +    <menu_item_check.on_click +     function="People.requestFBC" +     parameter="http://www.facebook.com/settings?tab=applications" /> +  </menu_item_check> +  <menu_item_check +   label="Facebook App Send" +   layout="topleft" +   name="Facebook App Send"> +    <menu_item_check.on_click +     function="People.sendFBC" +     parameter="http://www.facebook.com/settings?tab=applications" /> +  </menu_item_check> +  <menu_item_check +   label="Facebook Add 300 test users to AvatarList" +   layout="topleft" +   name="Facebook App Add"> +    <menu_item_check.on_click +     function="People.testaddFBC"/> +  </menu_item_check> +  <menu_item_check +   label="Facebook Add 300 test users to FolderView" +   layout="topleft" +   name="Facebook App Add"> +    <menu_item_check.on_click +     function="People.testaddFBCFolderView"/> +  </menu_item_check>   +</toggleable_menu>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index a11cd13fdb..39e777b246 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3018,6 +3018,13 @@               parameter="http://google.com"/>            </menu_item_call>            <menu_item_call +           label="FB Connect Test" +           name="FB Connect Test"> +            <menu_item_call.on_click +             function="Advanced.WebContentTest" +             parameter="https://cryptic-ridge-1632.herokuapp.com/"/> +          </menu_item_call> +          <menu_item_call               label="Dump SelectMgr"               name="Dump SelectMgr">                  <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 7ce2627be9..9bab2ccb0b 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -633,5 +633,200 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M             top="0"             right="-1" />          </panel> +         +<!-- ================================= FBC TEST tab (Temporary) ========================== --> +         +        <panel +           background_opaque="true" +           background_visible="true" +           bg_alpha_color="DkGray" +           bg_opaque_color="DkGray" +           follows="all" +           height="383" +           label="FBC TEST" +           layout="topleft" +           left="0" +           help_topic="people_fbctest_tab" +           name="fbctest_panel" +           top="0" +           width="313"> +          <accordion +                    background_visible="true" +                    bg_alpha_color="DkGray2" +                    bg_opaque_color="DkGray2" +                      follows="all" +                      height="356" +                      layout="topleft" +                      left="3" +                      name="friends_accordion" +                      top="0" +                      width="307"> +            <accordion_tab +             layout="topleft" +             height="172" +             min_height="150" +             name="tab_facebook" +             title="Facebook Friends"> +              <social_list +               allow_select="true" +               follows="all" +               height="172" +               layout="topleft" +               left="0" +               multi_select="true" +               name="facebook_friends" +               show_permissions_granted="true" +               top="0" +               width="307" /> +            </accordion_tab> +          </accordion> +          <panel +           background_visible="true" +           follows="left|right|bottom" +           height="27" +           label="bottom_panel" +           layout="topleft" +           left="3" +           name="bottom_panel" +           top_pad="0" +           width="313"> +            <menu_button +              follows="bottom|left" +              tool_tip="Options" +              height="25" +              image_hover_unselected="Toolbar_Left_Over" +              image_overlay="OptionsMenu_Off" +              image_selected="Toolbar_Left_Selected" +              image_unselected="Toolbar_Left_Off" +              layout="topleft" +              name="fbc_options_btn" +              top="1" +              width="31" /> +            <button +               follows="bottom|left" +               height="25" +               image_hover_unselected="Toolbar_Middle_Over" +               image_overlay="AddItem_Off" +               image_selected="Toolbar_Middle_Selected" +               image_unselected="Toolbar_Middle_Off" +               layout="topleft" +               left_pad="1" +               name="fbc_login_btn" +               tool_tip="Log in to FBC" +               width="31"> +              <commit_callback +                 function="People.loginFBC" /> +            </button> +            <icon +             follows="bottom|left|right" +             height="25" +             image_name="Toolbar_Right_Off" +             layout="topleft" +             left_pad="1" +             name="dummy_icon" +             width="244" +             /> +          </panel> +        </panel> +         +<!-- ================================= FBC TEST TWO tab (Final, to be renamed) ========================== --> +         +        <panel +           background_opaque="true" +           background_visible="true" +           bg_alpha_color="DkGray" +           bg_opaque_color="DkGray" +           follows="all" +           height="383" +           label="FBC TEST TWO" +           layout="topleft" +           left="0" +           help_topic="people_fbctest_tab" +           name="fbctesttwo_panel" +           top="0"> +            <panel +                follows="left|top|right" +                height="27" +                label="bottom_panel" +                layout="topleft" +                left="0" +                name="fbc_buttons_panel" +                right="-1" +                top="0"> +                <filter_editor +                follows="left|top|right" +                height="23" +                layout="topleft" +                left="6" +                label="Filter People" +                max_length_chars="300" +                name="fbc_filter_input" +                text_color="Black" +                text_pad_left="10" +                top="4" +                width="177" /> +                <button +                commit_callback.function="People.Gear" +                follows="right" +                height="25" +                image_hover_unselected="Toolbar_Middle_Over" +                image_overlay="OptionsMenu_Off" +                image_selected="Toolbar_Middle_Selected" +                image_unselected="Toolbar_Middle_Off" +                layout="topleft" +                left_pad="8" +                name="gear_btn" +                tool_tip="Actions on selected person" +                top="3" +                width="31" /> +                <menu_button +                follows="right" +                height="25" +                image_hover_unselected="Toolbar_Middle_Over" +                image_overlay="Conv_toolbar_sort" +                image_selected="Toolbar_Middle_Selected" +                image_unselected="Toolbar_Middle_Off" +                layout="topleft" +                left_pad="2" +                menu_filename="menu_people_friends_view.xml" +                menu_position="bottomleft" +                name="fbc_view_btn" +                tool_tip="View/sort options" +                top_delta="0" +                width="31" /> +                <button +                    follows="right" +                    height="25" +                    image_hover_unselected="Toolbar_Middle_Over" +                    image_overlay="AddItem_Off" +                    image_selected="Toolbar_Middle_Selected" +                    image_unselected="Toolbar_Middle_Off" +                    layout="topleft" +                    left_pad="2" +                    name="fbc_add_btn" +                    tool_tip="Offer friendship to a resident" +                    top_delta="0" +                    width="31"> +                    <commit_callback +                    function="People.AddFriendWizard" /> +                </button> +                <dnd_button +                    follows="right" +                    height="25" +                    image_hover_unselected="Toolbar_Middle_Over" +                    image_overlay="TrashItem_Off" +                    image_selected="Toolbar_Middle_Selected" +                    image_unselected="Toolbar_Middle_Off" +                    left_pad="2" +                    layout="topleft" +                    name="fbc_del_btn" +                    tool_tip="Remove selected person as a friend" +                    top_delta="0" +                    width="31"> +                    <commit_callback +                    function="People.DelFriend" /> +                </dnd_button> +            </panel> +        </panel>      </tab_container>  </panel> diff --git a/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml new file mode 100644 index 0000000000..af5aec2c34 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<person_tab_view +  folder_arrow_image="Folder_Arrow" +  folder_indentation="5" +  item_height="24"  +  item_top_pad="3" +  mouse_opaque="true" +  follows="left|top|right" +  text_pad="6" +  text_pad_left="4" +  text_pad_right="4" +  arrow_size="10" +  max_folder_item_overlap="2"/> diff --git a/indra/newview/skins/default/xui/en/widgets/person_view.xml b/indra/newview/skins/default/xui/en/widgets/person_view.xml new file mode 100644 index 0000000000..4a39109f36 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/person_view.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<person_view +  folder_arrow_image="Folder_Arrow" +  folder_indentation="5" +  item_height="24"  +  item_top_pad="3" +  mouse_opaque="true" +  follows="left|top|right" +  icon_pad="4" +  icon_width="20" +  text_pad="6" +  text_pad_left="4" +  text_pad_right="4" +  arrow_size="10" +  max_folder_item_overlap="2"> +    <avatar_icon +      follows="left" +      layout="topleft" +      height="20" +      default_icon_name="Generic_Person" +      left="5" +      top="2" +      width="20" /> +    <last_interaction_time_textbox +      layout="topleft" +      follows="right" +      font="SansSerifSmall" +      height="15" +      left_pad="5" +      right="-164" +      name="last_interaction_time_textbox" +      text_color="LtGray_50" +      value="0s" +      visible="false"  +      width="35" /> +    <permission_edit_theirs_icon +      layout="topleft" +      height="16" +      follows="right" +      image_name="Permission_Edit_Objects_Theirs" +      left_pad="3" +      right="-129" +      name="permission_edit_theirs_icon" +      tool_tip="You can edit this friend's objects" +      top="4" +      visible="false"  +      width="16" /> +    <permission_edit_mine_icon +      layout="topleft" +      height="16" +      follows="right" +      image_name="Permission_Edit_Objects_Mine" +      left_pad="3" +      right="-110" +      name="permission_edit_mine_icon" +      tool_tip="This friend can edit, delete or take your objects" +      top="4" +      visible="false"  +      width="16" /> +    <permission_map_icon +      height="16" +      follows="right" +      image_name="Permission_Visible_Map" +      left_pad="3" +      tool_tip="This friend can locate you on the map" +      right="-91" +      name="permission_map_icon" +      visible="false"  +      width="16" /> +    <permission_online_icon +      height="16" +      follows="right" +      image_name="Permission_Visible_Online" +      left_pad="3" +      right="-72" +      name="permission_online_icon" +      tool_tip="This friend can see when you're online" +      visible="false"  +      width="16" /> +    <info_btn +      follows="right" +      height="16" +      image_pressed="Info_Press" +      image_unselected="Info_Over" +      left_pad="3" +      right="-53" +      name="info_btn" +      tool_tip="More info" +      tab_stop="false" +      visible="false"       +      width="16" /> +    <profile_btn +      layout="topleft" +      follows="right" +      height="20" +      image_overlay="Web_Profile_Off" +      left_pad="5" +      right="-28" +      name="profile_btn" +      tab_stop="false" +      tool_tip="View profile" +      top="2" +      visible="false" +      width="20" /> +    <output_monitor +      auto_update="true" +      follows="right" +      draw_border="false" +      height="16" +      right="-3" +      mouse_opaque="true" +      name="speaking_indicator" +      visible="false" +      width="20" /> + </person_view> + diff --git a/indra/test_apps/llfbconnecttest/CMakeLists.txt b/indra/test_apps/llfbconnecttest/CMakeLists.txt new file mode 100644 index 0000000000..f56329a010 --- /dev/null +++ b/indra/test_apps/llfbconnecttest/CMakeLists.txt @@ -0,0 +1,304 @@ +# -*- cmake -*- +project(llfbconnecttest) + +include(00-Common) +include(FindOpenGL) +include(LLCommon) +include(LLPlugin) +include(Linking) +include(LLSharedLibs) +include(PluginAPI) +include(LLImage) +include(LLMath) +include(LLMessage) +include(LLRender) +include(LLWindow) +include(Glut) +include(Glui) + +include_directories( +    ${LLPLUGIN_INCLUDE_DIRS} +    ${LLCOMMON_INCLUDE_DIRS} +    ${LLIMAGE_INCLUDE_DIRS} +    ${LLMATH_INCLUDE_DIRS} +    ${LLMESSAGE_INCLUDE_DIRS} +    ${LLRENDER_INCLUDE_DIRS} +    ${LLWINDOW_INCLUDE_DIRS} +) + +if (DARWIN) +    include(CMakeFindFrameworks) +    find_library(COREFOUNDATION_LIBRARY CoreFoundation) +endif (DARWIN) + +### llfbconnecttest + +set(llfbconnecttest_SOURCE_FILES +    llfbconnecttest.cpp +    llfbconnecttest.h +    bookmarks.txt +    ) + +add_executable(llfbconnecttest +    WIN32 +    MACOSX_BUNDLE +    ${llfbconnecttest_SOURCE_FILES} +) + +set_target_properties(llfbconnecttest +    PROPERTIES +    WIN32_EXECUTABLE +    FALSE +) + +target_link_libraries(llfbconnecttest +  ${GLUT_LIBRARY} +  ${GLUI_LIBRARY} +  ${OPENGL_LIBRARIES} +  ${LLPLUGIN_LIBRARIES} +  ${LLMESSAGE_LIBRARIES} +  ${LLCOMMON_LIBRARIES} +  ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +if (DARWIN) +  # The testbed needs to use a couple of CoreFoundation calls now, to deal with being a bundled app. +  target_link_libraries(llfbconnecttest +    ${COREFOUNDATION_LIBRARY} +  ) +endif (DARWIN) + +add_dependencies(llfbconnecttest +  stage_third_party_libs +  SLPlugin +  media_plugin_webkit +  ${LLPLUGIN_LIBRARIES} +  ${LLMESSAGE_LIBRARIES} +  ${LLCOMMON_LIBRARIES} +) + +# turn off weird GLUI pragma  +add_definitions(-DGLUI_NO_LIB_PRAGMA) + +if (DARWIN OR LINUX) +  # glui.h contains code that triggers the "overloaded-virtual" warning in gcc.   +  set_source_files_properties(llfbconnecttest.cpp PROPERTIES COMPILE_FLAGS "-Wno-overloaded-virtual") +endif (DARWIN OR LINUX) + +# Gather build products of the various dependencies into the build directory for the testbed. + +if (DARWIN) +  # path inside the app bundle where we'll need to copy plugins and other related files +  set(PLUGINS_DESTINATION_DIR +    ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llfbconnecttest.app/Contents/Resources +  ) +   +  # create the Contents/Resources directory +  add_custom_command( +    TARGET llfbconnecttest POST_BUILD +    COMMAND ${CMAKE_COMMAND} +    ARGS +      -E +      make_directory +      ${PLUGINS_DESTINATION_DIR} +    COMMENT "Creating Resources directory in app bundle." +  )  +else (DARWIN) +  set(PLUGINS_DESTINATION_DIR +    ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ +  ) +endif (DARWIN) + +get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION) +add_custom_command(TARGET llfbconnecttest POST_BUILD +  COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_SLPLUGIN}  ${PLUGINS_DESTINATION_DIR} +  DEPENDS ${BUILT_SLPLUGIN} +) + +get_target_property(BUILT_LLCOMMON llcommon LOCATION) +add_custom_command(TARGET llfbconnecttest POST_BUILD +  COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_LLCOMMON}  ${PLUGINS_DESTINATION_DIR} +  DEPENDS ${BUILT_LLCOMMON} +) + + +if (DARWIN OR WINDOWS) +  get_target_property(BUILT_WEBKIT_PLUGIN media_plugin_webkit LOCATION) +  add_custom_command(TARGET llfbconnecttest POST_BUILD +    COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_WEBKIT_PLUGIN}  ${PLUGINS_DESTINATION_DIR} +    DEPENDS ${BUILT_WEBKIT_PLUGIN} +  ) +   +  # copy over bookmarks file if llfbconnecttest gets built +  get_target_property(BUILT_LLFBCONNECTTEST llfbconnecttest LOCATION) +  add_custom_command(TARGET llfbconnecttest POST_BUILD +    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${CMAKE_CURRENT_BINARY_DIR}/ +    DEPENDS ${BUILT_LLFBCONNECTTEST} +  ) +  # also copy it to the same place as SLPlugin, which is what the mac wants... +  add_custom_command(TARGET llfbconnecttest POST_BUILD +    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${PLUGINS_DESTINATION_DIR} +    DEPENDS ${BUILT_LLFBCONNECTTEST} +  ) +endif (DARWIN OR WINDOWS) + +if (DARWIN) +  add_custom_command(TARGET llfbconnecttest POST_BUILD +    COMMAND ${CMAKE_COMMAND} -E copy ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib ${PLUGINS_DESTINATION_DIR} +    DEPENDS ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib +  ) +endif (DARWIN) + +if(WINDOWS) +  #******************** +  # Plugin test library deploy +  # +  # Debug config runtime files required for the FB connect test +  set(fbconnecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}") +  set(fbconnecttest_debug_files +    libeay32.dll +    libglib-2.0-0.dll +    libgmodule-2.0-0.dll +    libgobject-2.0-0.dll +    libgthread-2.0-0.dll +    qtcored4.dll +    qtguid4.dll +    qtnetworkd4.dll +    qtopengld4.dll +    qtwebkitd4.dll +    ssleay32.dll +    ) +  copy_if_different( +    ${fbconnecttest_debug_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/Debug" +    out_targets +    ${fbconnecttest_debug_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) +   +  # Debug config runtime files required for the FB connect test (Qt image format plugins) +  set(fbconecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}/imageformats") +  set(fbconecttest_debug_files +    qgifd4.dll +    qicod4.dll +    qjpegd4.dll +    qmngd4.dll +    qsvgd4.dll +    qtiffd4.dll +    ) +  copy_if_different( +    ${fbconecttest_debug_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/Debug/imageformats" +    out_targets +    ${fbconecttest_debug_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + +  # Debug config runtime files required for the FB connect test (Qt codec plugins) +  set(fbconnecttest_debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}/codecs") +  set(fbconnecttest_debug_files +    qcncodecsd4.dll +    qjpcodecsd4.dll +    qkrcodecsd4.dll +    qtwcodecsd4.dll +    ) +  copy_if_different( +    ${fbconnecttest_debug_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/Debug/codecs" +    out_targets +    ${fbconnecttest_debug_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) +  +  # Release & ReleaseDebInfo config runtime files required for the FB connect test +  set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}") +  set(fbconnecttest_release_files +    libeay32.dll +    libglib-2.0-0.dll +    libgmodule-2.0-0.dll +    libgobject-2.0-0.dll +    libgthread-2.0-0.dll +    qtcore4.dll +    qtgui4.dll +    qtnetwork4.dll +    qtopengl4.dll +    qtwebkit4.dll +    qtxmlpatterns4.dll +    ssleay32.dll +    ) +  copy_if_different( +    ${fbconnecttest_release_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/Release" +    out_targets +    ${fbconnecttest_release_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + +  copy_if_different( +    ${fbconnecttest_release_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" +    out_targets +    ${fbconnecttest_release_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + +  # Release & ReleaseDebInfo config runtime files required for the FB connect test (Qt image format plugins) +  set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}/imageformats") +  set(fbconnecttest_release_files +    qgif4.dll +    qico4.dll +    qjpeg4.dll +    qmng4.dll +    qsvg4.dll +    qtiff4.dll +    ) +  copy_if_different( +    ${fbconnecttest_release_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/Release/imageformats" +    out_targets +    ${fbconnecttest_release_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + +  copy_if_different( +    ${fbconnecttest_release_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/imageformats" +    out_targets +    ${fbconnecttest_release_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + +  # Release & ReleaseDebInfo config runtime files required for the FB connect test (Qt codec plugins) +  set(fbconnecttest_release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}/codecs") +  set(fbconnecttest_release_files +    qcncodecs4.dll   +    qjpcodecs4.dll   +    qkrcodecs4.dll   +    qtwcodecs4.dll   +    ) +  copy_if_different( +    ${fbconnecttest_release_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/Release/codecs" +    out_targets +    ${fbconnecttest_release_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) + +  copy_if_different( +    ${fbconnecttest_release_src_dir} +    "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/codecs" +    out_targets +    ${fbconnecttest_release_files} +    ) +  set(fbconnect_test_targets ${fbconnect_test_targets} ${out_targets}) +  +   add_custom_target(copy_fbconnecttest_libs ALL +     DEPENDS  +     ${fbconnect_test_targets} +     ) + +  add_dependencies(llfbconnecttest copy_fbconnecttest_libs) + +endif(WINDOWS) + +ll_deploy_sharedlibs_command(llfbconnecttest)  diff --git a/indra/test_apps/llfbconnecttest/bookmarks.txt b/indra/test_apps/llfbconnecttest/bookmarks.txt new file mode 100644 index 0000000000..3995627ea9 --- /dev/null +++ b/indra/test_apps/llfbconnecttest/bookmarks.txt @@ -0,0 +1,4 @@ +# format is description, url (don't put ',' chars in description :) +# if no ',' found, whole line is used for both description and url +Google Home Page,http://www.google.com +Facebook Home Page,http://www.facebook.com diff --git a/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp b/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp new file mode 100644 index 0000000000..483a15c468 --- /dev/null +++ b/indra/test_apps/llfbconnecttest/llfbconnecttest.cpp @@ -0,0 +1,2394 @@ +/** + * @file LLFBConnectTest.cpp + * @brief Facebook Connect Test App + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "indra_constants.h" + +#include "llapr.h" +#include "llerrorcontrol.h" + +#include <math.h> +#include <iomanip> +#include <sstream> +#include <ctime> + +#include "llfbconnecttest.h" + +#if __APPLE__ +	#include <GLUT/glut.h> +	#include <CoreFoundation/CoreFoundation.h> +#else +	#define FREEGLUT_STATIC +	#include "GL/freeglut.h" +	#define GLUI_FREEGLUT +#endif + +#if LL_WINDOWS +#pragma warning(disable: 4263) +#pragma warning(disable: 4264) +#endif +#include "glui.h" + + +LLFBConnectTest* gApplication = 0; +static void gluiCallbackWrapper( int control_id ); + +//////////////////////////////////////////////////////////////////////////////// +// +static bool isTexture( GLuint texture ) +{ +	bool result = false; + +	// glIsTexture will sometimes return false for real textures... do this instead. +	if(texture != 0) +		result = true; + +	return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel::mediaPanel() +{ +	mMediaTextureHandle = 0; +	mPickTextureHandle = 0; +	mMediaSource = NULL; +	mPickTexturePixels = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel::~mediaPanel() +{ +	// delete OpenGL texture handles +	if ( isTexture( mPickTextureHandle ) ) +	{ +		std::cerr << "remMediaPanel: deleting pick texture " << mPickTextureHandle << std::endl; +		glDeleteTextures( 1, &mPickTextureHandle ); +		mPickTextureHandle = 0; +	} + +	if ( isTexture( mMediaTextureHandle ) ) +	{ +		std::cerr << "remMediaPanel: deleting media texture " << mMediaTextureHandle << std::endl; +		glDeleteTextures( 1, &mMediaTextureHandle ); +		mMediaTextureHandle = 0; +	} + +	if(mPickTexturePixels) +	{ +		delete mPickTexturePixels; +	} + +	if(mMediaSource) +	{ +		delete mMediaSource; +	} + +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLFBConnectTest::LLFBConnectTest( int app_window, int window_width, int window_height ) : +	mVersionMajor( 2 ), +	mVersionMinor( 0 ), +	mVersionPatch( 0 ), +	mMaxPanels( 25 ), +	mViewportAspect( 0 ), +	mAppWindow( app_window ), +	mCurMouseX( 0 ), +	mCurMouseY( 0 ), +	mFuzzyMedia( true ), +	mSelectedPanel( 0 ), +	mDistanceCameraToSelectedGeometry( 0.0f ), +	//mMediaBrowserControlEnableCookies( 0 ), +	mMediaBrowserControlBackButton( 0 ), +	mMediaBrowserControlForwardButton( 0 ), +	//mMediaTimeControlVolume( 100 ), +	//mMediaTimeControlSeekSeconds( 0 ), +	//mGluiMediaTimeControlWindowFlag( true ), +	mGluiMediaBrowserControlWindowFlag( true ), +	mMediaBrowserControlBackButtonFlag( true ), +	mMediaBrowserControlForwardButtonFlag( true ), +	mHomeWebUrl( "https://cryptic-ridge-1632.herokuapp.com/" ) +{ +	// debugging spam +	std::cout << std::endl << "             GLUT version: " << "3.7.6" << std::endl;	// no way to get real version from GLUT +	std::cout << std::endl << "             GLUI version: " << GLUI_Master.get_version() << std::endl; +	std::cout << std::endl << "Media Plugin Test version: " << mVersionMajor << "." << mVersionMinor << "." << mVersionPatch << std::endl; + +	// bookmark title +	mBookmarks.push_back( std::pair< std::string, std::string >( "--- Bookmarks ---", "" ) ); + +	// insert hardcoded URLs here as required for testing +	//mBookmarks.push_back( std::pair< std::string, std::string >( "description", "url" ) ); + +	// read bookmarks from file. +	// note: uses command in ./CmakeLists.txt which copies bookmmarks file from source directory +	//       to app directory (WITHOUT build configuration dir) (this is cwd in Windows within MSVC) +	//		 For example, test_apps\llplugintest and not test_apps\llplugintest\Release +	//		 This may need to be changed for Mac/Linux builds. +	// See https://jira.lindenlab.com/browse/DEV-31350 for large list of media URLs from AGNI +	const std::string bookmarks_filename( "bookmarks.txt" ); +	std::ifstream file_handle( bookmarks_filename.c_str() ); +	if ( file_handle.is_open() ) +	{ +		std::cout << "Reading bookmarks for test" << std::endl; +		while( ! file_handle.eof() ) +		{ +			std::string line; +			std::getline( file_handle, line ); +			if ( file_handle.eof() ) +				break; + +			if ( line.substr( 0, 1 ) != "#" ) +			{ +				size_t comma_pos = line.find_first_of( ',' ); +				if ( comma_pos != std::string::npos ) +				{ +					std::string description = line.substr( 0, comma_pos ); +					std::string url = line.substr( comma_pos + 1 ); +					mBookmarks.push_back( std::pair< std::string, std::string >( description, url ) ); +				} +				else +				{ +					mBookmarks.push_back( std::pair< std::string, std::string >( line, line ) ); +				}; +			}; +		}; +		std::cout << "Read " << mBookmarks.size() << " bookmarks" << std::endl; +	} +	else +	{ +		std::cout << "Unable to read bookmarks from file: " << bookmarks_filename << std::endl; +	}; + +	// initialize linden lab APR module +	ll_init_apr(); + +	// Set up llerror logging +	{ +		LLError::initForApplication("."); +		LLError::setDefaultLevel(LLError::LEVEL_INFO); +		//LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG); +	} + +	// lots of randomness in this app +	srand( ( unsigned int )time( 0 ) ); + +	// build GUI +	makeChrome(); + +	// OpenGL initialilzation +	glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); +	glClearDepth( 1.0f ); +	glEnable( GL_DEPTH_TEST ); +	glEnable( GL_COLOR_MATERIAL ); +	glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); +	glDepthFunc( GL_LEQUAL ); +	glEnable( GL_TEXTURE_2D ); +	glDisable( GL_BLEND ); +	glColor3f( 1.0f, 1.0f, 1.0f ); +	glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); +	glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + +	// start with a sane view +	resetView(); + +	// initial media panel +	const int num_initial_panels = 1; +	for( int i = 0; i < num_initial_panels; ++i ) +	{ +		//addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); +		addMediaPanel( mHomeWebUrl ); +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLFBConnectTest::~LLFBConnectTest() +{ +	// delete all media panels +	for( int i = 0; i < (int)mMediaPanels.size(); ++i ) +	{ +		remMediaPanel( mMediaPanels[ i ] ); +	}; +	 +	// Stop the plugin read thread if it's running. +	LLPluginProcessParent::setUseReadThread(false); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::reshape( int width, int height ) +{ +	// update viewport (the active window inside the chrome) +	int viewport_x, viewport_y; +	int viewport_height, viewport_width; +	GLUI_Master.get_viewport_area( &viewport_x, &viewport_y, &viewport_width, &viewport_height ); +	mViewportAspect = (float)( viewport_width ) / (float)( viewport_height ); +	glViewport( viewport_x, viewport_y, viewport_width, viewport_height ); + +	// save these as we'll need them later +	mWindowWidth = width; +	mWindowHeight = height; + +	// adjust size of URL bar so it doesn't get clipped +	mUrlEdit->set_w( mWindowWidth - 360 ); + +	// GLUI requires this +	if ( glutGetWindow() != mAppWindow ) +		glutSetWindow( mAppWindow ); + +	// trigger re-display +	glutPostRedisplay(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::bindTexture(GLuint texture, GLint row_length, GLint alignment) +{ +	glEnable( GL_TEXTURE_2D ); + +	glBindTexture( GL_TEXTURE_2D, texture ); +	glPixelStorei( GL_UNPACK_ROW_LENGTH, row_length ); +	glPixelStorei( GL_UNPACK_ALIGNMENT, alignment ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLFBConnectTest::checkGLError(const char *name) +{ +	bool result = false; +	GLenum error = glGetError(); + +	if(error != GL_NO_ERROR) +	{ +		// For some reason, glGenTextures is returning GL_INVALID_VALUE... +		std::cout << name << " ERROR 0x" << std::hex << error << std::dec << std::endl; +		result = true; +	} + +	return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +GLfloat LLFBConnectTest::distanceToCamera( GLfloat point_x, GLfloat point_y, GLfloat point_z ) +{ +	GLdouble camera_pos_x = 0.0f; +	GLdouble camera_pos_y = 0.0f; +	GLdouble camera_pos_z = 0.0f; + +	GLdouble modelMatrix[16]; +	GLdouble projMatrix[16]; +	GLint viewport[4]; + +	glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); +	glGetDoublev(GL_PROJECTION_MATRIX, projMatrix); +	glGetIntegerv(GL_VIEWPORT, viewport); + +	gluUnProject( +		(viewport[2]-viewport[0])/2 , (viewport[3]-viewport[1])/2, +		0.0, +		modelMatrix, projMatrix, viewport, +		&camera_pos_x, &camera_pos_y, &camera_pos_z ); + +	GLfloat distance = +		sqrt( ( camera_pos_x - point_x ) * ( camera_pos_x - point_x ) + +			  ( camera_pos_y - point_y ) * ( camera_pos_y - point_y ) + +			  ( camera_pos_z - point_z ) * ( camera_pos_z - point_z ) ); + +	return distance; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::drawGeometry( int panel, bool selected ) +{ +	// texture coordinates for each panel +	GLfloat non_opengl_texture_coords[ 8 ] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }; +	GLfloat opengl_texture_coords[ 8 ] =     { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + +	GLfloat *texture_coords = mMediaPanels[ panel ]->mAppTextureCoordsOpenGL?opengl_texture_coords:non_opengl_texture_coords; + +	// base coordinates for each panel +	GLfloat base_vertex_pos[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + +	// calculate posiitons +	const int num_panels = (int)mMediaPanels.size(); +	const int num_rows = (int)sqrt( (float)num_panels ); +	const int num_cols = num_panels / num_rows; +	const int panel_x = ( panel / num_rows ); +	const int panel_y = ( panel % num_rows ); + +	// default spacing is small - make it larger if checkbox set - for testing positional audio +	float spacing = 0.1f; +	//if ( mLargePanelSpacing ) +	//	spacing = 2.0f; + +	const GLfloat offset_x = num_cols * ( 1.0 + spacing ) / 2; +	const GLfloat offset_y = num_rows * ( 1.0 + spacing ) / 2; + +	// Adjust for media aspect ratios +	{ +		float aspect = 1.0f; + +		if(mMediaPanels[ panel ]->mMediaHeight != 0) +		{ +			aspect = (float)mMediaPanels[ panel ]->mMediaWidth / (float)mMediaPanels[ panel ]->mMediaHeight; +		} + +		if(aspect > 1.0f) +		{ +			// media is wider than it is high -- adjust the top and bottom in +			for( int corner = 0; corner < 4; ++corner ) +			{ +				float temp = base_vertex_pos[corner * 2 + 1]; + +				if(temp < 0.5f) +					temp += 0.5 - (0.5f / aspect); +				else +					temp -= 0.5 - (0.5f / aspect); + +				base_vertex_pos[corner * 2 + 1] = temp; +			} +		} +		else if(aspect < 1.0f) +		{ +			// media is higher than it is wide -- adjust the left and right sides in +			for( int corner = 0; corner < 4; ++corner ) +			{ +				float temp = base_vertex_pos[corner * 2]; + +				if(temp < 0.5f) +					temp += 0.5f - (0.5f * aspect); +				else +					temp -= 0.5f - (0.5f * aspect); + +				base_vertex_pos[corner * 2] = temp; +			} +		} +	} + +	glBegin( GL_QUADS ); +	for( int corner = 0; corner < 4; ++corner ) +	{ +		glTexCoord2f( texture_coords[ corner * 2 ], texture_coords[ corner * 2 + 1 ] ); +		GLfloat x = base_vertex_pos[ corner * 2 ] + panel_x * ( 1.0 + spacing ) - offset_x + spacing / 2.0f; +		GLfloat y = base_vertex_pos[ corner * 2 + 1 ] + panel_y * ( 1.0 + spacing ) - offset_y + spacing / 2.0f; + +		glVertex3f( x, y, 0.0f ); +	}; +	glEnd(); + +	// calculate distance to this panel if it's selected +	if ( selected ) +	{ +		GLfloat point_x = base_vertex_pos[ 0 ] + panel_x * ( 1.0 + spacing ) - offset_x + spacing / 2.0f; +		GLfloat point_y = base_vertex_pos[ 0 + 1 ] + panel_y * ( 1.0 + spacing ) - offset_y + spacing / 2.0f; +		GLfloat point_z = 0.0f; +		mDistanceCameraToSelectedGeometry = distanceToCamera( point_x, point_y, point_z ); +	}; +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::startPanelHighlight( float red, float green, float blue, float line_width ) +{ +	glPushAttrib( GL_ALL_ATTRIB_BITS ); +	glEnable( GL_POLYGON_OFFSET_FILL ); +	glPolygonOffset( -2.5f, -2.5f ); +	glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); +	glLineWidth( line_width ); +	glColor3f( red, green, blue ); +	glDisable( GL_TEXTURE_2D ); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::endPanelHighlight() +{ +	glPopAttrib(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::draw( int draw_type ) +{ +	for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) +	{ +		// drawing pick texture +		if ( draw_type == DrawTypePickTexture ) +		{ +			// only bother with pick if we have something to render +			// Actually, we need to pick even if we're not ready to render. +			// Otherwise you can't select and remove a panel which has gone bad. +			//if ( mMediaPanels[ panel ]->mReadyToRender ) +			{ +				glMatrixMode( GL_TEXTURE ); +				glPushMatrix(); + +				// pick texture is a power of 2 so no need to scale +				glLoadIdentity(); + +				// bind to media texture +				glLoadIdentity(); +				bindTexture( mMediaPanels[ panel ]->mPickTextureHandle ); +				glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); +				glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + +				// draw geometry using pick texture +				drawGeometry( panel, false ); + +				glMatrixMode( GL_TEXTURE ); +				glPopMatrix(); +			}; +		} +		else +		if ( draw_type == DrawTypeMediaTexture ) +		{ +			bool texture_valid = false; +			bool plugin_exited = false; + +			if(mMediaPanels[ panel ]->mMediaSource) +			{ +				texture_valid = mMediaPanels[ panel ]->mMediaSource->textureValid(); +				plugin_exited = mMediaPanels[ panel ]->mMediaSource->isPluginExited(); +			} + +			// save texture matrix (changes for each panel) +			glMatrixMode( GL_TEXTURE ); +			glPushMatrix(); + +			// only process texture if the media is ready to draw +			// (we still want to draw the geometry) +			if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid ) +			{ +				// bind to media texture +				bindTexture( mMediaPanels[ panel ]->mMediaTextureHandle ); + +				if ( mFuzzyMedia ) +				{ +					glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); +					glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); +				} +				else +				{ +					glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); +					glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); +				} + +				// scale to fit panel +				glScalef( mMediaPanels[ panel ]->mTextureScaleX, +							mMediaPanels[ panel ]->mTextureScaleY, +								1.0f ); +			}; + +			float intensity = plugin_exited?0.25f:1.0f; + +			// highlight the selected panel +			if ( mSelectedPanel && ( mMediaPanels[ panel ]->mId == mSelectedPanel->mId ) ) +			{ +				startPanelHighlight( intensity, intensity, 0.0f, 5.0f ); +				drawGeometry( panel, true ); +				endPanelHighlight(); +			} +			else +			// this panel not able to render yet since it +			// doesn't have enough information +			if ( !mMediaPanels[ panel ]->mReadyToRender ) +			{ +				startPanelHighlight( intensity, 0.0f, 0.0f, 2.0f ); +				drawGeometry( panel, false ); +				endPanelHighlight(); +			} +			else +			// just display a border around the media +			{ +				startPanelHighlight( 0.0f, intensity, 0.0f, 2.0f ); +				drawGeometry( panel, false ); +				endPanelHighlight(); +			}; + +			if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid ) +			{ +				// draw visual geometry +				drawGeometry( panel, false ); +			} + +			// restore texture matrix (changes for each panel) +			glMatrixMode( GL_TEXTURE ); +			glPopMatrix(); +		}; +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::display() +{ +	// GLUI requires this +	if ( glutGetWindow() != mAppWindow ) +		glutSetWindow( mAppWindow ); + +	// start with a clean slate +	glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); +	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + +	// set up OpenGL view +	glMatrixMode( GL_PROJECTION ); +	glLoadIdentity(); +	glFrustum( -mViewportAspect * 0.04f, mViewportAspect * 0.04f, -0.04f, 0.04f, 0.1f, 50.0f ); +	glMatrixMode( GL_MODELVIEW ); +	glLoadIdentity(); +	glTranslatef( 0.0, 0.0, 0.0f ); +	glTranslatef( mViewPos[ 0 ], mViewPos[ 1 ], -mViewPos[ 2 ] ); +	glMultMatrixf( mViewRotation ); + +	// draw pick texture +	draw( DrawTypePickTexture ); + +	// read colors and get coordinate values +	glReadPixels( mCurMouseX, mCurMouseY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, mPixelReadColor ); + +	// clear the pick render (otherwise it may depth-fight with the textures rendered later) +	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + +	// draw visible geometry +	draw( DrawTypeMediaTexture ); + +	glutSwapBuffers(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::idle() +{ +//	checkGLError("LLFBConnectTest::idle"); + +	// GLUI requires this +	if ( glutGetWindow() != mAppWindow ) +		glutSetWindow( mAppWindow ); + +	// random creation/destruction of panels enabled? +/* +	const time_t panel_timeout_time = 5; +	if ( mRandomPanelCount ) +	{ +		// time for a change +		static time_t last_panel_time = 0; +		if ( time( NULL ) - last_panel_time > panel_timeout_time ) +		{ +			if ( rand() % 2 == 0 ) +			{ +				if ( mMediaPanels.size() < 16 ) +				{ +					std::cout << "Randomly adding new panel" << std::endl; +					addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); +				}; +			} +			else +			{ +				if ( mMediaPanels.size() > 0 ) +				{ +					std::cout << "Deleting selected panel" << std::endl; +					remMediaPanel( mSelectedPanel ); +				}; +			}; +			time( &last_panel_time ); +		}; +	}; + +	// random selection of bookmarks enabled? +	const time_t bookmark_timeout_time = 5; +	if ( mRandomBookmarks ) +	{ +		// time for a change +		static time_t last_bookmark_time = 0; +		if ( time( NULL ) - last_bookmark_time > bookmark_timeout_time ) +		{ +			// go to a different random bookmark on each panel +			for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) +			{ +				std::string uri = mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second; + +				std::cout << "Random: navigating to : " << uri << std::endl; + +				std::string mime_type = mimeTypeFromUrl( uri ); + +				if ( mime_type != mMediaPanels[ panel ]->mMimeType ) +				{ +					replaceMediaPanel( mMediaPanels[ panel ], uri ); +				} +				else +				{ +					mMediaPanels[ panel ]->mMediaSource->loadURI( uri ); +					mMediaPanels[ panel ]->mMediaSource->start(); +				}; +			}; + +			time( &last_bookmark_time ); +		}; +	}; +*/ +	// update UI +	if ( mSelectedPanel ) +	{ +		// set volume based on slider if we have time media +		//if ( mGluiMediaTimeControlWindowFlag ) +		//{ +		//	mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f ); +		//}; + +		// NOTE: it is absurd that we need cache the state of GLUI controls +		//       but enabling/disabling controls drags framerate from 500+ +		//		 down to 15. Not a problem for plugin system - only this test +		// enable/disable time based UI controls based on type of plugin +		if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) +		{ +			/* +			if ( ! mGluiMediaTimeControlWindowFlag ) +			{ +				mGluiMediaTimeControlWindow->enable(); +				mGluiMediaTimeControlWindowFlag = true; +			}; +			*/ +		} +		else +		{ +			/* +			if ( mGluiMediaTimeControlWindowFlag ) +			{ +				mGluiMediaTimeControlWindow->disable(); +				mGluiMediaTimeControlWindowFlag = false; +			}; +			*/ +		}; + +		// enable/disable browser based UI controls based on type of plugin +		if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() ) +		{ +			if ( ! mGluiMediaBrowserControlWindowFlag ) +			{ +				mGluiMediaBrowserControlWindow->enable(); +				mGluiMediaBrowserControlWindowFlag = true; +			}; +		} +		else +		{ +			if ( mGluiMediaBrowserControlWindowFlag ) +			{ +				mGluiMediaBrowserControlWindow->disable(); +				mGluiMediaBrowserControlWindowFlag = false; +			}; +		}; + +		// enable/disable browser back button depending on browser history +		if ( mSelectedPanel->mMediaSource->getHistoryBackAvailable()  ) +		{ +			if ( ! mMediaBrowserControlBackButtonFlag ) +			{ +				mMediaBrowserControlBackButton->enable(); +				mMediaBrowserControlBackButtonFlag = true; +			}; +		} +		else +		{ +			if ( mMediaBrowserControlBackButtonFlag ) +			{ +				mMediaBrowserControlBackButton->disable(); +				mMediaBrowserControlBackButtonFlag = false; +			}; +		}; + +		// enable/disable browser forward button depending on browser history +		if ( mSelectedPanel->mMediaSource->getHistoryForwardAvailable()  ) +		{ +			if ( ! mMediaBrowserControlForwardButtonFlag ) +			{ +				mMediaBrowserControlForwardButton->enable(); +				mMediaBrowserControlForwardButtonFlag = true; +			}; +		} +		else +		{ +			if ( mMediaBrowserControlForwardButtonFlag ) +			{ +				mMediaBrowserControlForwardButton->disable(); +				mMediaBrowserControlForwardButtonFlag = false; +			}; +		}; + +		// NOTE: This is *very* slow and not worth optimising +		updateStatusBar(); +	}; + +	// update all the panels +	for( int panel_index = 0; panel_index < (int)mMediaPanels.size(); ++panel_index ) +	{ +		mediaPanel *panel = mMediaPanels[ panel_index ]; + +		// call plugins idle function so it can potentially update itself +		panel->mMediaSource->idle(); + +		// update each media panel +		updateMediaPanel( panel ); + +		LLRect dirty_rect; +		if ( ! panel->mMediaSource->textureValid() ) +		{ +			//std::cout << "texture invalid, skipping update..." << std::endl; +		} +		else +		if ( panel && +			 ( panel->mMediaWidth != panel->mMediaSource->getWidth() || +			   panel->mMediaHeight != panel->mMediaSource->getHeight() ) ) +		{ +			//std::cout << "Resize in progress, skipping update..." << std::endl; +		} +		else +		if ( panel->mMediaSource->getDirty( &dirty_rect ) ) +		{ +			const unsigned char* pixels = panel->mMediaSource->getBitsData(); +			if ( pixels && isTexture(panel->mMediaTextureHandle)) +			{ +				int x_offset = dirty_rect.mLeft; +				int y_offset = dirty_rect.mBottom; +				int width = dirty_rect.mRight - dirty_rect.mLeft; +				int height = dirty_rect.mTop - dirty_rect.mBottom; + +				if((dirty_rect.mRight <= panel->mTextureWidth) && (dirty_rect.mTop <= panel->mTextureHeight)) +				{ +					// Offset the pixels pointer properly +					pixels += ( y_offset * panel->mMediaSource->getTextureDepth() * panel->mMediaSource->getBitsWidth() ); +					pixels += ( x_offset * panel->mMediaSource->getTextureDepth() ); + +					// set up texture +					bindTexture( panel->mMediaTextureHandle, panel->mMediaSource->getBitsWidth() ); +					if ( mFuzzyMedia ) +					{ +						glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); +						glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); +					} +					else +					{ +						glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); +						glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); +					}; + +					checkGLError("glTexParameteri"); + +					if(panel->mMediaSource->getTextureFormatSwapBytes()) +					{ +						glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); +						checkGLError("glPixelStorei"); +					} + +					// draw portion that changes into texture +					glTexSubImage2D( GL_TEXTURE_2D, 0, +						x_offset, +						y_offset, +						width, +						height, +						panel->mMediaSource->getTextureFormatPrimary(), +						panel->mMediaSource->getTextureFormatType(), +						pixels ); + +					if(checkGLError("glTexSubImage2D")) +					{ +						std::cerr << "    panel ID=" << panel->mId << std::endl; +						std::cerr << "    texture size = " << panel->mTextureWidth << " x " << panel->mTextureHeight << std::endl; +						std::cerr << "    media size = " << panel->mMediaWidth << " x " << panel->mMediaHeight << std::endl; +						std::cerr << "    dirty rect = " << dirty_rect.mLeft << ", " << dirty_rect.mBottom << ", " << dirty_rect.mRight << ", " << dirty_rect.mTop << std::endl; +						std::cerr << "    texture width = " << panel->mMediaSource->getBitsWidth() << std::endl; +						std::cerr << "    format primary = 0x" << std::hex << panel->mMediaSource->getTextureFormatPrimary() << std::dec << std::endl; +						std::cerr << "    format type = 0x" << std::hex << panel->mMediaSource->getTextureFormatType() << std::dec << std::endl; +						std::cerr << "    pixels = " << (void*)pixels << std::endl; +					} + +					if(panel->mMediaSource->getTextureFormatSwapBytes()) +					{ +						glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); +						checkGLError("glPixelStorei"); +					} + +					panel->mMediaSource->resetDirty(); + +					panel->mReadyToRender = true; +				} +				else +				{ +					std::cerr << "dirty rect is outside current media size, skipping update" << std::endl; +				} +			}; +		}; +	}; + +	// GLUI requires this +	if ( glutGetWindow() != mAppWindow ) +		glutSetWindow( mAppWindow ); + +	// trigger re-display +	glutPostRedisplay(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::windowPosToTexturePos( int window_x, int window_y, +											   int& media_x, int& media_y, +											   int& id ) +{ +	if ( ! mSelectedPanel ) +	{ +		media_x = 0; +		media_y = 0; +		id = 0; +		return; +	}; + +	// record cursor poisiton for a readback next frame +	mCurMouseX = window_x; +	// OpenGL app == coordinate system this way +	// NOTE: unrelated to settings in plugin - this +	// is just for this app +	mCurMouseY = mWindowHeight - window_y; + +	// extract x (0..1023, y (0..1023) and id (0..15) from RGB components +	unsigned long pixel_read_color_bits = ( mPixelReadColor[ 0 ] << 16 ) | ( mPixelReadColor[ 1 ] << 8 ) | mPixelReadColor[ 2 ]; +	int texture_x = pixel_read_color_bits & 0x3ff; +	int texture_y = ( pixel_read_color_bits >> 10 ) & 0x3ff; +	id = ( pixel_read_color_bits >> 20 ) & 0x0f; + +	// scale to size of media (1024 because we use 10 bits for X and Y from 24) +	media_x = (int)( ( (float)mSelectedPanel->mMediaWidth * (float)texture_x ) / 1024.0f ); +	media_y = (int)( ( (float)mSelectedPanel->mMediaHeight * (float)texture_y ) / 1024.0f ); + +	// we assume the plugin uses an inverted coordinate scheme like OpenGL +	// if not, the plugin code inverts the Y coordinate for us - we don't need to +	media_y = mSelectedPanel->mMediaHeight - media_y; + +	if ( media_x > 0 && media_y > 0 ) +	{ +		//std::cout << "      mouse coords: " << mCurMouseX << " x " << mCurMouseY << " and id = " << id  << std::endl; +		//std::cout << "raw texture coords: " << texture_x << " x " << texture_y << " and id = " << id  << std::endl; +		//std::cout << "      media coords: " << media_x << " x " << media_y << " and id = " << id  << std::endl; +		//std::cout << std::endl; +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::selectPanelById( int id ) +{ +	for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) +	{ +		if ( mMediaPanels[ panel ]->mId == id ) +		{ +			selectPanel(mMediaPanels[ panel ]); +			return; +		}; +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::selectPanel( mediaPanel* panel ) +{ +	if( mSelectedPanel == panel ) +		return; + +	// turn off volume before we delete it +	if( mSelectedPanel && mSelectedPanel->mMediaSource ) +	{ +		mSelectedPanel->mMediaSource->setVolume( 0.0f ); +		mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_LOW ); +	}; + +	mSelectedPanel = panel; + +	if( mSelectedPanel && mSelectedPanel->mMediaSource ) +	{ +		//mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f ); +		mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_NORMAL ); + +		if(!mSelectedPanel->mStartUrl.empty()) +		{ +			mUrlEdit->set_text(const_cast<char*>(mSelectedPanel->mStartUrl.c_str()) ); +		} +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel*  LLFBConnectTest::findMediaPanel( LLPluginClassMedia* source ) +{ +	mediaPanel *result = NULL; + +	for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) +	{ +		if ( mMediaPanels[ panel ]->mMediaSource == source ) +		{ +			result = mMediaPanels[ panel ]; +		} +	} + +	return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLFBConnectTest::findMediaPanel( const std::string &target_name ) +{ +	mediaPanel *result = NULL; + +	for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) +	{ +		if ( mMediaPanels[ panel ]->mTarget == target_name ) +		{ +			result = mMediaPanels[ panel ]; +		} +	} + +	return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::navigateToNewURI( std::string uri ) +{ +	if ( uri.length() ) +	{ +		std::string mime_type = mimeTypeFromUrl( uri ); + +		if ( !mSelectedPanel->mMediaSource->isPluginExited() && (mime_type == mSelectedPanel->mMimeType) ) +		{ +			std::cout << "MIME type is the same" << std::endl; +			mSelectedPanel->mMediaSource->loadURI( uri ); +			mSelectedPanel->mMediaSource->start(); +			mBookmarkList->do_selection( 0 ); +		} +		else +		{ +			std::cout << "MIME type changed or plugin had exited" << std::endl; +			replaceMediaPanel( mSelectedPanel, uri ); +			mBookmarkList->do_selection( 0 ); +		} +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::initUrlHistory( std::string uris ) +{ +	if ( uris.length() > 0 ) +	{ +		std::cout << "init URL : " << uris << std::endl; +		LLSD historySD; + +		char *cstr, *p; +		cstr = new char[uris.size()+1]; +		strcpy(cstr, uris.c_str()); +		const char *DELIMS = " ,;"; +		p = strtok(cstr, DELIMS); +		while (p != NULL) { +			historySD.insert(0, p); +			p = strtok(NULL, DELIMS); +		} +		mSelectedPanel->mMediaSource->initializeUrlHistory(historySD); +		delete[] cstr; +	} +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::gluiCallback( int control_id ) +{ +	if ( control_id == mIdBookmarks ) +	{ +		std::string uri = mBookmarks[ mSelBookmark ].second; + +		navigateToNewURI( uri ); +	} +	else +    if ( control_id == mIdUrlEdit) +	{ +		std::string uri = mUrlEdit->get_text(); + +		navigateToNewURI( uri ); +	} +/* +	else +	if ( control_id == mIdUrlInitHistoryEdit ) +	{ +		std::string uri = mUrlInitHistoryEdit->get_text(); + +		initUrlHistory( uri ); +	} +	else +	if ( control_id == mIdControlAddPanel ) +	{ +		addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); +	} +	else +	if ( control_id == mIdControlRemPanel ) +	{ +		remMediaPanel( mSelectedPanel ); +	} +	else +	if ( control_id == mIdDisableTimeout ) +	{ +		// Set the "disable timeout" flag for all active plugins. +		for( int i = 0; i < (int)mMediaPanels.size(); ++i ) +		{ +			mMediaPanels[ i ]->mMediaSource->setDisableTimeout(mDisableTimeout); +		} +	} +	else +	if ( control_id == mIdUsePluginReadThread ) +	{ +		LLPluginProcessParent::setUseReadThread(mUsePluginReadThread); +	} +	else +	if ( control_id == mIdControlCrashPlugin ) +	{ +		// send message to plugin and ask it to crash +		// (switch out for ReleaseCandidate version :) ) +		if(mSelectedPanel && mSelectedPanel->mMediaSource) +		{ +			mSelectedPanel->mMediaSource->crashPlugin(); +		} +	} +	else +	if ( control_id == mIdControlHangPlugin ) +	{ +		// send message to plugin and ask it to hang +		// (switch out for ReleaseCandidate version :) ) +		if(mSelectedPanel && mSelectedPanel->mMediaSource) +		{ +			mSelectedPanel->mMediaSource->hangPlugin(); +		} +	} +	else +*/ +	if ( control_id == mIdControlExitApp ) +	{ +		// text for exiting plugin system cleanly +		delete this;	// clean up +		exit( 0 ); +	} +/* +	else +	if ( control_id == mIdMediaTimeControlPlay ) +	{ +		if ( mSelectedPanel ) +		{ +			mSelectedPanel->mMediaSource->setLoop( false ); +			mSelectedPanel->mMediaSource->start(); +		}; +	} +	else +	if ( control_id == mIdMediaTimeControlLoop ) +	{ +		if ( mSelectedPanel ) +		{ +			mSelectedPanel->mMediaSource->setLoop( true ); +			mSelectedPanel->mMediaSource->start(); +		}; +	} +	else +	if ( control_id == mIdMediaTimeControlPause ) +	{ +		if ( mSelectedPanel ) +			mSelectedPanel->mMediaSource->pause(); +	} +	else +	if ( control_id == mIdMediaTimeControlStop ) +	{ +		if ( mSelectedPanel ) +		{ +			mSelectedPanel->mMediaSource->stop(); +		}; +	} +	else +	if ( control_id == mIdMediaTimeControlSeek ) +	{ +		if ( mSelectedPanel ) +		{ +			// get value from spinner +			float seconds_to_seek = mMediaTimeControlSeekSeconds; +			mSelectedPanel->mMediaSource->seek( seconds_to_seek ); +			mSelectedPanel->mMediaSource->start(); +		}; +	} +	else +	if ( control_id == mIdMediaTimeControlRewind ) +	{ +		if ( mSelectedPanel ) +		{ +			mSelectedPanel->mMediaSource->setLoop( false ); +			mSelectedPanel->mMediaSource->start(-2.0f); +		}; +	} +	else +	if ( control_id == mIdMediaTimeControlFastForward ) +	{ +		if ( mSelectedPanel ) +		{ +			mSelectedPanel->mMediaSource->setLoop( false ); +			mSelectedPanel->mMediaSource->start(2.0f); +		}; +	} +	else +*/ +	if ( control_id == mIdMediaBrowserControlBack ) +	{ +		if ( mSelectedPanel ) +			mSelectedPanel->mMediaSource->browse_back(); +	} +	else +	if ( control_id == mIdMediaBrowserControlStop ) +	{ +		if ( mSelectedPanel ) +			mSelectedPanel->mMediaSource->browse_stop(); +	} +	else +	if ( control_id == mIdMediaBrowserControlForward ) +	{ +		if ( mSelectedPanel ) +			mSelectedPanel->mMediaSource->browse_forward(); +	} +	else +	if ( control_id == mIdMediaBrowserControlHome ) +	{ +		if ( mSelectedPanel ) +			mSelectedPanel->mMediaSource->loadURI( mHomeWebUrl ); +	} +	else +	if ( control_id == mIdMediaBrowserControlReload ) +	{ +		if ( mSelectedPanel ) +			mSelectedPanel->mMediaSource->browse_reload( true ); +	} +/* +	else +	if ( control_id == mIdMediaBrowserControlClearCache ) +	{ +		if ( mSelectedPanel ) +			mSelectedPanel->mMediaSource->clear_cache(); +	} +	else +	if ( control_id == mIdMediaBrowserControlClearCookies ) +	{ +		if ( mSelectedPanel ) +			mSelectedPanel->mMediaSource->clear_cookies(); +	} +	else +	if ( control_id == mIdMediaBrowserControlEnableCookies ) +	{ +		if ( mSelectedPanel ) +		{ +			if ( mMediaBrowserControlEnableCookies ) +			{ +				mSelectedPanel->mMediaSource->enable_cookies( true ); +			} +			else +			{ +				mSelectedPanel->mMediaSource->enable_cookies( false ); +			} +		}; +	}; +*/ +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::keyboard( int key ) +{ +	//if ( key == 'a' || key == 'A' ) +	//	addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); +	//else +	//if ( key == 'r' || key == 'R' ) +	//	remMediaPanel( mSelectedPanel ); +	//else +	//if ( key == 'd' || key == 'D' ) +	//	dumpPanelInfo(); +	//else +	if ( key == 27 ) +	{ +		std::cout << "Application finished - exiting..." << std::endl; +		delete this; +		exit( 0 ); +	}; + +	mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_DOWN, key, 0 , LLSD()); +	mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_UP, key, 0, LLSD()); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::mouseButton( int button, int state, int x, int y ) +{ +	if ( button == GLUT_LEFT_BUTTON ) +	{ +		if ( state == GLUT_DOWN ) +		{ +			int media_x, media_y, id; +			windowPosToTexturePos( x, y, media_x, media_y, id ); + +			if ( mSelectedPanel ) +				mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_DOWN, 0, media_x, media_y, 0 ); +		} +		else +		if ( state == GLUT_UP ) +		{ +			int media_x, media_y, id; +			windowPosToTexturePos( x, y, media_x, media_y, id ); + +			// only select a panel if we're on a panel +			// (HACK: strictly speaking this rules out clicking on +			// the origin of a panel but that's very unlikely) +			if ( media_x > 0 && media_y > 0 ) +			{ +				selectPanelById( id ); + +				if ( mSelectedPanel ) +					mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_UP, 0, media_x, media_y, 0 ); +			}; +		}; +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::mousePassive( int x, int y ) +{ +	int media_x, media_y, id; +	windowPosToTexturePos( x, y, media_x, media_y, id ); + +	if ( mSelectedPanel ) +		mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, 0, media_x, media_y, 0 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::mouseMove( int x, int y ) +{ +	int media_x, media_y, id; +	windowPosToTexturePos( x, y, media_x, media_y, id ); + +	if ( mSelectedPanel ) +		mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, 0, media_x, media_y, 0 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::makeChrome() +{ +	// IDs used by GLUI +	int start_id = 0x1000; + +	// right side window - geometry manipulators +#if __APPLE__ +	// the Apple GLUT implementation doesn't seem to set the graphic offset of subwindows correctly when they overlap in certain ways. +	// Use a separate controls window in this case. +	// GLUI window at right containing manipulation controls and other buttons +	int x = glutGet(GLUT_WINDOW_X) + glutGet(GLUT_WINDOW_WIDTH) + 4; +	int y = glutGet(GLUT_WINDOW_Y); +	GLUI* right_glui_window = GLUI_Master.create_glui( "", 0, x, y ); +#else +	GLUI* right_glui_window = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_RIGHT ); +#endif +	mViewRotationCtrl = right_glui_window->add_rotation( "Rotation", mViewRotation ); +	mViewTranslationCtrl = right_glui_window->add_translation( "Translate", GLUI_TRANSLATION_XY, mViewPos ); +	mViewTranslationCtrl->set_speed( 0.01f ); +	mViewScaleCtrl = right_glui_window->add_translation( "Scale", GLUI_TRANSLATION_Z, &mViewPos[ 2 ] ); +	mViewScaleCtrl->set_speed( 0.05f ); +	right_glui_window->set_main_gfx_window( mAppWindow ); + +	// right side window - app controls +	/* +	mIdControlAddPanel = start_id++; +	right_glui_window->add_statictext( "" ); +	right_glui_window->add_separator(); +	right_glui_window->add_statictext( "" ); +	right_glui_window->add_button( "Add panel", mIdControlAddPanel, gluiCallbackWrapper ); +	right_glui_window->add_statictext( "" ); +	mIdControlRemPanel = start_id++; +	right_glui_window->add_button( "Rem panel", mIdControlRemPanel, gluiCallbackWrapper ); +	right_glui_window->add_statictext( "" ); +	right_glui_window->add_separator(); +	right_glui_window->add_statictext( "" ); +	mIdControlCrashPlugin = start_id++; +	right_glui_window->add_button( "Crash plugin", mIdControlCrashPlugin, gluiCallbackWrapper ); +	mIdControlHangPlugin = start_id++; +	right_glui_window->add_button( "Hang plugin", mIdControlHangPlugin, gluiCallbackWrapper ); +	*/ +	right_glui_window->add_statictext( "" ); +	right_glui_window->add_separator(); +	right_glui_window->add_statictext( "" ); +	mIdControlExitApp = start_id++; +	right_glui_window->add_button( "Exit app", mIdControlExitApp, gluiCallbackWrapper ); + +	//// top window - holds bookmark UI +	mIdBookmarks = start_id++; +	mSelBookmark = 0; +	GLUI* glui_window_top = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); +	mBookmarkList = glui_window_top->add_listbox( "", &mSelBookmark, mIdBookmarks, gluiCallbackWrapper ); +	// only add the first 50 bookmarks - list can be very long sometimes (30,000+) +	// when testing list of media URLs from AGNI for example +	for( unsigned int each = 0; each < mBookmarks.size() && each < 50; ++each ) +		mBookmarkList->add_item( each, const_cast< char* >( mBookmarks[ each ].first.c_str() ) ); +	glui_window_top->set_main_gfx_window( mAppWindow ); + +	glui_window_top->add_column( false ); +	mIdUrlEdit = start_id++; +	mUrlEdit = glui_window_top->add_edittext( "Url:", GLUI_EDITTEXT_TEXT, 0, mIdUrlEdit, gluiCallbackWrapper ); +	mUrlEdit->set_w( 600 ); +	//GLUI* glui_window_top2 = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); +	//mIdUrlInitHistoryEdit = start_id++; +	//mUrlInitHistoryEdit = glui_window_top2->add_edittext( "Init History (separate by commas or semicolons):", +	//	GLUI_EDITTEXT_TEXT, 0, mIdUrlInitHistoryEdit, gluiCallbackWrapper ); +	//mUrlInitHistoryEdit->set_w( 800 ); + +	// top window - media controls for "time" media types (e.g. movies) +/* +	mGluiMediaTimeControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); +	mGluiMediaTimeControlWindow->set_main_gfx_window( mAppWindow ); +	mIdMediaTimeControlPlay = start_id++; +	mGluiMediaTimeControlWindow->add_button( "PLAY", mIdMediaTimeControlPlay, gluiCallbackWrapper ); +	mGluiMediaTimeControlWindow->add_column( false ); +	mIdMediaTimeControlLoop = start_id++; +	mGluiMediaTimeControlWindow->add_button( "LOOP", mIdMediaTimeControlLoop, gluiCallbackWrapper ); +	mGluiMediaTimeControlWindow->add_column( false ); +	mIdMediaTimeControlPause = start_id++; +	mGluiMediaTimeControlWindow->add_button( "PAUSE", mIdMediaTimeControlPause, gluiCallbackWrapper ); +	mGluiMediaTimeControlWindow->add_column( false ); + +	GLUI_Button  *button; +	mIdMediaTimeControlRewind = start_id++; +	button = mGluiMediaTimeControlWindow->add_button( "<<", mIdMediaTimeControlRewind, gluiCallbackWrapper ); +	button->set_w(30); +	mGluiMediaTimeControlWindow->add_column( false ); +	mIdMediaTimeControlFastForward = start_id++; +	button = mGluiMediaTimeControlWindow->add_button( ">>", mIdMediaTimeControlFastForward, gluiCallbackWrapper ); +	button->set_w(30); + +	mGluiMediaTimeControlWindow->add_column( true ); + +	mIdMediaTimeControlStop = start_id++; +	mGluiMediaTimeControlWindow->add_button( "STOP", mIdMediaTimeControlStop, gluiCallbackWrapper ); +	mGluiMediaTimeControlWindow->add_column( false ); +	mIdMediaTimeControlVolume = start_id++; +	GLUI_Spinner* spinner = mGluiMediaTimeControlWindow->add_spinner( "Volume", 2, &mMediaTimeControlVolume, mIdMediaTimeControlVolume, gluiCallbackWrapper); +	spinner->set_float_limits( 0, 100 ); +	mGluiMediaTimeControlWindow->add_column( true ); +	mIdMediaTimeControlSeekSeconds = start_id++; +	spinner = mGluiMediaTimeControlWindow->add_spinner( "", 2, &mMediaTimeControlSeekSeconds, mIdMediaTimeControlSeekSeconds, gluiCallbackWrapper); +	spinner->set_float_limits( 0, 200 ); +	spinner->set_w( 32 ); +	spinner->set_speed( 0.025f ); +	mGluiMediaTimeControlWindow->add_column( false ); +	mIdMediaTimeControlSeek = start_id++; +	mGluiMediaTimeControlWindow->add_button( "SEEK", mIdMediaTimeControlSeek, gluiCallbackWrapper ); +	mGluiMediaTimeControlWindow->add_column( false ); +*/ + +	// top window - media controls for "browser" media types (e.g. web browser) +	mGluiMediaBrowserControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); +	mGluiMediaBrowserControlWindow->set_main_gfx_window( mAppWindow ); +	mIdMediaBrowserControlBack = start_id++; +	mMediaBrowserControlBackButton = mGluiMediaBrowserControlWindow->add_button( "BACK", mIdMediaBrowserControlBack, gluiCallbackWrapper ); +	mGluiMediaBrowserControlWindow->add_column( false ); +	mIdMediaBrowserControlStop = start_id++; +	mGluiMediaBrowserControlWindow->add_button( "STOP", mIdMediaBrowserControlStop, gluiCallbackWrapper ); +	mGluiMediaBrowserControlWindow->add_column( false ); +	mIdMediaBrowserControlForward = start_id++; +	mMediaBrowserControlForwardButton = mGluiMediaBrowserControlWindow->add_button( "FORWARD", mIdMediaBrowserControlForward, gluiCallbackWrapper ); +	mGluiMediaBrowserControlWindow->add_column( false ); +	mIdMediaBrowserControlHome = start_id++; +	mGluiMediaBrowserControlWindow->add_button( "HOME", mIdMediaBrowserControlHome, gluiCallbackWrapper ); +	mGluiMediaBrowserControlWindow->add_column( false ); +	mIdMediaBrowserControlReload = start_id++; +	mGluiMediaBrowserControlWindow->add_button( "RELOAD", mIdMediaBrowserControlReload, gluiCallbackWrapper ); +	mGluiMediaBrowserControlWindow->add_column( false ); +	/* +	mIdMediaBrowserControlClearCache = start_id++; +	mGluiMediaBrowserControlWindow->add_button( "CLEAR CACHE", mIdMediaBrowserControlClearCache, gluiCallbackWrapper ); +	mGluiMediaBrowserControlWindow->add_column( false ); +	mIdMediaBrowserControlClearCookies = start_id++; +	mGluiMediaBrowserControlWindow->add_button( "CLEAR COOKIES", mIdMediaBrowserControlClearCookies, gluiCallbackWrapper ); +	mGluiMediaBrowserControlWindow->add_column( false ); +	mIdMediaBrowserControlEnableCookies = start_id++; +	mMediaBrowserControlEnableCookies = 0; +	mGluiMediaBrowserControlWindow->add_checkbox( "Enable Cookies", &mMediaBrowserControlEnableCookies, mIdMediaBrowserControlEnableCookies, gluiCallbackWrapper ); + +	// top window - misc controls +	GLUI* glui_window_misc_control = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); +	mIdRandomPanelCount = start_id++; +	mRandomPanelCount = 0; +	glui_window_misc_control->add_checkbox( "Randomize panel count", &mRandomPanelCount, mIdRandomPanelCount, gluiCallbackWrapper ); +	glui_window_misc_control->set_main_gfx_window( mAppWindow ); +	glui_window_misc_control->add_column( true ); +	mIdRandomBookmarks = start_id++; +	mRandomBookmarks = 0; +	glui_window_misc_control->add_checkbox( "Randomize bookmarks", &mRandomBookmarks, mIdRandomBookmarks, gluiCallbackWrapper ); +	glui_window_misc_control->set_main_gfx_window( mAppWindow ); +	glui_window_misc_control->add_column( true ); + +	mIdDisableTimeout = start_id++; +	mDisableTimeout = 0; +	glui_window_misc_control->add_checkbox( "Disable plugin timeout", &mDisableTimeout, mIdDisableTimeout, gluiCallbackWrapper ); +	glui_window_misc_control->set_main_gfx_window( mAppWindow ); +	glui_window_misc_control->add_column( true ); + +	mIdUsePluginReadThread = start_id++; +	mUsePluginReadThread = 0; +	glui_window_misc_control->add_checkbox( "Use plugin read thread", &mUsePluginReadThread, mIdUsePluginReadThread, gluiCallbackWrapper ); +	glui_window_misc_control->set_main_gfx_window( mAppWindow ); +	glui_window_misc_control->add_column( true ); + +	mIdLargePanelSpacing = start_id++; +	mLargePanelSpacing = 0; +	glui_window_misc_control->add_checkbox( "Large Panel Spacing", &mLargePanelSpacing, mIdLargePanelSpacing, gluiCallbackWrapper ); +	glui_window_misc_control->set_main_gfx_window( mAppWindow ); +	glui_window_misc_control->add_column( true ); +*/ +	// bottom window - status +	mBottomGLUIWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_BOTTOM ); +	mStatusText = mBottomGLUIWindow->add_statictext( "" ); +	mBottomGLUIWindow->set_main_gfx_window( mAppWindow ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::resetView() +{ +	mViewRotationCtrl->reset(); + +	mViewScaleCtrl->set_x( 0.0f ); +	mViewScaleCtrl->set_y( 0.0f ); +	mViewScaleCtrl->set_z( 1.3f ); + +	mViewTranslationCtrl->set_x( 0.0f ); +	mViewTranslationCtrl->set_y( 0.0f ); +	mViewTranslationCtrl->set_z( 0.0f ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels ) +{ +	int pick_texture_width = 1024; +	int pick_texture_height = 1024; +	int pick_texture_depth = 3; +	unsigned char* ptr = new unsigned char[ pick_texture_width * pick_texture_height * pick_texture_depth ]; +	for( int y = 0; y < pick_texture_height; ++y ) +	{ +		for( int x = 0; x < pick_texture_width * pick_texture_depth ; x += pick_texture_depth ) +		{ +			unsigned long bits = 0L; +			bits |= ( id << 20 ) | ( y << 10 ) | ( x / 3 ); +			unsigned char r_component = ( bits >> 16 ) & 0xff; +			unsigned char g_component = ( bits >> 8 ) & 0xff; +			unsigned char b_component = bits & 0xff; + +			ptr[ y * pick_texture_width * pick_texture_depth + x + 0 ] = r_component; +			ptr[ y * pick_texture_width * pick_texture_depth + x + 1 ] = g_component; +			ptr[ y * pick_texture_width * pick_texture_depth + x + 2 ] = b_component; +		}; +	}; + +	glGenTextures( 1, texture_handle ); + +	checkGLError("glGenTextures"); +	std::cout << "glGenTextures returned " << *texture_handle << std::endl; + +	bindTexture( *texture_handle ); +	glTexImage2D( GL_TEXTURE_2D, 0, +					GL_RGB, +						pick_texture_width, pick_texture_height, +							0, GL_RGB, GL_UNSIGNED_BYTE, ptr ); + +	*texture_pixels = ptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLFBConnectTest::mimeTypeFromUrl( std::string& url ) +{ +	// default to web +	std::string mime_type = "text/html"; + +	// we may need a more advanced MIME type accessor later :-) +	if ( url.find( ".mov" ) != std::string::npos )	// Movies +		mime_type = "video/quicktime"; +	else +	if ( url.find( ".txt" ) != std::string::npos )	// Apple Text descriptors +		mime_type = "video/quicktime"; +	else +	if ( url.find( ".mp3" ) != std::string::npos )	// Apple Text descriptors +		mime_type = "video/quicktime"; +	else +	if ( url.find( "example://" ) != std::string::npos )	// Example plugin +		mime_type = "example/example"; + +	return mime_type; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLFBConnectTest::pluginNameFromMimeType( std::string& mime_type ) +{ +#if LL_DARWIN +	std::string plugin_name( "media_plugin_null.dylib" ); +	if ( mime_type == "video/quicktime" ) +		plugin_name = "media_plugin_quicktime.dylib"; +	else +	if ( mime_type == "text/html" ) +		plugin_name = "media_plugin_webkit.dylib"; + +#elif LL_WINDOWS +	std::string plugin_name( "media_plugin_null.dll" ); + +	if ( mime_type == "video/quicktime" ) +		plugin_name = "media_plugin_quicktime.dll"; +	else +	if ( mime_type == "text/html" ) +		plugin_name = "media_plugin_webkit.dll"; +	else +	if ( mime_type == "example/example" ) +		plugin_name = "media_plugin_example.dll"; + +#elif LL_LINUX +	std::string plugin_name( "libmedia_plugin_null.so" ); + +	if ( mime_type == "video/quicktime" ) +		plugin_name = "libmedia_plugin_quicktime.so"; +	else +	if ( mime_type == "text/html" ) +		plugin_name = "libmedia_plugin_webkit.so"; +#endif +	return plugin_name; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLFBConnectTest::addMediaPanel( std::string url ) +{ +	// Get the plugin filename using the URL +	std::string mime_type = mimeTypeFromUrl( url ); +	std::string plugin_name = pluginNameFromMimeType( mime_type ); + +	// create a random size for the new media +	int media_width; +	int media_height; +	getRandomMediaSize( media_width, media_height, mime_type ); +	media_width = 1024; +	media_height = 1536; + +	// make a new plugin +	LLPluginClassMedia* media_source = new LLPluginClassMedia(this); + +	// enable cookies so the FB login works +	media_source->enable_cookies(true); + +	// tell the plugin what size we asked for +	media_source->setSize( media_width, media_height ); + +	// Use the launcher start and initialize the plugin +#if LL_DARWIN || LL_LINUX +	std::string launcher_name( "SLPlugin" ); +#elif LL_WINDOWS +	std::string launcher_name( "SLPlugin.exe" ); +#endif + +	// for this test app, use the cwd as the user data path (ugh). +#if LL_WINDOWS +	std::string user_data_path = ".\\"; +#else +        char cwd[ FILENAME_MAX ]; +	if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) +	{ +		std::cerr << "Couldn't get cwd - probably too long - failing to init." << std::endl; +		return NULL; +	} +	std::string user_data_path = std::string( cwd ) + "/"; +#endif +	media_source->setUserDataPath(user_data_path); +	media_source->init( launcher_name, user_data_path, plugin_name, false ); +	//media_source->setDisableTimeout(mDisableTimeout); + +	// make a new panel and save parameters +	mediaPanel* panel = new mediaPanel; +	panel->mMediaSource = media_source; +	panel->mStartUrl = url; +	panel->mMimeType = mime_type; +	panel->mMediaWidth = media_width; +	panel->mMediaHeight = media_height; +	panel->mTextureWidth = 0; +	panel->mTextureHeight = 0; +	panel->mTextureScaleX = 0; +	panel->mTextureScaleY = 0; +	panel->mMediaTextureHandle = 0; +	panel->mPickTextureHandle = 0; +	panel->mAppTextureCoordsOpenGL = false;	// really need an 'undefined' state here too +	panel->mReadyToRender = false; + +	// look through current media panels to find an unused index number +	bool id_exists = true; +	for( int nid = 0; nid < mMaxPanels; ++nid ) +	{ +		// does this id exist already? +		id_exists = false; +		for( int pid = 0; pid < (int)mMediaPanels.size(); ++pid ) +		{ +			if ( nid == mMediaPanels[ pid ]->mId ) +			{ +				id_exists = true; +				break; +			}; +		}; + +		// id wasn't found so we can use it +		if ( ! id_exists ) +		{ +			panel->mId = nid; +			break; +		}; +	}; + +	// if we get here and this flag is set, there is no room for any more panels +	if ( id_exists ) +	{ +		std::cout << "No room for any more panels" << std::endl; +	} +	else +	{ +		// now we have the ID we can use it to make the +		// pick texture (id is baked into texture pixels) +		makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels ); + +		// save this in the list of panels +		mMediaPanels.push_back( panel ); + +		// select the panel that was just created +		selectPanel( panel ); + +		// load and start the URL +		panel->mMediaSource->loadURI( url ); +		panel->mMediaSource->start(); + +		std::cout << "Adding new media panel for " << url << "(" << media_width << "x" << media_height << ") with index " << panel->mId << " - total panels = " << mMediaPanels.size() << std::endl; +	} +	 +	return panel; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::updateMediaPanel( mediaPanel* panel ) +{ +//	checkGLError("LLFBConnectTest::updateMediaPanel"); + +	if ( ! panel ) +		return; + +	if(!panel->mMediaSource || !panel->mMediaSource->textureValid()) +	{ +		panel->mReadyToRender = false; +		return; +	} + +	// take a reference copy of the plugin values since they +	// might change during this lifetime of this function +	int plugin_media_width = panel->mMediaSource->getWidth(); +	int plugin_media_height = panel->mMediaSource->getHeight(); +	int plugin_texture_width = panel->mMediaSource->getBitsWidth(); +	int plugin_texture_height = panel->mMediaSource->getBitsHeight(); + +	// If the texture isn't created or the media or texture dimensions changed AND +	// the sizes are valid then we need to delete the old media texture (if necessary) +	// then make a new one. +	if ((panel->mMediaTextureHandle == 0 || +		 panel->mMediaWidth != plugin_media_width || +		 panel->mMediaHeight != plugin_media_height || +		 panel->mTextureWidth != plugin_texture_width || +		 panel->mTextureHeight != plugin_texture_height) && +		( plugin_media_width > 0 && plugin_media_height > 0 && +		  plugin_texture_width > 0 && plugin_texture_height > 0 ) ) +	{ +		std::cout << "Valid media size (" <<  plugin_media_width << " x " << plugin_media_height +				<< ") and texture size (" <<  plugin_texture_width << " x " << plugin_texture_height +				<< ") for panel with ID=" << panel->mId << " - making texture" << std::endl; + +		// delete old GL texture +		if ( isTexture( panel->mMediaTextureHandle ) ) +		{ +			std::cerr << "updateMediaPanel: deleting texture " << panel->mMediaTextureHandle << std::endl; +			glDeleteTextures( 1, &panel->mMediaTextureHandle ); +			panel->mMediaTextureHandle = 0; +		} + +		std::cerr << "before: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl; + +		// make a GL texture based on the dimensions the plugin told us +		GLuint new_texture = 0; +		glGenTextures( 1, &new_texture ); + +		checkGLError("glGenTextures"); + +		std::cout << "glGenTextures returned " << new_texture << std::endl; + +		panel->mMediaTextureHandle = new_texture; + +		bindTexture( panel->mMediaTextureHandle ); + +		std::cout << "Setting texture size to " << plugin_texture_width << " x " << plugin_texture_height << std::endl; +		glTexImage2D( GL_TEXTURE_2D, 0, +			GL_RGB, +				plugin_texture_width, plugin_texture_height, +					0, GL_RGB, GL_UNSIGNED_BYTE, +						0 ); + + +		std::cerr << "after: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl; +	}; + +	// update our record of the media and texture dimensions +	// NOTE: do this after we we check for sizes changes +	panel->mMediaWidth = plugin_media_width; +	panel->mMediaHeight = plugin_media_height; +	panel->mTextureWidth = plugin_texture_width; +	panel->mTextureHeight = plugin_texture_height; +	if ( plugin_texture_width > 0 ) +	{ +		panel->mTextureScaleX = (double)panel->mMediaWidth / (double)panel->mTextureWidth; +	}; +	if ( plugin_texture_height > 0 ) +	{ +		panel->mTextureScaleY = (double)panel->mMediaHeight / (double)panel->mTextureHeight; +	}; + +	// update the flag which tells us if the media source uses OprnGL coords or not. +	panel->mAppTextureCoordsOpenGL = panel->mMediaSource->getTextureCoordsOpenGL(); + +	// Check to see if we have enough to render this panel. +	// If we do, set a flag that the display functions use so +	// they only render a panel with media if it's ready. +	if ( panel->mMediaWidth < 0 || +		 panel->mMediaHeight < 0 || +		 panel->mTextureWidth < 1 || +		 panel->mTextureHeight < 1 || +		 panel->mMediaTextureHandle == 0 ) +	{ +		panel->mReadyToRender = false; +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLFBConnectTest::replaceMediaPanel( mediaPanel* panel, std::string url ) +{ +	// no media panels so we can't change anything - have to add +	if ( mMediaPanels.size() == 0 ) +		return NULL; + +	// sanity check +	if ( ! panel ) +		return NULL; + +	int index; +	for(index = 0; index < (int)mMediaPanels.size(); index++) +	{ +		if(mMediaPanels[index] == panel) +			break; +	} + +	if(index >= (int)mMediaPanels.size()) +	{ +		// panel isn't in mMediaPanels +		return NULL; +	} + +	std::cout << "Replacing media panel with index " << panel->mId << std::endl; + +	int panel_id = panel->mId; + +	if(mSelectedPanel == panel) +		mSelectedPanel = NULL; + +	delete panel; + +	// Get the plugin filename using the URL +	std::string mime_type = mimeTypeFromUrl( url ); +	std::string plugin_name = pluginNameFromMimeType( mime_type ); + +	// create a random size for the new media +	int media_width; +	int media_height; +	getRandomMediaSize( media_width, media_height, mime_type ); + +	// make a new plugin +	LLPluginClassMedia* media_source = new LLPluginClassMedia(this); + +	// tell the plugin what size we asked for +	media_source->setSize( media_width, media_height ); + +	// Use the launcher start and initialize the plugin +#if LL_DARWIN || LL_LINUX +	std::string launcher_name( "SLPlugin" ); +#elif LL_WINDOWS +	std::string launcher_name( "SLPlugin.exe" ); +#endif + +	// for this test app, use the cwd as the user data path (ugh). +#if LL_WINDOWS +	std::string user_data_path = ".\\"; +#else +        char cwd[ FILENAME_MAX ]; +	if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) +	{ +		std::cerr << "Couldn't get cwd - probably too long - failing to init." << std::endl; +		return NULL; +	} +	std::string user_data_path = std::string( cwd ) + "/"; +#endif + +	media_source->setUserDataPath(user_data_path); +	media_source->init( launcher_name, user_data_path, plugin_name, false ); +	//media_source->setDisableTimeout(mDisableTimeout); + +	// make a new panel and save parameters +	panel = new mediaPanel; +	panel->mMediaSource = media_source; +	panel->mStartUrl = url; +	panel->mMimeType = mime_type; +	panel->mMediaWidth = media_width; +	panel->mMediaHeight = media_height; +	panel->mTextureWidth = 0; +	panel->mTextureHeight = 0; +	panel->mTextureScaleX = 0; +	panel->mTextureScaleY = 0; +	panel->mMediaTextureHandle = 0; +	panel->mPickTextureHandle = 0; +	panel->mAppTextureCoordsOpenGL = false;	// really need an 'undefined' state here too +	panel->mReadyToRender = false; + +	panel->mId = panel_id; + +	// Replace the entry in the panels array +	mMediaPanels[index] = panel; + +	// now we have the ID we can use it to make the +	// pick texture (id is baked into texture pixels) +	makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels ); + +	// select the panel that was just created +	selectPanel( panel ); + +	// load and start the URL +	panel->mMediaSource->loadURI( url ); +	panel->mMediaSource->start(); +	 +	return panel; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::getRandomMediaSize( int& width, int& height, std::string mime_type ) +{ +	// Make a new media source with a random size which we'll either +	// directly or the media plugin will tell us what it wants later. +	// Use a random size so we can test support for weird media sizes. +	// (Almost everything else will get filled in later once the +	// plugin responds) +	// NB. Do we need to enforce that width is on 4 pixel boundary? +	width = ( ( rand() % 170 ) + 30 ) * 4; +	height = ( ( rand() % 170 ) + 30 ) * 4; + +	// adjust this random size if it's a browser so we get +	// a more useful size for testing.. +	if ( mime_type == "text/html" || mime_type == "example/example"  ) +	{ +		width = ( ( rand() % 100 ) + 100 ) * 4; +		height = ( width * ( ( rand() % 400 ) + 1000 ) ) / 1000; +	}; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::remMediaPanel( mediaPanel* panel ) +{ +	// always leave one panel +	if ( mMediaPanels.size() == 1 ) +		return; + +	// sanity check - don't think this can happen but see above for a case where it might... +	if ( ! panel ) +		return; + +	std::cout << "Removing media panel with index " << panel->mId << " - total panels = " << mMediaPanels.size() - 1 << std::endl; + +	if(mSelectedPanel == panel) +		mSelectedPanel = NULL; + +	delete panel; + +	// remove from storage list +	for( int i = 0; i < (int)mMediaPanels.size(); ++i ) +	{ +		if ( mMediaPanels[ i ] == panel ) +		{ +			mMediaPanels.erase( mMediaPanels.begin() + i ); +			break; +		}; +	}; + +	// select the first panel +	selectPanel( mMediaPanels[ 0 ] ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::updateStatusBar() +{ +	if ( ! mSelectedPanel ) +		return; + +	// cache results - this is a very slow function +	static int cached_id = -1; +	static int cached_media_width = -1; +	static int cached_media_height = -1; +	static int cached_texture_width = -1; +	static int cached_texture_height = -1; +	static bool cached_supports_browser_media = true; +	static bool cached_supports_time_media = false; +	static int cached_movie_time = -1; +	static GLfloat cached_distance = -1.0f; + +	static std::string cached_plugin_version = ""; +	if ( +		 cached_id == mSelectedPanel->mId && +		 cached_media_width == mSelectedPanel->mMediaWidth && +		 cached_media_height  == mSelectedPanel->mMediaHeight && +		 cached_texture_width == mSelectedPanel->mTextureWidth && +		 cached_texture_height == mSelectedPanel->mTextureHeight && +		 cached_supports_browser_media == mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() && +		 cached_supports_time_media == mSelectedPanel->mMediaSource->pluginSupportsMediaTime() && +		 cached_plugin_version == mSelectedPanel->mMediaSource->getPluginVersion() && +		 cached_movie_time == (int)mSelectedPanel->mMediaSource->getCurrentTime() && +		 cached_distance == mDistanceCameraToSelectedGeometry +	   ) +	{ +		// nothing changed so don't spend time here +		return; +	}; + +	std::ostringstream stream( "" ); + +	stream.str( "" ); +	stream.clear(); + +	stream << "Id: "; +	stream << std::setw( 2 ) << std::setfill( '0' ); +	stream << mSelectedPanel->mId; +	stream << " | "; +	stream << "Media: "; +	stream << std::setw( 3 ) << std::setfill( '0' ); +	stream << mSelectedPanel->mMediaWidth; +	stream << " x "; +	stream << std::setw( 3 ) << std::setfill( '0' ); +	stream << mSelectedPanel->mMediaHeight; +	stream << " | "; +	stream << "Texture: "; +	stream << std::setw( 4 ) << std::setfill( '0' ); +	stream << mSelectedPanel->mTextureWidth; +	stream << " x "; +	stream << std::setw( 4 ) << std::setfill( '0' ); +	stream << mSelectedPanel->mTextureHeight; + +	stream << " | "; +	stream << "Distance: "; +	stream << std::setw( 6 ); +	stream << std::setprecision( 3 ); +	stream << std::setprecision( 3 ); +	stream << mDistanceCameraToSelectedGeometry; +	stream << " | "; + +	if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() ) +		stream << "BROWSER"; +	else +	if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) +		stream << "TIME   "; +	stream << " | "; +	stream << mSelectedPanel->mMediaSource->getPluginVersion(); +	stream << " | "; +	if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) +	{ +		stream << std::setw( 3 ) << std::setfill( '0' ); +		stream << (int)mSelectedPanel->mMediaSource->getCurrentTime(); +		stream << " / "; +		stream << std::setw( 3 ) << std::setfill( '0' ); +		stream << (int)mSelectedPanel->mMediaSource->getDuration(); +		stream << " @ "; +		stream << (int)mSelectedPanel->mMediaSource->getCurrentPlayRate(); +		stream << " | "; +	}; + +	glutSetWindow( mBottomGLUIWindow->get_glut_window_id() ); +	mStatusText->set_text( const_cast< char*>( stream.str().c_str() ) ); +	glutSetWindow( mAppWindow ); + +	// caching +	cached_id = mSelectedPanel->mId; +	cached_media_width = mSelectedPanel->mMediaWidth; +	cached_media_height = mSelectedPanel->mMediaHeight; +	cached_texture_width = mSelectedPanel->mTextureWidth; +	cached_texture_height = mSelectedPanel->mTextureHeight; +	cached_supports_browser_media = mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser(); +	cached_supports_time_media = mSelectedPanel->mMediaSource->pluginSupportsMediaTime(); +	cached_plugin_version = mSelectedPanel->mMediaSource->getPluginVersion(); +	cached_movie_time = (int)mSelectedPanel->mMediaSource->getCurrentTime(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::dumpPanelInfo() +{ +	std::cout << std::endl << "===== Media Panels =====" << std::endl; +	for( int i = 0; i < (int)mMediaPanels.size(); ++i ) +	{ +		std::cout << std::setw( 2 ) << std::setfill( '0' ); +		std::cout << i + 1 << "> "; +		std::cout << "Id: "; +		std::cout << std::setw( 2 ) << std::setfill( '0' ); +		std::cout << mMediaPanels[ i ]->mId; +		std::cout << " | "; +		std::cout << "Media: "; +		std::cout << std::setw( 3 ) << std::setfill( '0' ); +		std::cout << mMediaPanels[ i ]->mMediaWidth; +		std::cout << " x "; +		std::cout << std::setw( 3 ) << std::setfill( '0' ); +		std::cout << mMediaPanels[ i ]->mMediaHeight; +		std::cout << " | "; +		std::cout << "Texture: "; +		std::cout << std::setw( 4 ) << std::setfill( '0' ); +		std::cout << mMediaPanels[ i ]->mTextureWidth; +		std::cout << " x "; +		std::cout << std::setw( 4 ) << std::setfill( '0' ); +		std::cout << mMediaPanels[ i ]->mTextureHeight; +		std::cout << " | "; +		if ( mMediaPanels[ i ] == mSelectedPanel ) +			std::cout << "(selected)"; + +		std::cout << std::endl; +	}; +	std::cout << "========================" << std::endl; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFBConnectTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ +	// Uncomment this to make things much, much quieter. +//	return; + +	switch(event) +	{ +		case MEDIA_EVENT_CONTENT_UPDATED: +			// too spammy -- don't log these +//			std::cerr <<  "Media event:  MEDIA_EVENT_CONTENT_UPDATED " << std::endl; +		break; + +		case MEDIA_EVENT_TIME_DURATION_UPDATED: +			// too spammy -- don't log these +//			std::cerr <<  "Media event:  MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << std::endl; +		break; + +		case MEDIA_EVENT_SIZE_CHANGED: +			std::cerr <<  "Media event:  MEDIA_EVENT_SIZE_CHANGED " << std::endl; +		break; + +		case MEDIA_EVENT_CURSOR_CHANGED: +			std::cerr <<  "Media event:  MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << std::endl; +		break; + +		case MEDIA_EVENT_NAVIGATE_BEGIN: +			std::cerr <<  "Media event:  MEDIA_EVENT_NAVIGATE_BEGIN " << std::endl; +		break; + +		case MEDIA_EVENT_NAVIGATE_COMPLETE: +			std::cerr <<  "Media event:  MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << std::endl; +		break; + +		case MEDIA_EVENT_PROGRESS_UPDATED: +			std::cerr <<  "Media event:  MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << std::endl; +		break; + +		case MEDIA_EVENT_STATUS_TEXT_CHANGED: +			std::cerr <<  "Media event:  MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << std::endl; +		break; + +		case MEDIA_EVENT_NAME_CHANGED: +			std::cerr <<  "Media event:  MEDIA_EVENT_NAME_CHANGED, new name is: " << self->getMediaName() << std::endl; +			glutSetWindowTitle( self->getMediaName().c_str() ); +		break; + +		case MEDIA_EVENT_LOCATION_CHANGED: +		{ +			std::cerr <<  "Media event:  MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << std::endl; +			mediaPanel* panel = findMediaPanel(self); +			if(panel != NULL) +			{ +				panel->mStartUrl = self->getLocation(); +				if(panel == mSelectedPanel) +				{ +					mUrlEdit->set_text(const_cast<char*>(panel->mStartUrl.c_str()) ); +				} +			} +		} +		break; + +		case MEDIA_EVENT_NAVIGATE_ERROR_PAGE: +			std::cerr <<  "Media event:  MEDIA_EVENT_NAVIGATE_ERROR_PAGE, uri is: " << self->getClickURL() << std::endl; +		break; +			 +		case MEDIA_EVENT_CLICK_LINK_HREF: +		{ +			std::cerr <<  "Media event:  MEDIA_EVENT_CLICK_LINK_HREF, uri is " << self->getClickURL() << ", target is " << self->getClickTarget() << std::endl; +			// retrieve the event parameters +			std::string url = self->getClickURL(); +			std::string target = self->getClickTarget(); +			 +			if(target == "_external") +			{ +				// this should open in an external browser, but since this is a test app we don't care. +			} +			else if(target == "_blank") +			{ +				// Create a new panel with the specified URL. +				addMediaPanel(url); +			} +			else // other named target +			{ +				mediaPanel *target_panel = findMediaPanel(target); +				if(target_panel) +				{ +					target_panel = replaceMediaPanel(target_panel, url); +				} +				else +				{ +					target_panel = addMediaPanel(url); +				} + +				if(target_panel) +				{ +					target_panel->mTarget = target; +				} +			} +		} +		break; + +		case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: +			std::cerr <<  "Media event:  MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << std::endl; +		break; + +		case MEDIA_EVENT_PLUGIN_FAILED: +			std::cerr <<  "Media event:  MEDIA_EVENT_PLUGIN_FAILED" << std::endl; +		break; + +		case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH: +			std::cerr <<  "Media event:  MEDIA_EVENT_PLUGIN_FAILED_LAUNCH" << std::endl; +		break; + +		case MEDIA_EVENT_CLOSE_REQUEST: +			std::cerr <<  "Media event:  MEDIA_EVENT_CLOSE_REQUEST" << std::endl; +		break; +		 +		case MEDIA_EVENT_PICK_FILE_REQUEST: +			std::cerr <<  "Media event:  MEDIA_EVENT_PICK_FILE_REQUEST" << std::endl; +			// TODO: display an actual file picker +			self->sendPickFileResponse("cake"); +		break; + +		case MEDIA_EVENT_GEOMETRY_CHANGE: +			std::cerr <<  "Media event:  MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID()  +				<< ", x = " << self->getGeometryX()  +				<< ", y = " << self->getGeometryY()  +				<< ", width = " << self->getGeometryWidth()  +				<< ", height = " << self->getGeometryHeight()  +				<< std::endl; +		break; + +		case MEDIA_EVENT_AUTH_REQUEST: +		{ +			//std::cerr <<  "Media event:  MEDIA_EVENT_AUTH_REQUEST, url " << self->getAuthURL() ", realm " << self->getAuthRealm() << std::endl; + +			// TODO: display an auth dialog +			self->sendAuthResponse(false, "", ""); +		} +		break; + +		case MEDIA_EVENT_LINK_HOVERED: +		{ +			std::cerr <<  "Media event:  MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << std::endl; +		} +		break; + +		default: +		{ +			std::cerr <<  "Media event:  <unknown>, code is: " << int(event) << std::endl; +		} +		break; +	} +} + +//////////////////////////////////////////////////////////////////////////////// +// +static void gluiCallbackWrapper( int control_id ) +{ +	if ( gApplication ) +		gApplication->gluiCallback( control_id ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutReshape( int width, int height ) +{ +	if ( gApplication ) +		gApplication->reshape( width, height ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutDisplay() +{ +	if ( gApplication ) +		gApplication->display(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutIdle(int update_ms) +{ +	GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms); + +	if ( gApplication ) +		gApplication->idle(); + +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutKeyboard( unsigned char key, int x, int y ) +{ +	if ( gApplication ) +		gApplication->keyboard( key ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMousePassive( int x, int y ) +{ +	if ( gApplication ) +		gApplication->mousePassive( x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseMove( int x , int y ) +{ +	if ( gApplication ) +		gApplication->mouseMove( x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseButton( int button, int state, int x, int y ) +{ +	if ( gApplication ) +		gApplication->mouseButton( button, state, x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int main( int argc, char* argv[] ) +{ +#if LL_DARWIN +	// Set the current working directory to <application bundle>/Contents/Resources/ +	CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()); +	if(resources_url != NULL) +	{ +		CFStringRef resources_string = CFURLCopyFileSystemPath(resources_url, kCFURLPOSIXPathStyle); +		CFRelease(resources_url); +		if(resources_string != NULL) +		{ +			char buffer[PATH_MAX] = ""; +			if(CFStringGetCString(resources_string, buffer, sizeof(buffer), kCFStringEncodingUTF8)) +			{ +				chdir(buffer); +			} +			CFRelease(resources_string); +		} +	} +#endif + +	glutInit( &argc, argv ); +	glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB ); + +	const int app_window_x = 80; +	const int app_window_y = 0; +	const int app_window_width = 960; +	const int app_window_height = 960; + +	glutInitWindowPosition( app_window_x, app_window_y ); +	glutInitWindowSize( app_window_width, app_window_height ); + +	int app_window_handle = glutCreateWindow( "LLFBConnectTest" ); + +	glutDisplayFunc( glutDisplay ); + +	GLUI_Master.set_glutReshapeFunc( glutReshape ); +	GLUI_Master.set_glutKeyboardFunc( glutKeyboard ); +	GLUI_Master.set_glutMouseFunc( glutMouseButton ); + +	glutPassiveMotionFunc( glutMousePassive ); +	glutMotionFunc( glutMouseMove ); + +	glutSetWindow( app_window_handle ); + +	gApplication = new LLFBConnectTest( app_window_handle, app_window_width, app_window_height ); + +	// update at approximately 60hz +	int update_ms = 1000 / 60; + +	GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms); + +	glutMainLoop(); + +	delete gApplication; +} diff --git a/indra/test_apps/llfbconnecttest/llfbconnecttest.h b/indra/test_apps/llfbconnecttest/llfbconnecttest.h new file mode 100644 index 0000000000..6f442a55b3 --- /dev/null +++ b/indra/test_apps/llfbconnecttest/llfbconnecttest.h @@ -0,0 +1,207 @@ +/** + * @file LLFBConnectTest.cpp + * @brief Facebook Connect Test App + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_FB_CONNECT_H +#define LL_FB_CONNECT_H + +#include <vector> +#include <string> +#include "llpluginclassmedia.h" +#include "llgl.h" + +// Forward declarations +class GLUI_Rotation; +class GLUI_Translation; +class GLUI_Listbox; +class GLUI_EditText; +class GLUI_StaticText; +class GLUI; +class GLUI_Button; + +//////////////////////////////////////////////////////////////////////////////// +// +struct mediaPanel +{ +	public: +		mediaPanel(); +		~mediaPanel(); +		int mId; +		std::string mStartUrl; +		std::string mMimeType; +		std::string mTarget; +		LLPluginClassMedia *mMediaSource; +		int mMediaWidth; +		int mMediaHeight; +		int mTextureWidth; +		int mTextureHeight; +		double mTextureScaleX; +		double mTextureScaleY; +		GLuint mMediaTextureHandle; +		GLuint mPickTextureHandle; +		unsigned char* mPickTexturePixels; +		bool mAppTextureCoordsOpenGL; +		bool mReadyToRender; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +class LLFBConnectTest : public LLPluginClassMediaOwner +{ +	public: +		LLFBConnectTest( int app_window, int window_width, int window_height ); +		~LLFBConnectTest(); + +		void reshape( int width, int height ); +		void display(); +		void idle(); +		void gluiCallback( int control_id ); +		void keyboard( int key ); +		void mousePassive( int x, int y ); +		void mouseButton( int button, int state, int x, int y ); +		void mouseMove( int x, int y ); + +		void bindTexture(GLuint texture, GLint row_length = 0, GLint alignment = 1); +		bool checkGLError(const char *name = "OpenGL"); +		void drawGeometry( int panel, bool selected ); +		void startPanelHighlight( float red, float green, float blue, float line_width ); +		void endPanelHighlight(); +		enum { DrawTypePickTexture, DrawTypeMediaTexture }; +		void draw( int draw_type ); +		void windowPosToTexturePos( int window_x, int window_y, int& media_x, int& media_y, int& id ); + +		mediaPanel* addMediaPanel( std::string url ); +		void updateMediaPanel( mediaPanel* panel ); +		void remMediaPanel( mediaPanel* panel ); +		mediaPanel* replaceMediaPanel( mediaPanel* panel, std::string url ); +		void getRandomMediaSize( int& width, int& height, std::string mime_type ); +		void navigateToNewURI( std::string uri ); +        void initUrlHistory( std::string uri ); +		void selectPanelById( int id ); +		void selectPanel( mediaPanel* panel ); +		mediaPanel* findMediaPanel( LLPluginClassMedia* panel ); +		mediaPanel* findMediaPanel( const std::string &target_name ); +		void makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels ); +		void makeChrome(); +		void resetView(); + +		void dumpPanelInfo(); +		void updateStatusBar(); + +		GLfloat distanceToCamera( GLfloat point_x, GLfloat point_y, GLfloat point_z ); +		 + +	// Inherited from LLPluginClassMediaOwner +	/*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent); + +	private: +		const int mVersionMajor; +		const int mVersionMinor; +		const int mVersionPatch; +		const int mMaxPanels; +		int mAppWindow; +		int mWindowWidth; +		int mWindowHeight; +		int mCurMouseX; +		int mCurMouseY; +		unsigned char mPixelReadColor[ 3 ]; +		bool mFuzzyMedia; +		const std::string mHomeWebUrl; + +		std::vector< mediaPanel* > mMediaPanels; +		mediaPanel* mSelectedPanel; +		std::string mimeTypeFromUrl( std::string& url ); +		std::string pluginNameFromMimeType( std::string& mime_type ); + +		GLUI_Rotation* mViewRotationCtrl; +		GLUI_Translation* mViewScaleCtrl; +		GLUI_Translation* mViewTranslationCtrl; +		float mViewportAspect; +		float mViewPos[ 3 ]; +		float mViewRotation[ 16 ]; + +		float mDistanceCameraToSelectedGeometry; + +		int mIdControlAddPanel; +		int mIdControlRemPanel; + +		std::vector< std::pair< std::string, std::string > > mBookmarks; +		GLUI_Listbox* mBookmarkList; +		int mIdBookmarks; +		int mIdUrlEdit; +		GLUI_EditText* mUrlEdit; +        //int mIdUrlInitHistoryEdit; +		//GLUI_EditText* mUrlInitHistoryEdit; +		int mSelBookmark; +		//int mIdRandomPanelCount; +		//int mRandomPanelCount; +		//int mIdRandomBookmarks; +		//int mRandomBookmarks; +		//int mIdDisableTimeout; +		//int mDisableTimeout; +		//int mIdUsePluginReadThread; +		//int mUsePluginReadThread; +		//int mIdLargePanelSpacing; +		//int mLargePanelSpacing; +		//int mIdControlCrashPlugin; +		//int mIdControlHangPlugin; +		int mIdControlExitApp; + +		//GLUI* mGluiMediaTimeControlWindow; +		//int mIdMediaTimeControlPlay; +		//int mIdMediaTimeControlLoop; +		//int mIdMediaTimeControlPause; +		//int mIdMediaTimeControlStop; +		//int mIdMediaTimeControlSeek; +		//int mIdMediaTimeControlVolume; +		//int mMediaTimeControlVolume; +		//int mIdMediaTimeControlSeekSeconds; +		//int mMediaTimeControlSeekSeconds; +		//int mIdMediaTimeControlRewind; +		//int mIdMediaTimeControlFastForward; + +		GLUI* mGluiMediaBrowserControlWindow; +		int mIdMediaBrowserControlBack; +		GLUI_Button* mMediaBrowserControlBackButton; +		int mIdMediaBrowserControlStop; +		int mIdMediaBrowserControlForward; +		GLUI_Button* mMediaBrowserControlForwardButton; +		bool mGluiMediaTimeControlWindowFlag; +		bool mGluiMediaBrowserControlWindowFlag; +		bool mMediaBrowserControlBackButtonFlag; +		bool mMediaBrowserControlForwardButtonFlag; +		int mIdMediaBrowserControlHome; +		int mIdMediaBrowserControlReload; +		int mIdMediaBrowserControlClearCache; +		int mIdMediaBrowserControlClearCookies; +		int mIdMediaBrowserControlEnableCookies; +		int mMediaBrowserControlEnableCookies; + +		GLUI* mBottomGLUIWindow; +		GLUI_StaticText* mStatusText; +}; + +#endif	// LL_FB_CONNECT_H + | 
