PageRenderTime 181ms CodeModel.GetById 60ms app.highlight 61ms RepoModel.GetById 40ms app.codeStats 0ms

/indra/newview/llfloaterchat.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 485 lines | 340 code | 59 blank | 86 comment | 58 complexity | 26249f8437f008c9d903febc8df64343 MD5 | raw file
  1/** 
  2 * @file llfloaterchat.cpp
  3 * @brief LLFloaterChat class implementation
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27/**
 28 * Actually the "Chat History" floater.
 29 * Should be llfloaterchathistory, not llfloaterchat.
 30 */
 31
 32#include "llviewerprecompiledheaders.h"
 33
 34// project include
 35#include "llagent.h"
 36#include "llappviewer.h"
 37#include "llbutton.h"
 38#include "llcheckboxctrl.h"
 39#include "llcombobox.h"
 40#include "llconsole.h"
 41#include "llfloateractivespeakers.h"
 42#include "llfloaterchatterbox.h"
 43#include "llfloaterreg.h"
 44#include "llfloaterscriptdebug.h"
 45#include "llkeyboard.h"
 46//#include "lllineeditor.h"
 47#include "llmutelist.h"
 48//#include "llresizehandle.h"
 49#include "llchatbar.h"
 50#include "llrecentpeople.h"
 51#include "llpanelblockedlist.h"
 52#include "llslurl.h"
 53#include "llstatusbar.h"
 54#include "llviewertexteditor.h"
 55#include "llviewergesture.h"			// for triggering gestures
 56#include "llviewermessage.h"
 57#include "llviewerwindow.h"
 58#include "llviewercontrol.h"
 59#include "lluictrlfactory.h"
 60#include "lllogchat.h"
 61#include "lltexteditor.h"
 62#include "lltextparser.h"
 63#include "llweb.h"
 64#include "llstylemap.h"
 65
 66// linden library includes
 67#include "llaudioengine.h"
 68#include "llchat.h"
 69#include "llfontgl.h"
 70#include "llrect.h"
 71#include "llerror.h"
 72#include "llstring.h"
 73#include "llwindow.h"
 74#include "message.h"
 75
 76//
 77// Constants
 78//
 79const F32 INSTANT_MSG_SIZE = 8.0f;
 80const F32 CHAT_MSG_SIZE = 8.0f;
 81
 82
 83//
 84// Global statics
 85//
 86LLColor4 get_text_color(const LLChat& chat);
 87
 88//
 89// Member Functions
 90//
 91LLFloaterChat::LLFloaterChat(const LLSD& seed)
 92	: LLFloater(seed),
 93	  mPanel(NULL)
 94{
 95	mFactoryMap["chat_panel"] = LLCallbackMap(createChatPanel, NULL);
 96	mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, NULL);
 97	//Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this,"floater_chat_history.xml");
 98
 99}
100
101LLFloaterChat::~LLFloaterChat()
102{
103	// Children all cleaned up by default view destructor.
104}
105
106void LLFloaterChat::draw()
107{
108	// enable say and shout only when text available
109		
110	childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel"));
111
112	LLChatBar* chat_barp = findChild<LLChatBar>("chat_panel", TRUE);
113	if (chat_barp)
114	{
115		chat_barp->refresh();
116	}
117
118	mPanel->refreshSpeakers();
119	LLFloater::draw();
120}
121
122BOOL LLFloaterChat::postBuild()
123{
124	// Hide the chat overlay when our history is visible.
125	setVisibleCallback(boost::bind(&LLFloaterChat::updateConsoleVisibility, this));
126	
127	mPanel = (LLPanelActiveSpeakers*)getChild<LLPanel>("active_speakers_panel");
128
129	childSetCommitCallback("show mutes",onClickToggleShowMute,this); //show mutes
130	childSetVisible("Chat History Editor with mute",FALSE);
131	childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this);
132
133	return TRUE;
134}
135
136void LLFloaterChat::updateConsoleVisibility()
137{
138	if(gDisconnected)
139	{
140		return;
141	}
142	// determine whether we should show console due to not being visible
143	gConsole->setVisible( !isInVisibleChain()								// are we not in part of UI being drawn?
144							|| isMinimized()								// are we minimized?
145							|| (getHost() && getHost()->isMinimized() ));	// are we hosted in a minimized floater?
146}
147
148void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& color)
149{
150	std::string line = chat.mText;
151	bool prepend_newline = true;
152	if (gSavedSettings.getBOOL("ChatShowTimestamps"))
153	{
154		edit->appendTime(prepend_newline);
155		prepend_newline = false;
156	}
157
158	// If the msg is from an agent (not yourself though),
159	// extract out the sender name and replace it with the hotlinked name.
160	if (chat.mSourceType == CHAT_SOURCE_AGENT &&
161		chat.mFromID != LLUUID::null)
162	{
163		chat.mURL = LLSLURL("agent", chat.mFromID, "inspect").getSLURLString();
164	}
165
166	// If the chat line has an associated url, link it up to the name.
167	if (!chat.mURL.empty()
168		&& (line.length() > chat.mFromName.length() && line.find(chat.mFromName,0) == 0))
169	{
170		std::string start_line = line.substr(0, chat.mFromName.length() + 1);
171		line = line.substr(chat.mFromName.length() + 1);
172		edit->appendText(start_line, prepend_newline, LLStyleMap::instance().lookup(chat.mFromID,chat.mURL));
173		edit->blockUndo();
174		prepend_newline = false;
175	}
176	edit->appendText(line, prepend_newline, LLStyle::Params().color(color));
177	edit->blockUndo();
178}
179
180// static
181void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
182{	
183	if (log_to_file && (gSavedPerAccountSettings.getBOOL("LogChat"))) 
184	{
185		if (chat.mChatType != CHAT_TYPE_WHISPER && chat.mChatType != CHAT_TYPE_SHOUT)
186		{
187			LLLogChat::saveHistory("chat", chat.mFromName, chat.mFromID, chat.mText);
188		}
189		else
190		{
191			LLLogChat::saveHistory("chat", "", chat.mFromID, chat.mFromName + " " + chat.mText);
192		}
193	}
194	
195	LLColor4 color = get_text_color(chat);
196	
197	if (!log_to_file) color = LLColor4::grey;	//Recap from log file.
198
199	if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
200	{
201		if(gSavedSettings.getBOOL("ShowScriptErrors") == FALSE)
202			return;
203		if (gSavedSettings.getS32("ShowScriptErrorsLocation") == 1)
204		{
205			LLFloaterScriptDebug::addScriptLine(chat.mText,
206												chat.mFromName, 
207												color, 
208												chat.mFromID);
209			return;
210		}
211	}
212	
213	// could flash the chat button in the status bar here. JC
214	LLFloaterChat* chat_floater = LLFloaterChat::getInstance();
215	LLViewerTextEditor*	history_editor = chat_floater->getChild<LLViewerTextEditor>("Chat History Editor");
216	LLViewerTextEditor*	history_editor_with_mute = chat_floater->getChild<LLViewerTextEditor>("Chat History Editor with mute");
217
218	if (!chat.mMuted)
219	{
220		add_timestamped_line(history_editor, chat, color);
221		add_timestamped_line(history_editor_with_mute, chat, color);
222	}
223	else
224	{
225		// desaturate muted chat
226		LLColor4 muted_color = lerp(color, LLColor4::grey, 0.5f);
227		add_timestamped_line(history_editor_with_mute, chat, color);
228	}
229	
230	// add objects as transient speakers that can be muted
231	if (chat.mSourceType == CHAT_SOURCE_OBJECT)
232	{
233		chat_floater->mPanel->setSpeaker(chat.mFromID, chat.mFromName, LLSpeaker::STATUS_NOT_IN_CHANNEL, LLSpeaker::SPEAKER_OBJECT);
234	}
235
236	// start tab flashing on incoming text from other users (ignoring system text, etc)
237	if (!chat_floater->isInVisibleChain() && chat.mSourceType == CHAT_SOURCE_AGENT)
238	{
239		LLFloaterChatterBox::getInstance()->setFloaterFlashing(chat_floater, TRUE);
240	}
241}
242
243// static
244void LLFloaterChat::setHistoryCursorAndScrollToEnd()
245{
246	LLViewerTextEditor*	history_editor = LLFloaterChat::getInstance()->getChild<LLViewerTextEditor>("Chat History Editor");
247	LLViewerTextEditor*	history_editor_with_mute = LLFloaterChat::getInstance()->getChild<LLViewerTextEditor>("Chat History Editor with mute");
248	
249	if (history_editor) 
250	{
251		history_editor->setCursorAndScrollToEnd();
252	}
253	if (history_editor_with_mute)
254	{
255		 history_editor_with_mute->setCursorAndScrollToEnd();
256	}
257}
258
259
260//static 
261void LLFloaterChat::onClickMute(void *data)
262{
263	LLFloaterChat* self = (LLFloaterChat*)data;
264
265	LLComboBox*	chatter_combo = self->getChild<LLComboBox>("chatter combobox");
266
267	const std::string& name = chatter_combo->getSimple();
268	LLUUID id = chatter_combo->getCurrentID();
269
270	if (name.empty()) return;
271
272	LLMute mute(id);
273	mute.setFromDisplayName(name);
274	LLMuteList::getInstance()->add(mute);
275	LLPanelBlockedList::showPanelAndSelect(mute.mID);
276}
277
278//static
279void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data)
280{
281	LLFloaterChat* floater = (LLFloaterChat*)data;
282
283
284	//LLCheckBoxCtrl*	
285	BOOL show_mute = floater->getChild<LLCheckBoxCtrl>("show mutes")->get();
286	LLViewerTextEditor*	history_editor = floater->getChild<LLViewerTextEditor>("Chat History Editor");
287	LLViewerTextEditor*	history_editor_with_mute = floater->getChild<LLViewerTextEditor>("Chat History Editor with mute");
288
289	if (!history_editor || !history_editor_with_mute)
290		return;
291
292	//BOOL show_mute = floater->mShowMuteCheckBox->get();
293	if (show_mute)
294	{
295		history_editor->setVisible(FALSE);
296		history_editor_with_mute->setVisible(TRUE);
297		history_editor_with_mute->setCursorAndScrollToEnd();
298	}
299	else
300	{
301		history_editor->setVisible(TRUE);
302		history_editor_with_mute->setVisible(FALSE);
303		history_editor->setCursorAndScrollToEnd();
304	}
305}
306
307// Put a line of chat in all the right places
308void LLFloaterChat::addChat(const LLChat& chat, BOOL local_agent)
309{
310	triggerAlerts(chat.mText);
311
312	// Add the sender to the list of people with which we've recently interacted.
313	// this is not the best place to add _all_ messages to recent list
314	// comment this for now, may remove later on code cleanup
315	//if(chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID.notNull())
316	//	LLRecentPeople::instance().add(chat.mFromID);
317	
318	addChatHistory(chat, true);
319}
320
321// Moved from lltextparser.cpp to break llui/llaudio library dependency.
322//static
323void LLFloaterChat::triggerAlerts(const std::string& text)
324{
325	LLTextParser* parser = LLTextParser::getInstance();
326//    bool spoken=FALSE;
327	for (S32 i=0;i<parser->mHighlights.size();i++)
328	{
329		LLSD& highlight = parser->mHighlights[i];
330		if (parser->findPattern(text,highlight) >= 0 )
331		{
332			if(gAudiop)
333			{
334				if ((std::string)highlight["sound_lluuid"] != LLUUID::null.asString())
335				{
336					gAudiop->triggerSound(highlight["sound_lluuid"].asUUID(), 
337						gAgent.getID(),
338						1.f,
339						LLAudioEngine::AUDIO_TYPE_UI,
340						gAgent.getPositionGlobal() );
341				}
342/*				
343				if (!spoken) 
344				{
345					LLTextToSpeech* text_to_speech = NULL;
346					text_to_speech = LLTextToSpeech::getInstance();
347					spoken = text_to_speech->speak((LLString)highlight["voice"],text); 
348				}
349 */
350			}
351			if (highlight["flash"])
352			{
353				LLWindow* viewer_window = gViewerWindow->getWindow();
354				if (viewer_window && viewer_window->getMinimized())
355				{
356					viewer_window->flashIcon(5.f);
357				}
358			}
359		}
360	}
361}
362
363LLColor4 get_text_color(const LLChat& chat)
364{
365	LLColor4 text_color;
366
367	if(chat.mMuted)
368	{
369		text_color.setVec(0.8f, 0.8f, 0.8f, 1.f);
370	}
371	else
372	{
373		switch(chat.mSourceType)
374		{
375		case CHAT_SOURCE_SYSTEM:
376			text_color = LLUIColorTable::instance().getColor("SystemChatColor");
377			break;
378		case CHAT_SOURCE_AGENT:
379		    if (chat.mFromID.isNull())
380			{
381				text_color = LLUIColorTable::instance().getColor("SystemChatColor");
382			}
383			else
384			{
385				if(gAgent.getID() == chat.mFromID)
386				{
387					text_color = LLUIColorTable::instance().getColor("UserChatColor");
388				}
389				else
390				{
391					text_color = LLUIColorTable::instance().getColor("AgentChatColor");
392				}
393			}
394			break;
395		case CHAT_SOURCE_OBJECT:
396			if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
397			{
398				text_color = LLUIColorTable::instance().getColor("ScriptErrorColor");
399			}
400			else if ( chat.mChatType == CHAT_TYPE_OWNER )
401			{
402				text_color = LLUIColorTable::instance().getColor("llOwnerSayChatColor");
403			}
404			else
405			{
406				text_color = LLUIColorTable::instance().getColor("ObjectChatColor");
407			}
408			break;
409		default:
410			text_color.setToWhite();
411		}
412
413		if (!chat.mPosAgent.isExactlyZero())
414		{
415			LLVector3 pos_agent = gAgent.getPositionAgent();
416			F32 distance_squared = dist_vec_squared(pos_agent, chat.mPosAgent);
417			F32 dist_near_chat = gAgent.getNearChatRadius();
418			if (distance_squared > dist_near_chat * dist_near_chat)
419			{
420				// diminish far-off chat
421				text_color.mV[VALPHA] = 0.8f;
422			}
423		}
424	}
425
426	return text_color;
427}
428
429//static
430void LLFloaterChat::loadHistory()
431{
432	LLLogChat::loadHistory(std::string("chat"), &chatFromLogFile, (void *)LLFloaterChat::getInstance()); 
433}
434
435//static
436void LLFloaterChat::chatFromLogFile(LLLogChat::ELogLineType type , const LLSD& line, void* userdata)
437{
438	switch (type)
439	{
440	case LLLogChat::LOG_EMPTY:
441	case LLLogChat::LOG_END:
442		// *TODO: nice message from XML file here
443		break;
444	case LLLogChat::LOG_LINE:
445	case LLLogChat::LOG_LLSD:
446		{
447			LLChat chat;					
448			chat.mText = line["message"].asString();
449			get_text_color(chat);
450			addChatHistory(chat,  FALSE);
451		}
452		break;
453	default:
454		// nothing
455		break;
456	}
457}
458
459//static
460void* LLFloaterChat::createSpeakersPanel(void* data)
461{
462	return new LLPanelActiveSpeakers(LLLocalSpeakerMgr::getInstance(), TRUE);
463}
464
465//static
466void* LLFloaterChat::createChatPanel(void* data)
467{
468	LLChatBar* chatp = new LLChatBar();
469	return chatp;
470}
471
472// static
473void LLFloaterChat::onClickToggleActiveSpeakers(void* userdata)
474{
475	LLFloaterChat* self = (LLFloaterChat*)userdata;
476
477	self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel"));
478}
479
480//static
481 LLFloaterChat* LLFloaterChat::getInstance()
482 {
483	 return LLFloaterReg::getTypedInstance<LLFloaterChat>("chat", LLSD()) ;
484	 
485 }