summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/CMakeLists.txt9
-rw-r--r--indra/newview/CMakeLists.txt8
-rw-r--r--indra/newview/llappviewer.cpp43
-rw-r--r--indra/newview/llfloaterimcontainer.cpp7
-rw-r--r--indra/newview/llpanelpeople.cpp496
-rw-r--r--indra/newview/llpanelpeople.h38
-rw-r--r--indra/newview/llparticipantlist.cpp18
-rw-r--r--indra/newview/llparticipantlist.h1
-rw-r--r--indra/newview/llpersonfolderview.cpp114
-rw-r--r--indra/newview/llpersonfolderview.h64
-rw-r--r--indra/newview/llpersonmodelcommon.cpp281
-rw-r--r--indra/newview/llpersonmodelcommon.h244
-rw-r--r--indra/newview/llpersontabview.cpp398
-rw-r--r--indra/newview/llpersontabview.h146
-rw-r--r--indra/newview/llsociallist.cpp154
-rw-r--r--indra/newview/llsociallist.h102
-rw-r--r--indra/newview/llviewerregion.cpp2
-rw-r--r--indra/newview/skins/default/xui/en/menu_gear_fbc.xml44
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml7
-rw-r--r--indra/newview/skins/default/xui/en/panel_people.xml195
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_tab_view.xml13
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_view.xml116
-rw-r--r--indra/test_apps/llfbconnecttest/CMakeLists.txt304
-rw-r--r--indra/test_apps/llfbconnecttest/bookmarks.txt4
-rw-r--r--indra/test_apps/llfbconnecttest/llfbconnecttest.cpp2394
-rw-r--r--indra/test_apps/llfbconnecttest/llfbconnecttest.h207
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&apos;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&apos;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
+