aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llimview.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llimview.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/newview/llimview.cpp')
-rw-r--r--linden/indra/newview/llimview.cpp736
1 files changed, 736 insertions, 0 deletions
diff --git a/linden/indra/newview/llimview.cpp b/linden/indra/newview/llimview.cpp
new file mode 100644
index 0000000..e2d33fd
--- /dev/null
+++ b/linden/indra/newview/llimview.cpp
@@ -0,0 +1,736 @@
1/**
2 * @file llimview.cpp
3 * @brief Container for Instant Messaging
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29
30#include "llimview.h"
31
32#include "llfontgl.h"
33#include "llrect.h"
34#include "llerror.h"
35#include "llbutton.h"
36#include "llstring.h"
37#include "linked_lists.h"
38#include "llvieweruictrlfactory.h"
39
40#include "llagent.h"
41#include "llcallingcard.h"
42#include "llviewerwindow.h"
43#include "llresmgr.h"
44#include "llfloaternewim.h"
45#include "llimpanel.h"
46#include "llresizebar.h"
47#include "lltabcontainer.h"
48#include "viewer.h"
49#include "llfloater.h"
50#include "llresizehandle.h"
51#include "llkeyboard.h"
52#include "llui.h"
53#include "llviewermenu.h"
54#include "llcallingcard.h"
55#include "lltoolbar.h"
56
57const EInstantMessage EVERYONE_DIALOG = IM_NOTHING_SPECIAL;
58const EInstantMessage GROUP_DIALOG = IM_SESSION_GROUP_START;
59const EInstantMessage DEFAULT_DIALOG = IM_NOTHING_SPECIAL;
60
61//
62// Globals
63//
64LLIMView* gIMView = NULL;
65
66//
67// Statics
68//
69static LLString sOnlyUserMessage;
70static LLString sOfflineMessage;
71
72//
73// Helper Functions
74//
75
76// returns true if a should appear before b
77static BOOL group_dictionary_sort( LLGroupData* a, LLGroupData* b )
78{
79 return (LLString::compareDict( a->mName, b->mName ) < 0);
80}
81
82
83// the other_participant_id is either an agent_id, a group_id, or an inventory
84// folder item_id (collection of calling cards)
85static LLUUID compute_session_id(EInstantMessage dialog, const LLUUID& other_participant_id)
86{
87 LLUUID session_id;
88 if (IM_SESSION_GROUP_START == dialog)
89 {
90 // slam group session_id to the group_id (other_participant_id)
91 session_id = other_participant_id;
92 }
93 else
94 {
95 LLUUID agent_id = gAgent.getID();
96 if (other_participant_id == agent_id)
97 {
98 // if we try to send an IM to ourselves then the XOR would be null
99 // so we just make the session_id the same as the agent_id
100 session_id = agent_id;
101 }
102 else
103 {
104 // peer-to-peer or peer-to-asset session_id is the XOR
105 session_id = other_participant_id ^ agent_id;
106 }
107 }
108 return session_id;
109}
110
111//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112// LLFloaterIM
113//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114
115LLFloaterIM::LLFloaterIM()
116{
117 gUICtrlFactory->buildFloater(this, "floater_im.xml");
118}
119
120BOOL LLFloaterIM::postBuild()
121{
122 requires("only_user_message", WIDGET_TYPE_TEXT_BOX);
123 requires("offline_message", WIDGET_TYPE_TEXT_BOX);
124
125 if (checkRequirements())
126 {
127 sOnlyUserMessage = childGetText("only_user_message");
128 sOfflineMessage = childGetText("offline_message");
129 return TRUE;
130 }
131 return FALSE;
132}
133
134//// virtual
135//BOOL LLFloaterIM::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
136//{
137// BOOL handled = FALSE;
138// if (getEnabled()
139// && mask == (MASK_CONTROL|MASK_SHIFT))
140// {
141// if (key == 'W')
142// {
143// LLFloater* floater = getActiveFloater();
144// if (floater)
145// {
146// if (mTabContainer->getTabCount() == 1)
147// {
148// // trying to close last tab, close
149// // entire window.
150// close();
151// handled = TRUE;
152// }
153// }
154// }
155// }
156// return handled || LLMultiFloater::handleKeyHere(key, mask, called_from_parent);
157//}
158
159void LLFloaterIM::onClose(bool app_quitting)
160{
161 if (!app_quitting)
162 {
163 gSavedSettings.setBOOL("ShowIM", FALSE);
164 }
165 setVisible(FALSE);
166}
167
168//virtual
169void LLFloaterIM::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
170{
171 // this code is needed to fix the bug where new IMs received will resize the IM floater.
172 // SL-29075, SL-24556, and others
173 LLRect parent_rect = getRect();
174 S32 dheight = LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT;
175 LLRect rect(0, parent_rect.getHeight()-dheight, parent_rect.getWidth(), 0);
176 floaterp->reshape(rect.getWidth(), rect.getHeight(), TRUE);
177 LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
178}
179
180//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
181// Class LLIMViewFriendObserver
182//
183// Bridge to suport knowing when the inventory has changed.
184//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
185
186class LLIMViewFriendObserver : public LLFriendObserver
187{
188public:
189 LLIMViewFriendObserver(LLIMView* tv) : mTV(tv) {}
190 virtual ~LLIMViewFriendObserver() {}
191 virtual void changed(U32 mask)
192 {
193 if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
194 {
195 mTV->refresh();
196 }
197 }
198protected:
199 LLIMView* mTV;
200};
201
202
203//
204// Public Static Member Functions
205//
206
207// This is a helper function to determine what kind of im session
208// should be used for the given agent.
209// static
210EInstantMessage LLIMView::defaultIMTypeForAgent(const LLUUID& agent_id)
211{
212 EInstantMessage type = IM_SESSION_CARDLESS_START;
213 if(is_agent_friend(agent_id))
214 {
215 if(LLAvatarTracker::instance().isBuddyOnline(agent_id))
216 {
217 type = IM_SESSION_ADD;
218 }
219 else
220 {
221 type = IM_SESSION_OFFLINE_ADD;
222 }
223 }
224 return type;
225}
226
227// static
228//void LLIMView::onPinButton(void*)
229//{
230// BOOL state = gSavedSettings.getBOOL( "PinTalkViewOpen" );
231// gSavedSettings.setBOOL( "PinTalkViewOpen", !state );
232//}
233
234// static
235void LLIMView::toggle(void*)
236{
237 static BOOL return_to_mouselook = FALSE;
238
239 // Hide the button and show the floater or vice versa.
240 llassert( gIMView );
241 BOOL old_state = gIMView->getFloaterOpen();
242
243 // If we're in mouselook and we triggered the Talk View, we want to talk.
244 if( gAgent.cameraMouselook() && old_state )
245 {
246 return_to_mouselook = TRUE;
247 gAgent.changeCameraToDefault();
248 return;
249 }
250
251 BOOL new_state = !old_state;
252
253 if (new_state)
254 {
255 // ...making visible
256 if ( gAgent.cameraMouselook() )
257 {
258 return_to_mouselook = TRUE;
259 gAgent.changeCameraToDefault();
260 }
261 }
262 else
263 {
264 // ...hiding
265 if ( gAgent.cameraThirdPerson() && return_to_mouselook )
266 {
267 gAgent.changeCameraToMouselook();
268 }
269 return_to_mouselook = FALSE;
270 }
271
272 gIMView->setFloaterOpen( new_state );
273}
274
275//
276// Member Functions
277//
278
279LLIMView::LLIMView(const std::string& name, const LLRect& rect) :
280 LLView(name, rect, FALSE),
281 mFriendObserver(NULL),
282 mIMReceived(FALSE)
283{
284 gIMView = this;
285 mFriendObserver = new LLIMViewFriendObserver(this);
286 LLAvatarTracker::instance().addObserver(mFriendObserver);
287
288 mTalkFloater = new LLFloaterIM();
289
290 // New IM Panel
291 mNewIMFloater = new LLFloaterNewIM();
292 mTalkFloater->addFloater(mNewIMFloater, TRUE);
293
294 // Tabs sometimes overlap resize handle
295 mTalkFloater->moveResizeHandleToFront();
296}
297
298LLIMView::~LLIMView()
299{
300 LLAvatarTracker::instance().removeObserver(mFriendObserver);
301 delete mFriendObserver;
302 // Children all cleaned up by default view destructor.
303}
304
305EWidgetType LLIMView::getWidgetType() const
306{
307 return WIDGET_TYPE_TALK_VIEW;
308}
309
310LLString LLIMView::getWidgetTag() const
311{
312 return LL_TALK_VIEW_TAG;
313}
314
315// Add a message to a session.
316void LLIMView::addMessage(
317 const LLUUID& session_id,
318 const LLUUID& other_participant_id,
319 const char* from,
320 const char* msg,
321 const char* session_name,
322 EInstantMessage dialog,
323 U32 parent_estate_id,
324 const LLUUID& region_id,
325 const LLVector3& position)
326{
327 LLFloaterIMPanel* floater;
328 LLUUID new_session_id = session_id;
329 if (new_session_id.isNull())
330 {
331 new_session_id = compute_session_id(dialog, other_participant_id);
332 }
333 floater = findFloaterBySession(new_session_id);
334 if (!floater)
335 {
336 floater = findFloaterBySession(other_participant_id);
337 if (floater)
338 {
339 llinfos << "found the IM session " << session_id
340 << " by participant " << other_participant_id << llendl;
341 }
342 }
343 if(floater)
344 {
345 floater->addHistoryLine(msg);
346 }
347 else
348 {
349 const char* name = from;
350 if(session_name && (strlen(session_name)>1))
351 {
352 name = session_name;
353 }
354 floater = createFloater(new_session_id, other_participant_id, name, dialog, FALSE);
355
356 // When we get a new IM, and if you are a god, display a bit
357 // of information about the source. This is to help liaisons
358 // when answering questions.
359 if(gAgent.isGodlike())
360 {
361 // XUI:translate
362 std::ostringstream bonus_info;
363 bonus_info << "*** parent estate: "
364 << parent_estate_id
365 << ((parent_estate_id == 1) ? ", mainland" : "")
366 << ((parent_estate_id == 5) ? ", teen" : "");
367
368 // once we have web-services (or something) which returns
369 // information about a region id, we can print this out
370 // and even have it link to map-teleport or something.
371 //<< "*** region_id: " << region_id << std::endl
372 //<< "*** position: " << position << std::endl;
373
374 floater->addHistoryLine(bonus_info.str());
375 }
376
377 floater->addHistoryLine(msg);
378 make_ui_sound("UISndNewIncomingIMSession");
379 }
380
381 if( !mTalkFloater->getVisible() && !floater->getVisible())
382 {
383 //if the IM window is not open and the floater is not visible (i.e. not torn off)
384 LLFloater* previouslyActiveFloater = mTalkFloater->getActiveFloater();
385
386 // select the newly added floater (or the floater with the new line added to it).
387 // it should be there.
388 mTalkFloater->selectFloater(floater);
389
390 //there was a previously unseen IM, make that old tab flashing
391 //it is assumed that the most recently unseen IM tab is the one current selected/active
392 if ( previouslyActiveFloater && getIMReceived() )
393 {
394 mTalkFloater->setFloaterFlashing(previouslyActiveFloater, TRUE);
395 }
396
397 //notify of a new IM
398 notifyNewIM();
399 }
400}
401
402void LLIMView::notifyNewIM()
403{
404 if(!gIMView->getFloaterOpen())
405 {
406 mIMReceived = TRUE;
407 }
408}
409
410BOOL LLIMView::getIMReceived() const
411{
412 return mIMReceived;
413}
414
415// This method returns TRUE if the local viewer has a session
416// currently open keyed to the uuid.
417BOOL LLIMView::isIMSessionOpen(const LLUUID& uuid)
418{
419 LLFloaterIMPanel* floater = findFloaterBySession(uuid);
420 if(floater) return TRUE;
421 return FALSE;
422}
423
424// This adds a session to the talk view. The name is the local name of
425// the session, dialog specifies the type of session. If the session
426// exists, it is brought forward. Specifying id = NULL results in an
427// im session to everyone. Returns the uuid of the session.
428LLUUID LLIMView::addSession(const std::string& name,
429 EInstantMessage dialog,
430 const LLUUID& other_participant_id)
431{
432 LLUUID session_id = compute_session_id(dialog, other_participant_id);
433 LLFloaterIMPanel* floater = findFloaterBySession(session_id);
434 if(!floater)
435 {
436 floater = createFloater(session_id, other_participant_id, name, dialog, TRUE);
437 LLDynamicArray<LLUUID> ids;
438 ids.put(other_participant_id);
439 noteOfflineUsers(floater, ids);
440 floater->addParticipants(ids);
441 mTalkFloater->showFloater(floater);
442 }
443 else
444 {
445 floater->open();
446 }
447 //mTabContainer->selectTabPanel(panel);
448 floater->setInputFocus(TRUE);
449 return floater->getSessionID();
450}
451
452// Adds a session using the given session_id. If the session already exists
453// the dialog type is assumed correct. Returns the uuid of the session.
454LLUUID LLIMView::addSession(const std::string& name,
455 EInstantMessage dialog,
456 const LLUUID& session_id,
457 const LLDynamicArray<LLUUID>& ids)
458{
459 if (0 == ids.getLength())
460 {
461 return LLUUID::null;
462 }
463
464 LLUUID other_participant_id = ids[0];
465 LLUUID new_session_id = session_id;
466 if (new_session_id.isNull())
467 {
468 new_session_id = compute_session_id(dialog, other_participant_id);
469 }
470
471 LLFloaterIMPanel* floater = findFloaterBySession(new_session_id);
472 if(!floater)
473 {
474 // On creation, use the first element of ids as the "other_participant_id"
475 floater = createFloater(new_session_id, other_participant_id, name, dialog, TRUE);
476 noteOfflineUsers(floater, ids);
477 }
478 floater->addParticipants(ids);
479 mTalkFloater->showFloater(floater);
480 //mTabContainer->selectTabPanel(panel);
481 floater->setInputFocus(TRUE);
482 return floater->getSessionID();
483}
484
485// This removes the panel referenced by the uuid, and then restores
486// internal consistency. The internal pointer is not deleted.
487void LLIMView::removeSession(const LLUUID& session_id)
488{
489 LLFloaterIMPanel* floater = findFloaterBySession(session_id);
490 if(floater)
491 {
492 mFloaters.erase(floater->getHandle());
493 mTalkFloater->removeFloater(floater);
494 //mTabContainer->removeTabPanel(floater);
495 }
496}
497
498void LLIMView::refresh()
499{
500 S32 old_scroll_pos = mNewIMFloater->getScrollPos();
501 mNewIMFloater->clearAllTargets();
502
503 // build a list of groups.
504 LLLinkedList<LLGroupData> group_list( group_dictionary_sort );
505
506 LLGroupData* group;
507 S32 count = gAgent.mGroups.count();
508 S32 i;
509 // read/sort groups on the first pass.
510 for(i = 0; i < count; ++i)
511 {
512 group = &(gAgent.mGroups.get(i));
513 group_list.addDataSorted( group );
514 }
515
516 // add groups to the floater on the second pass.
517 for(group = group_list.getFirstData();
518 group;
519 group = group_list.getNextData())
520 {
521 mNewIMFloater->addTarget(group->mID, group->mName,
522 (void*)(&GROUP_DIALOG), TRUE, FALSE);
523 }
524
525 // build a set of buddies in the current buddy list.
526 LLCollectAllBuddies collector;
527 LLAvatarTracker::instance().applyFunctor(collector);
528 LLCollectAllBuddies::buddy_map_t::iterator it;
529 LLCollectAllBuddies::buddy_map_t::iterator end;
530 it = collector.mOnline.begin();
531 end = collector.mOnline.end();
532 for( ; it != end; ++it)
533 {
534 mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), TRUE);
535 }
536 it = collector.mOffline.begin();
537 end = collector.mOffline.end();
538 for( ; it != end; ++it)
539 {
540 mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), FALSE);
541 }
542
543 if(gAgent.isGodlike())
544 {
545 // XUI:translate
546 mNewIMFloater->addTarget(LLUUID::null, "All Residents, All Grids",
547 (void*)(&EVERYONE_DIALOG), TRUE, FALSE);
548 }
549
550 mNewIMFloater->setScrollPos( old_scroll_pos );
551}
552
553// JC - This used to set console visibility. It doesn't any more.
554void LLIMView::setFloaterOpen(BOOL set_open)
555{
556 gSavedSettings.setBOOL("ShowIM", set_open);
557
558 //RN "visible" and "open" are considered synonomous for now
559 if (set_open)
560 {
561 mTalkFloater->open();
562 }
563 else
564 {
565 mTalkFloater->close();
566 }
567
568 if( set_open )
569 {
570 // notifyNewIM();
571
572 // We're showing the IM, so mark view as non-pending
573 mIMReceived = FALSE;
574 }
575}
576
577
578BOOL LLIMView::getFloaterOpen()
579{
580 return mTalkFloater->getVisible();
581}
582
583void LLIMView::pruneSessions()
584{
585 if(mNewIMFloater)
586 {
587 BOOL removed = TRUE;
588 LLFloaterIMPanel* floater = NULL;
589 while(removed)
590 {
591 removed = FALSE;
592 std::set<LLViewHandle>::iterator handle_it;
593 for(handle_it = mFloaters.begin();
594 handle_it != mFloaters.end();
595 ++handle_it)
596 {
597 floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
598 if(floater && !mNewIMFloater->isUUIDAvailable(floater->getOtherParticipantID()))
599 {
600 // remove this floater
601 removed = TRUE;
602 mFloaters.erase(handle_it++);
603 floater->close();
604 break;
605 }
606 }
607 }
608 }
609}
610
611
612void LLIMView::disconnectAllSessions()
613{
614 if(mNewIMFloater)
615 {
616 mNewIMFloater->setEnabled(FALSE);
617 }
618 LLFloaterIMPanel* floater = NULL;
619 std::set<LLViewHandle>::iterator handle_it;
620 for(handle_it = mFloaters.begin();
621 handle_it != mFloaters.end();
622 ++handle_it)
623 {
624 floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
625 if (floater)
626 {
627 floater->setEnabled(FALSE);
628 }
629 }
630}
631
632
633// This method returns the im panel corresponding to the uuid
634// provided. The uuid can either be a session id or an agent
635// id. Returns NULL if there is no matching panel.
636LLFloaterIMPanel* LLIMView::findFloaterBySession(const LLUUID& session_id)
637{
638 LLFloaterIMPanel* rv = NULL;
639 std::set<LLViewHandle>::iterator handle_it;
640 for(handle_it = mFloaters.begin();
641 handle_it != mFloaters.end();
642 ++handle_it)
643 {
644 rv = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
645 if(rv && session_id == rv->getSessionID())
646 {
647 break;
648 }
649 rv = NULL;
650 }
651 return rv;
652}
653
654
655BOOL LLIMView::hasSession(const LLUUID& session_id)
656{
657 return (findFloaterBySession(session_id) != NULL);
658}
659
660
661// create a floater and update internal representation for
662// consistency. Returns the pointer, caller (the class instance since
663// it is a private method) is not responsible for deleting the
664// pointer. Add the floater to this but do not select it.
665LLFloaterIMPanel* LLIMView::createFloater(const LLUUID& session_id,
666 const LLUUID& other_participant_id,
667 const std::string& session_label,
668 EInstantMessage dialog,
669 BOOL user_initiated)
670{
671 if (session_id.isNull())
672 {
673 llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
674 }
675 llinfos << "LLIMView::createFloater: from " << other_participant_id
676 << " in session " << session_id << llendl;
677 LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
678 LLRect(),
679 session_label,
680 session_id,
681 other_participant_id,
682 dialog);
683 LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
684 mTalkFloater->addFloater(floater, FALSE, i_pt);
685 mFloaters.insert(floater->getHandle());
686 return floater;
687}
688
689void LLIMView::noteOfflineUsers(LLFloaterIMPanel* floater,
690 const LLDynamicArray<LLUUID>& ids)
691{
692 S32 count = ids.count();
693 if(count == 0)
694 {
695 floater->addHistoryLine(sOnlyUserMessage);
696 }
697 else
698 {
699 const LLRelationship* info = NULL;
700 LLAvatarTracker& at = LLAvatarTracker::instance();
701 for(S32 i = 0; i < count; ++i)
702 {
703 info = at.getBuddyInfo(ids.get(i));
704 char first[DB_FIRST_NAME_BUF_SIZE];
705 char last[DB_LAST_NAME_BUF_SIZE];
706 if(info && !info->isOnline()
707 && gCacheName->getName(ids.get(i), first, last))
708 {
709 LLUIString offline = sOfflineMessage;
710 offline.setArg("[FIRST]", first);
711 offline.setArg("[LAST]", last);
712 floater->addHistoryLine(offline);
713 }
714 }
715 }
716}
717
718void LLIMView::processIMTypingStart(const LLIMInfo* im_info)
719{
720 processIMTypingCore(im_info, TRUE);
721}
722
723void LLIMView::processIMTypingStop(const LLIMInfo* im_info)
724{
725 processIMTypingCore(im_info, FALSE);
726}
727
728void LLIMView::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
729{
730 LLUUID session_id = compute_session_id(im_info->mIMType, im_info->mFromID);
731 LLFloaterIMPanel* floater = findFloaterBySession(session_id);
732 if (floater)
733 {
734 floater->processIMTyping(im_info, typing);
735 }
736}