From 420b91db29485df39fd6e724e782c449158811cb Mon Sep 17 00:00:00 2001 From: James Cook Date: Tue, 2 Jan 2007 08:33:20 +0000 Subject: Print done when done. --- indra/newview/llimview.cpp | 717 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 indra/newview/llimview.cpp (limited to 'indra/newview/llimview.cpp') diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp new file mode 100644 index 0000000000..fb8438dc93 --- /dev/null +++ b/indra/newview/llimview.cpp @@ -0,0 +1,717 @@ +/** + * @file llimview.cpp + * @brief Container for Instant Messaging + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llimview.h" + +#include "llfontgl.h" +#include "llrect.h" +#include "llerror.h" +#include "llbutton.h" +#include "llstring.h" +#include "linked_lists.h" +#include "llvieweruictrlfactory.h" + +#include "llagent.h" +#include "llcallingcard.h" +#include "llviewerwindow.h" +#include "llresmgr.h" +#include "llfloaternewim.h" +#include "llimpanel.h" +#include "llresizebar.h" +#include "lltabcontainer.h" +#include "viewer.h" +#include "llfloater.h" +#include "llresizehandle.h" +#include "llkeyboard.h" +#include "llui.h" +#include "llviewermenu.h" +#include "llcallingcard.h" +#include "lltoolbar.h" + +const EInstantMessage EVERYONE_DIALOG = IM_NOTHING_SPECIAL; +const EInstantMessage GROUP_DIALOG = IM_SESSION_GROUP_START; +const EInstantMessage DEFAULT_DIALOG = IM_NOTHING_SPECIAL; + +// +// Globals +// +LLIMView* gIMView = NULL; + +// +// Statics +// +static LLString sOnlyUserMessage; +static LLString sOfflineMessage; + +// +// Helper Functions +// + +// returns true if a should appear before b +BOOL group_dictionary_sort( LLGroupData* a, LLGroupData* b ) +{ + return (LLString::compareDict( a->mName, b->mName ) < 0); +} + + +// the other_participant_id is either an agent_id, a group_id, or an inventory +// folder item_id (collection of calling cards) +LLUUID compute_session_id(EInstantMessage dialog, const LLUUID& other_participant_id) +{ + LLUUID session_id; + if (IM_SESSION_GROUP_START == dialog) + { + // slam group session_id to the group_id (other_participant_id) + session_id = other_participant_id; + } + else + { + LLUUID agent_id = gAgent.getID(); + if (other_participant_id == agent_id) + { + // if we try to send an IM to ourselves then the XOR would be null + // so we just make the session_id the same as the agent_id + session_id = agent_id; + } + else + { + // peer-to-peer or peer-to-asset session_id is the XOR + session_id = other_participant_id ^ agent_id; + } + } + return session_id; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// LLFloaterIM +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +LLFloaterIM::LLFloaterIM() +{ +} + +BOOL LLFloaterIM::postBuild() +{ + requires("only_user_message", WIDGET_TYPE_TEXT_BOX); + requires("offline_message", WIDGET_TYPE_TEXT_BOX); + + if (checkRequirements()) + { + sOnlyUserMessage = childGetText("only_user_message"); + sOfflineMessage = childGetText("offline_message"); + return TRUE; + } + return FALSE; +} + +//// virtual +//BOOL LLFloaterIM::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) +//{ +// BOOL handled = FALSE; +// if (getEnabled() +// && mask == (MASK_CONTROL|MASK_SHIFT)) +// { +// if (key == 'W') +// { +// LLFloater* floater = getActiveFloater(); +// if (floater) +// { +// if (mTabContainer->getTabCount() == 1) +// { +// // trying to close last tab, close +// // entire window. +// close(); +// handled = TRUE; +// } +// } +// } +// } +// return handled || LLMultiFloater::handleKeyHere(key, mask, called_from_parent); +//} + +void LLFloaterIM::onClose(bool app_quitting) +{ + if (!app_quitting) + { + gSavedSettings.setBOOL("ShowIM", FALSE); + } + setVisible(FALSE); +} + +//virtual +void LLFloaterIM::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point) +{ + // this code is needed to fix the bug where new IMs received will resize the IM floater. + // SL-29075, SL-24556, and others + LLRect parent_rect = getRect(); + S32 dheight = LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT; + LLRect rect(0, parent_rect.getHeight()-dheight, parent_rect.getWidth(), 0); + floaterp->reshape(rect.getWidth(), rect.getHeight(), TRUE); + LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLIMViewFriendObserver +// +// Bridge to suport knowing when the inventory has changed. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLIMViewFriendObserver : public LLFriendObserver +{ +public: + LLIMViewFriendObserver(LLIMView* tv) : mTV(tv) {} + virtual ~LLIMViewFriendObserver() {} + virtual void changed(U32 mask) + { + if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE)) + { + mTV->refresh(); + } + } +protected: + LLIMView* mTV; +}; + + +// +// Public Static Member Functions +// + +// This is a helper function to determine what kind of im session +// should be used for the given agent. +// static +EInstantMessage LLIMView::defaultIMTypeForAgent(const LLUUID& agent_id) +{ + EInstantMessage type = IM_SESSION_CARDLESS_START; + if(is_agent_friend(agent_id)) + { + if(LLAvatarTracker::instance().isBuddyOnline(agent_id)) + { + type = IM_SESSION_ADD; + } + else + { + type = IM_SESSION_OFFLINE_ADD; + } + } + return type; +} + +// static +//void LLIMView::onPinButton(void*) +//{ +// BOOL state = gSavedSettings.getBOOL( "PinTalkViewOpen" ); +// gSavedSettings.setBOOL( "PinTalkViewOpen", !state ); +//} + +// static +void LLIMView::toggle(void*) +{ + static BOOL return_to_mouselook = FALSE; + + // Hide the button and show the floater or vice versa. + llassert( gIMView ); + BOOL old_state = gIMView->getFloaterOpen(); + + // If we're in mouselook and we triggered the Talk View, we want to talk. + if( gAgent.cameraMouselook() && old_state ) + { + return_to_mouselook = TRUE; + gAgent.changeCameraToDefault(); + return; + } + + BOOL new_state = !old_state; + + if (new_state) + { + // ...making visible + if ( gAgent.cameraMouselook() ) + { + return_to_mouselook = TRUE; + gAgent.changeCameraToDefault(); + } + } + else + { + // ...hiding + if ( gAgent.cameraThirdPerson() && return_to_mouselook ) + { + gAgent.changeCameraToMouselook(); + } + return_to_mouselook = FALSE; + } + + gIMView->setFloaterOpen( new_state ); +} + +// +// Member Functions +// + +LLIMView::LLIMView(const std::string& name, const LLRect& rect) : + LLView(name, rect, FALSE), + mFriendObserver(NULL), + mIMReceived(FALSE) +{ + gIMView = this; + mFriendObserver = new LLIMViewFriendObserver(this); + LLAvatarTracker::instance().addObserver(mFriendObserver); + + mTalkFloater = new LLFloaterIM(); + gUICtrlFactory->buildFloater(mTalkFloater, "floater_im.xml"); + + // New IM Panel + mNewIMFloater = new LLFloaterNewIM(); + mTalkFloater->addFloater(mNewIMFloater, TRUE); + + // Tabs sometimes overlap resize handle + mTalkFloater->moveResizeHandleToFront(); +} + +LLIMView::~LLIMView() +{ + LLAvatarTracker::instance().removeObserver(mFriendObserver); + delete mFriendObserver; + // Children all cleaned up by default view destructor. +} + +EWidgetType LLIMView::getWidgetType() const +{ + return WIDGET_TYPE_TALK_VIEW; +} + +LLString LLIMView::getWidgetTag() const +{ + return LL_TALK_VIEW_TAG; +} + +// Add a message to a session. +void LLIMView::addMessage( + const LLUUID& session_id, + const LLUUID& other_participant_id, + const char* from, + const char* msg, + const char* session_name, + EInstantMessage dialog, + U32 parent_estate_id, + const LLUUID& region_id, + const LLVector3& position) +{ + LLFloaterIMPanel* floater; + LLUUID new_session_id = session_id; + if (new_session_id.isNull()) + { + new_session_id = compute_session_id(dialog, other_participant_id); + } + floater = findFloaterBySession(new_session_id); + if (!floater) + { + floater = findFloaterBySession(other_participant_id); + if (floater) + { + llinfos << "found the IM session " << session_id + << " by participant " << other_participant_id << llendl; + } + } + if(floater) + { + floater->addHistoryLine(msg); + } + else + { + const char* name = from; + if(session_name && (strlen(session_name)>1)) + { + name = session_name; + } + floater = createFloater(new_session_id, other_participant_id, name, dialog, FALSE); + + // When we get a new IM, and if you are a god, display a bit + // of information about the source. This is to help liaisons + // when answering questions. + if(gAgent.isGodlike()) + { + // XUI:translate + std::ostringstream bonus_info; + bonus_info << "*** parent estate: " + << parent_estate_id + << ((parent_estate_id == 1) ? ", mainland" : "") + << ((parent_estate_id == 5) ? ", teen" : ""); + + // once we have web-services (or something) which returns + // information about a region id, we can print this out + // and even have it link to map-teleport or something. + //<< "*** region_id: " << region_id << std::endl + //<< "*** position: " << position << std::endl; + + floater->addHistoryLine(bonus_info.str()); + } + + floater->addHistoryLine(msg); + make_ui_sound("UISndNewIncomingIMSession"); + } + + if( !mTalkFloater->getVisible() && !floater->getVisible()) + { + //if the IM window is not open and the floater is not visible (i.e. not torn off) + LLFloater* previouslyActiveFloater = mTalkFloater->getActiveFloater(); + + // select the newly added floater (or the floater with the new line added to it). + // it should be there. + mTalkFloater->selectFloater(floater); + + //there was a previously unseen IM, make that old tab flashing + //it is assumed that the most recently unseen IM tab is the one current selected/active + if ( previouslyActiveFloater && getIMReceived() ) + { + mTalkFloater->setFloaterFlashing(previouslyActiveFloater, TRUE); + } + + //notify of a new IM + notifyNewIM(); + } +} + +void LLIMView::notifyNewIM() +{ + if(!gIMView->getFloaterOpen()) + { + mIMReceived = TRUE; + } +} + +BOOL LLIMView::getIMReceived() const +{ + return mIMReceived; +} + +// This method returns TRUE if the local viewer has a session +// currently open keyed to the uuid. +BOOL LLIMView::isIMSessionOpen(const LLUUID& uuid) +{ + LLFloaterIMPanel* floater = findFloaterBySession(uuid); + if(floater) return TRUE; + return FALSE; +} + +// This adds a session to the talk view. The name is the local name of +// the session, dialog specifies the type of session. If the session +// exists, it is brought forward. Specifying id = NULL results in an +// im session to everyone. Returns the uuid of the session. +LLUUID LLIMView::addSession(const std::string& name, + EInstantMessage dialog, + const LLUUID& other_participant_id) +{ + LLUUID session_id = compute_session_id(dialog, other_participant_id); + LLFloaterIMPanel* floater = findFloaterBySession(session_id); + if(!floater) + { + floater = createFloater(session_id, other_participant_id, name, dialog, TRUE); + LLDynamicArray ids; + ids.put(other_participant_id); + noteOfflineUsers(floater, ids); + floater->addParticipants(ids); + mTalkFloater->showFloater(floater); + } + else + { + floater->open(); + } + //mTabContainer->selectTabPanel(panel); + floater->setInputFocus(TRUE); + return floater->getSessionID(); +} + +// Adds a session using the given session_id. If the session already exists +// the dialog type is assumed correct. Returns the uuid of the session. +LLUUID LLIMView::addSession(const std::string& name, + EInstantMessage dialog, + const LLUUID& session_id, + const LLDynamicArray& ids) +{ + if (0 == ids.getLength()) + { + return LLUUID::null; + } + + LLUUID other_participant_id = ids[0]; + LLUUID new_session_id = session_id; + if (new_session_id.isNull()) + { + new_session_id = compute_session_id(dialog, other_participant_id); + } + + LLFloaterIMPanel* floater = findFloaterBySession(new_session_id); + if(!floater) + { + // On creation, use the first element of ids as the "other_participant_id" + floater = createFloater(new_session_id, other_participant_id, name, dialog, TRUE); + noteOfflineUsers(floater, ids); + } + floater->addParticipants(ids); + mTalkFloater->showFloater(floater); + //mTabContainer->selectTabPanel(panel); + floater->setInputFocus(TRUE); + return floater->getSessionID(); +} + +// This removes the panel referenced by the uuid, and then restores +// internal consistency. The internal pointer is not deleted. +void LLIMView::removeSession(const LLUUID& session_id) +{ + LLFloaterIMPanel* floater = findFloaterBySession(session_id); + if(floater) + { + mFloaters.erase(floater->getHandle()); + mTalkFloater->removeFloater(floater); + //mTabContainer->removeTabPanel(floater); + } +} + +void LLIMView::refresh() +{ + S32 old_scroll_pos = mNewIMFloater->getScrollPos(); + mNewIMFloater->clearAllTargets(); + + // build a list of groups. + LLLinkedList group_list( group_dictionary_sort ); + + LLGroupData* group; + S32 count = gAgent.mGroups.count(); + S32 i; + // read/sort groups on the first pass. + for(i = 0; i < count; ++i) + { + group = &(gAgent.mGroups.get(i)); + group_list.addDataSorted( group ); + } + + // add groups to the floater on the second pass. + for(group = group_list.getFirstData(); + group; + group = group_list.getNextData()) + { + mNewIMFloater->addTarget(group->mID, group->mName, + (void*)(&GROUP_DIALOG), TRUE, FALSE); + } + + // build a set of buddies in the current buddy list. + LLCollectAllBuddies collector; + LLAvatarTracker::instance().applyFunctor(collector); + LLCollectAllBuddies::buddy_map_t::iterator it; + LLCollectAllBuddies::buddy_map_t::iterator end; + it = collector.mOnline.begin(); + end = collector.mOnline.end(); + for( ; it != end; ++it) + { + mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), TRUE); + } + it = collector.mOffline.begin(); + end = collector.mOffline.end(); + for( ; it != end; ++it) + { + mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), FALSE); + } + + if(gAgent.isGodlike()) + { + // XUI:translate + mNewIMFloater->addTarget(LLUUID::null, "All Residents, All Grids", + (void*)(&EVERYONE_DIALOG), TRUE, FALSE); + } + + mNewIMFloater->setScrollPos( old_scroll_pos ); +} + +// JC - This used to set console visibility. It doesn't any more. +void LLIMView::setFloaterOpen(BOOL set_open) +{ + gSavedSettings.setBOOL("ShowIM", set_open); + + //RN "visible" and "open" are considered synonomous for now + if (set_open) + { + mTalkFloater->open(); + } + else + { + mTalkFloater->close(); + } + + if( set_open ) + { + // notifyNewIM(); + + // We're showing the IM, so mark view as non-pending + mIMReceived = FALSE; + } +} + + +BOOL LLIMView::getFloaterOpen() +{ + return mTalkFloater->getVisible(); +} + +void LLIMView::pruneSessions() +{ + if(mNewIMFloater) + { + BOOL removed = TRUE; + LLFloaterIMPanel* floater = NULL; + while(removed) + { + removed = FALSE; + std::set::iterator handle_it; + for(handle_it = mFloaters.begin(); + handle_it != mFloaters.end(); + ++handle_it) + { + floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it); + if(floater && !mNewIMFloater->isUUIDAvailable(floater->getOtherParticipantID())) + { + // remove this floater + removed = TRUE; + mFloaters.erase(handle_it++); + floater->close(); + break; + } + } + } + } +} + + +void LLIMView::disconnectAllSessions() +{ + if(mNewIMFloater) + { + mNewIMFloater->setEnabled(FALSE); + } + LLFloaterIMPanel* floater = NULL; + std::set::iterator handle_it; + for(handle_it = mFloaters.begin(); + handle_it != mFloaters.end(); + ++handle_it) + { + floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it); + if (floater) + { + floater->setEnabled(FALSE); + } + } +} + + +// This method returns the im panel corresponding to the uuid +// provided. The uuid can either be a session id or an agent +// id. Returns NULL if there is no matching panel. +LLFloaterIMPanel* LLIMView::findFloaterBySession(const LLUUID& session_id) +{ + LLFloaterIMPanel* rv = NULL; + std::set::iterator handle_it; + for(handle_it = mFloaters.begin(); + handle_it != mFloaters.end(); + ++handle_it) + { + rv = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it); + if(rv && session_id == rv->getSessionID()) + { + break; + } + rv = NULL; + } + return rv; +} + + +BOOL LLIMView::hasSession(const LLUUID& session_id) +{ + return (findFloaterBySession(session_id) != NULL); +} + + +// create a floater and update internal representation for +// consistency. Returns the pointer, caller (the class instance since +// it is a private method) is not responsible for deleting the +// pointer. Add the floater to this but do not select it. +LLFloaterIMPanel* LLIMView::createFloater(const LLUUID& session_id, + const LLUUID& other_participant_id, + const std::string& session_label, + EInstantMessage dialog, + BOOL user_initiated) +{ + if (session_id.isNull()) + { + llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl; + } + llinfos << "LLIMView::createFloater: from " << other_participant_id + << " in session " << session_id << llendl; + LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label, + LLRect(), + session_label, + session_id, + other_participant_id, + dialog); + LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END; + mTalkFloater->addFloater(floater, FALSE, i_pt); + mFloaters.insert(floater->getHandle()); + return floater; +} + +void LLIMView::noteOfflineUsers(LLFloaterIMPanel* floater, + const LLDynamicArray& ids) +{ + S32 count = ids.count(); + if(count == 0) + { + floater->addHistoryLine(sOnlyUserMessage); + } + else + { + const LLRelationship* info = NULL; + LLAvatarTracker& at = LLAvatarTracker::instance(); + for(S32 i = 0; i < count; ++i) + { + info = at.getBuddyInfo(ids.get(i)); + char first[DB_FIRST_NAME_BUF_SIZE]; + char last[DB_LAST_NAME_BUF_SIZE]; + if(info && !info->isOnline() + && gCacheName->getName(ids.get(i), first, last)) + { + LLUIString offline = sOfflineMessage; + offline.setArg("[FIRST]", first); + offline.setArg("[LAST]", last); + floater->addHistoryLine(offline); + } + } + } +} + +void LLIMView::processIMTypingStart(const LLIMInfo* im_info) +{ + processIMTypingCore(im_info, TRUE); +} + +void LLIMView::processIMTypingStop(const LLIMInfo* im_info) +{ + processIMTypingCore(im_info, FALSE); +} + +void LLIMView::processIMTypingCore(const LLIMInfo* im_info, BOOL typing) +{ + LLUUID session_id = compute_session_id(im_info->mIMType, im_info->mFromID); + LLFloaterIMPanel* floater = findFloaterBySession(session_id); + if (floater) + { + floater->processIMTyping(im_info, typing); + } +} -- cgit v1.2.3