diff options
70 files changed, 2923 insertions, 2947 deletions
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 75905d0927..c2f91ff7e0 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -84,8 +84,8 @@ void LLLineEditor::PrevalidateNamedFuncs::declareValues() declare("non_negative_s32", LLLineEditor::prevalidateNonNegativeS32); declare("alpha_num", LLLineEditor::prevalidateAlphaNum); declare("alpha_num_space", LLLineEditor::prevalidateAlphaNumSpace); - declare("printable_not_pipe", LLLineEditor::prevalidatePrintableNotPipe); - declare("printable_no_space", LLLineEditor::prevalidatePrintableNoSpace); + declare("ascii_printable_no_pipe", LLLineEditor::prevalidateASCIIPrintableNoPipe); + declare("ascii_printable_no_space", LLLineEditor::prevalidateASCIIPrintableNoSpace); } LLLineEditor::Params::Params() @@ -2186,20 +2186,28 @@ BOOL LLLineEditor::prevalidateAlphaNumSpace(const LLWString &str) return rv; } +// Used for most names of things stored on the server, due to old file-formats +// that used the pipe (|) for multiline text storage. Examples include +// inventory item names, parcel names, object names, etc. // static -BOOL LLLineEditor::prevalidatePrintableNotPipe(const LLWString &str) +BOOL LLLineEditor::prevalidateASCIIPrintableNoPipe(const LLWString &str) { BOOL rv = TRUE; S32 len = str.length(); if(len == 0) return rv; while(len--) { - if('|' == str[len]) + llwchar wc = str[len]; + if (wc < 0x20 + || wc > 0x7f + || wc == '|') { rv = FALSE; break; } - if(!((' ' == str[len]) || LLStringOps::isAlnum((char)str[len]) || LLStringOps::isPunct((char)str[len]))) + if(!(wc == ' ' + || LLStringOps::isAlnum((char)wc) + || LLStringOps::isPunct((char)wc) ) ) { rv = FALSE; break; @@ -2209,15 +2217,19 @@ BOOL LLLineEditor::prevalidatePrintableNotPipe(const LLWString &str) } +// Used for avatar names // static -BOOL LLLineEditor::prevalidatePrintableNoSpace(const LLWString &str) +BOOL LLLineEditor::prevalidateASCIIPrintableNoSpace(const LLWString &str) { BOOL rv = TRUE; S32 len = str.length(); if(len == 0) return rv; while(len--) { - if(LLStringOps::isSpace(str[len])) + llwchar wc = str[len]; + if (wc < 0x20 + || wc > 0x7f + || LLStringOps::isSpace(wc)) { rv = FALSE; break; @@ -2232,6 +2244,7 @@ BOOL LLLineEditor::prevalidatePrintableNoSpace(const LLWString &str) return rv; } + // static BOOL LLLineEditor::prevalidateASCII(const LLWString &str) { diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index d3daa941cf..4474963b1a 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -235,8 +235,8 @@ public: static BOOL prevalidateNonNegativeS32(const LLWString &str); static BOOL prevalidateAlphaNum(const LLWString &str ); static BOOL prevalidateAlphaNumSpace(const LLWString &str ); - static BOOL prevalidatePrintableNotPipe(const LLWString &str); - static BOOL prevalidatePrintableNoSpace(const LLWString &str); + static BOOL prevalidateASCIIPrintableNoPipe(const LLWString &str); + static BOOL prevalidateASCIIPrintableNoSpace(const LLWString &str); static BOOL prevalidateASCII(const LLWString &str); static BOOL postvalidateFloat(const std::string &str); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index caaf47240f..cd795282f9 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -2380,6 +2380,14 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); // if last character is a newline, then return true, forcing line break llwchar last_char = text[mStart + first_char + num_chars - 1]; + + LLUIImagePtr image = mStyle->getImage(); + if( image.notNull()) + { + width += image->getWidth(); + height = llmax(height, image->getHeight()); + } + return num_chars >= 1 && last_char == '\n'; } diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 94a2ca16f4..74ae8db0c7 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9645,116 +9645,6 @@ <key>Value</key> <string>00000000-0000-0000-0000-000000000000</string> </map> - <key>UISndPieMenuAppear</key> - <map> - <key>Comment</key> - <string>Sound file for opening pie menu (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>8eaed61f-92ff-6485-de83-4dcc938a478e</string> - </map> - <key>UISndPieMenuHide</key> - <map> - <key>Comment</key> - <string>Sound file for closing pie menu (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>00000000-0000-0000-0000-000000000000</string> - </map> - <key>UISndPieMenuSliceHighlight0</key> - <map> - <key>Comment</key> - <string>Sound file for selecting pie menu item 0 (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>d9f73cf8-17b4-6f7a-1565-7951226c305d</string> - </map> - <key>UISndPieMenuSliceHighlight1</key> - <map> - <key>Comment</key> - <string>Sound file for selecting pie menu item 1 (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>f6ba9816-dcaf-f755-7b67-51b31b6233e5</string> - </map> - <key>UISndPieMenuSliceHighlight2</key> - <map> - <key>Comment</key> - <string>Sound file for selecting pie menu item 2 (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>7aff2265-d05b-8b72-63c7-dbf96dc2f21f</string> - </map> - <key>UISndPieMenuSliceHighlight3</key> - <map> - <key>Comment</key> - <string>Sound file for selecting pie menu item 3 (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>09b2184e-8601-44e2-afbb-ce37434b8ba1</string> - </map> - <key>UISndPieMenuSliceHighlight4</key> - <map> - <key>Comment</key> - <string>Sound file for selecting pie menu item 4 (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>bbe4c7fc-7044-b05e-7b89-36924a67593c</string> - </map> - <key>UISndPieMenuSliceHighlight5</key> - <map> - <key>Comment</key> - <string>Sound file for selecting pie menu item 5 (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>d166039b-b4f5-c2ec-4911-c85c727b016c</string> - </map> - <key>UISndPieMenuSliceHighlight6</key> - <map> - <key>Comment</key> - <string>Sound file for selecting pie menu item 6 (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>242af82b-43c2-9a3b-e108-3b0c7e384981</string> - </map> - <key>UISndPieMenuSliceHighlight7</key> - <map> - <key>Comment</key> - <string>Sound file for selecting pie menu item 7 (uuid for sound asset)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>c1f334fb-a5be-8fe7-22b3-29631c21cf0b</string> - </map> <key>UISndSnapshot</key> <map> <key>Comment</key> diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index aa584b3ae8..94c303a30f 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -126,7 +126,7 @@ void LLChannelManager::onLoginCompleted() gViewerWindow->getRootView()->addChild(mStartUpChannel); // init channel's position and size - S32 channel_right_bound = gViewerWindow->getWorldViewRectRaw().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound); mStartUpChannel->setMouseDownCallback(boost::bind(&LLSysWellWindow::onStartUpToastClick, LLFloaterReg::getTypedInstance<LLSysWellWindow>("syswell_window"), _2, _3, _4)); diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index 886f5ec924..cd3432190b 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -415,7 +415,7 @@ LLPanelRegionTools::LLPanelRegionTools() BOOL LLPanelRegionTools::postBuild() { getChild<LLLineEditor>("region name")->setKeystrokeCallback(onChangeSimName, this); - childSetPrevalidate("region name", &LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("region name", &LLLineEditor::prevalidateASCIIPrintableNoPipe); childSetPrevalidate("estate", &LLLineEditor::prevalidatePositiveS32); childSetPrevalidate("parentestate", &LLLineEditor::prevalidatePositiveS32); childDisable("parentestate"); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 015a947d91..22d6098d5b 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -347,13 +347,14 @@ BOOL LLPanelLandGeneral::postBuild() { mEditName = getChild<LLLineEditor>("Name"); mEditName->setCommitCallback(onCommitAny, this); - childSetPrevalidate("Name", LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("Name", LLLineEditor::prevalidateASCIIPrintableNoPipe); mEditDesc = getChild<LLTextEditor>("Description"); mEditDesc->setCommitOnFocusLost(TRUE); mEditDesc->setCommitCallback(onCommitAny, this); - childSetPrevalidate("Description", LLLineEditor::prevalidatePrintableNotPipe); - + // No prevalidate function - historically the prevalidate function was broken, + // allowing residents to put in characters like U+2661 WHITE HEART SUIT, so + // preserve that ability. mTextSalePending = getChild<LLTextBox>("SalePending"); mTextOwnerLabel = getChild<LLTextBox>("Owner:"); diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp index 3fe711a166..d18f127f85 100644 --- a/indra/newview/llfloatermap.cpp +++ b/indra/newview/llfloatermap.cpp @@ -83,7 +83,6 @@ BOOL LLFloaterMap::postBuild() { mMap = getChild<LLNetMap>("Net Map"); mMap->setScale(gSavedSettings.getF32("MiniMapScale")); - mMap->setRotateMap(gSavedSettings.getBOOL( "MiniMapRotate" )); mMap->setToolTipMsg(getString("ToolTipMsg")); sendChildToBack(mMap); @@ -178,7 +177,8 @@ void LLFloaterMap::draw() { F32 rotation = 0; - if( mMap->getRotateMap() ) + static LLUICachedControl<bool> rotate_map("MiniMapRotate", true); + if( rotate_map ) { // rotate subsequent draws to agent rotation rotation = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp index b7296518d4..810761e034 100644 --- a/indra/newview/llfloaternamedesc.cpp +++ b/indra/newview/llfloaternamedesc.cpp @@ -111,7 +111,7 @@ BOOL LLFloaterNameDesc::postBuild() if (NameEditor) { NameEditor->setMaxTextLength(DB_INV_ITEM_NAME_STR_LEN); - NameEditor->setPrevalidate(&LLLineEditor::prevalidatePrintableNotPipe); + NameEditor->setPrevalidate(&LLLineEditor::prevalidateASCIIPrintableNoPipe); } y -= llfloor(PREVIEW_LINE_HEIGHT * 1.2f); @@ -123,7 +123,7 @@ BOOL LLFloaterNameDesc::postBuild() if (DescEditor) { DescEditor->setMaxTextLength(DB_INV_ITEM_DESC_STR_LEN); - DescEditor->setPrevalidate(&LLLineEditor::prevalidatePrintableNotPipe); + DescEditor->setPrevalidate(&LLLineEditor::prevalidateASCIIPrintableNoPipe); } y -= llfloor(PREVIEW_LINE_HEIGHT * 1.2f); diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp index e0d4a59d9d..ff9002787c 100644 --- a/indra/newview/llfloaterproperties.cpp +++ b/indra/newview/llfloaterproperties.cpp @@ -130,9 +130,9 @@ BOOL LLFloaterProperties::postBuild() { // build the UI // item name & description - childSetPrevalidate("LabelItemName",&LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("LabelItemName",&LLLineEditor::prevalidateASCIIPrintableNoPipe); getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitName,this)); - childSetPrevalidate("LabelItemDesc",&LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("LabelItemDesc",&LLLineEditor::prevalidateASCIIPrintableNoPipe); getChild<LLUICtrl>("LabelItemDesc")->setCommitCallback(boost::bind(&LLFloaterProperties:: onCommitDescription, this)); // Creator information getChild<LLUICtrl>("BtnCreator")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickCreator,this)); diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 4192c6a586..955bc64e05 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -220,7 +220,7 @@ LLFolderView::LLFolderView(const Params& p) params.font(getLabelFontForStyle(LLFontGL::NORMAL)); params.max_length_bytes(DB_INV_ITEM_NAME_STR_LEN); params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2)); - params.prevalidate_callback(&LLLineEditor::prevalidatePrintableNotPipe); + params.prevalidate_callback(&LLLineEditor::prevalidateASCIIPrintableNoPipe); params.commit_on_focus_lost(true); params.visible(false); mRenamer = LLUICtrlFactory::create<LLLineEditor> (params); diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 1f80543791..dbbf98ad08 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -358,7 +358,7 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) void LLIMFloater::getAllowedRect(LLRect& rect) { - rect = gViewerWindow->getWorldViewRectRaw(); + rect = gViewerWindow->getWorldViewRectScaled(); } void LLIMFloater::setDocked(bool docked, bool pop_on_undock) diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp index 524a889f97..c081af5879 100644 --- a/indra/newview/llimhandler.cpp +++ b/indra/newview/llimhandler.cpp @@ -58,7 +58,7 @@ LLIMHandler::~LLIMHandler() //-------------------------------------------------------------------------- void LLIMHandler::initChannel() { - S32 channel_right_bound = gViewerWindow->getWorldViewRectRaw().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mChannel->init(channel_right_bound - channel_width, channel_right_bound); } diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index dd78bb631f..ad80e7ed61 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -1,2556 +1,2556 @@ -/** - * @file LLIMMgr.cpp - * @brief Container for Instant Messaging - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llimview.h" - -#include "llfloaterreg.h" -#include "llfontgl.h" -#include "llrect.h" -#include "llerror.h" -#include "llbutton.h" -#include "llhttpclient.h" -#include "llsdutil_math.h" -#include "llstring.h" -#include "lluictrlfactory.h" - -#include "llagent.h" -#include "llavatariconctrl.h" -#include "llbottomtray.h" -#include "llcallingcard.h" -#include "llchat.h" +/**
+ * @file LLIMMgr.cpp
+ * @brief Container for Instant Messaging
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llimview.h"
+
+#include "llfloaterreg.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llbutton.h"
+#include "llhttpclient.h"
+#include "llsdutil_math.h"
+#include "llstring.h"
+#include "lluictrlfactory.h"
+
+#include "llagent.h"
+#include "llavatariconctrl.h"
+#include "llbottomtray.h"
+#include "llcallingcard.h"
+#include "llchat.h"
#include "llchiclet.h"
-#include "llresmgr.h" -#include "llfloaterchat.h" -#include "llfloaterchatterbox.h" -#include "llavataractions.h" -#include "llhttpnode.h" -#include "llimfloater.h" -#include "llimpanel.h" -#include "llresizebar.h" -#include "lltabcontainer.h" -#include "llviewercontrol.h" -#include "llfloater.h" -#include "llmutelist.h" -#include "llresizehandle.h" -#include "llkeyboard.h" -#include "llui.h" -#include "llviewermenu.h" -#include "llcallingcard.h" -#include "lltoolbar.h" -#include "llviewermessage.h" -#include "llviewerwindow.h" -#include "llnotify.h" -#include "llviewerregion.h" -#include "llvoicechannel.h" -#include "lltrans.h" -#include "llrecentpeople.h" -#include "llsyswellwindow.h" - -#include "llfirstuse.h" -#include "llagentui.h" - -// -// Globals -// -LLIMMgr* gIMMgr = NULL; - -// -// Statics -// -// *FIXME: make these all either UIStrings or Strings - -const static std::string IM_SEPARATOR(": "); - - -void toast_callback(const LLSD& msg){ - // do not show toast in busy mode or it goes from agent - if (gAgent.getBusy() || gAgent.getID() == msg["from_id"]) - { - return; - } - - // check whether incoming IM belongs to an active session or not - if (LLIMModel::getInstance()->getActiveSessionID() == msg["session_id"]) - { - return; - } - - // Skip toasting for system messages - if (msg["from_id"].asUUID() == LLUUID::null) - { - return; - } - - LLSD args; - args["MESSAGE"] = msg["message"]; - args["TIME"] = msg["time"]; - args["FROM"] = msg["from"]; - args["FROM_ID"] = msg["from_id"]; - args["SESSION_ID"] = msg["session_id"]; - - LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); -} - -void LLIMModel::setActiveSessionID(const LLUUID& session_id) -{ - // check if such an ID really exists - if (!findIMSession(session_id)) - { - llwarns << "Trying to set as active a non-existent session!" << llendl; - return; - } - - mActiveSessionID = session_id; -} - -LLIMModel::LLIMModel() -{ - addNewMsgCallback(LLIMFloater::newIMCallback); - addNewMsgCallback(toast_callback); -} - -LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids) -: mSessionID(session_id), - mName(name), - mType(type), - mNumUnread(0), - mOtherParticipantID(other_participant_id), - mInitialTargetIDs(ids), - mVoiceChannel(NULL), - mSpeakers(NULL), - mSessionInitialized(false), - mCallBackEnabled(true), - mTextIMPossible(true), - mOtherParticipantIsAvatar(true) -{ - if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type) - { - mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id); - } - else - { - mVoiceChannel = new LLVoiceChannelGroup(session_id, name); - } - mSpeakers = new LLIMSpeakerMgr(mVoiceChannel); - - // All participants will be added to the list of people we've recently interacted with. - mSpeakers->addListener(&LLRecentPeople::instance(), "add"); - - //we need to wait for session initialization for outgoing ad-hoc and group chat session - //correct session id for initiated ad-hoc chat will be received from the server - if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID, - mInitialTargetIDs, mType)) - { - //we don't need to wait for any responses - //so we're already initialized - mSessionInitialized = true; - } - - if (IM_NOTHING_SPECIAL == type) - { - mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID); - mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID); - mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID); - } - - if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) - LLLogChat::loadHistory(mName, &chatFromLogFile, (void *)this); -} - -LLIMModel::LLIMSession::~LLIMSession() -{ - delete mSpeakers; - mSpeakers = NULL; - - // End the text IM session if necessary - if(gVoiceClient && mOtherParticipantID.notNull()) - { - switch(mType) - { - case IM_NOTHING_SPECIAL: - case IM_SESSION_P2P_INVITE: - gVoiceClient->endUserIMSession(mOtherParticipantID); - break; - - default: - // Appease the linux compiler - break; - } - } - - // HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point). - mVoiceChannel->deactivate(); - - delete mVoiceChannel; - mVoiceChannel = NULL; -} - -void LLIMModel::LLIMSession::sessionInitReplyReceived(const LLUUID& new_session_id) -{ - mSessionInitialized = true; - - if (new_session_id != mSessionID) - { - mSessionID = new_session_id; - mVoiceChannel->updateSessionID(new_session_id); - } -} - -void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time) -{ - LLSD message; - message["from"] = from; - message["from_id"] = from_id; - message["message"] = utf8_text; - message["time"] = time; - message["index"] = (LLSD::Integer)mMsgs.size(); - - mMsgs.push_front(message); - - if (mSpeakers && from_id.notNull()) - { - mSpeakers->speakerChatted(from_id); - mSpeakers->setSpeakerTyping(from_id, FALSE); - } -} - -void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata) -{ - if (!userdata) return; - - LLIMSession* self = (LLIMSession*) userdata; - - if (type == LLLogChat::LOG_LINE) - { - self->addMessage("", LLSD(), msg["message"].asString(), ""); - } - else if (type == LLLogChat::LOG_LLSD) - { - self->addMessage(msg["from"].asString(), msg["from_id"].asUUID(), msg["message"].asString(), msg["time"].asString()); - } -} - -LLIMModel::LLIMSession* LLIMModel::findIMSession(const LLUUID& session_id) const -{ - return get_if_there(mId2SessionMap, session_id, - (LLIMModel::LLIMSession*) NULL); -} - -void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, const LLUUID& new_session_id) -{ - LLIMSession* session = findIMSession(old_session_id); - if (session) - { - session->sessionInitReplyReceived(new_session_id); - - if (old_session_id != new_session_id) - { - mId2SessionMap.erase(old_session_id); - mId2SessionMap[new_session_id] = session; - - gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id); - } - - LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id); - if (im_floater) - { - im_floater->sessionInitReplyReceived(new_session_id); - } - } - - //*TODO remove this "floater" stuff when Communicate Floater is gone - LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(old_session_id); - if (floater) - { - floater->sessionInitReplyReceived(new_session_id); - } -} - -void LLIMModel::testMessages() -{ - LLUUID bot1_id("d0426ec6-6535-4c11-a5d9-526bb0c654d9"); - LLUUID bot1_session_id; - std::string from = "IM Tester"; - - bot1_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot1_id); - newSession(bot1_session_id, from, IM_NOTHING_SPECIAL, bot1_id); - addMessage(bot1_session_id, from, bot1_id, "Test Message: Hi from testerbot land!"); - - LLUUID bot2_id; - std::string firstname[] = {"Roflcopter", "Joe"}; - std::string lastname[] = {"Linden", "Tester", "Resident", "Schmoe"}; - - S32 rand1 = ll_rand(sizeof firstname)/(sizeof firstname[0]); - S32 rand2 = ll_rand(sizeof lastname)/(sizeof lastname[0]); - - from = firstname[rand1] + " " + lastname[rand2]; - bot2_id.generate(from); - LLUUID bot2_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot2_id); - newSession(bot2_session_id, from, IM_NOTHING_SPECIAL, bot2_id); - addMessage(bot2_session_id, from, bot2_id, "Test Message: Hello there, I have a question. Can I bother you for a second? "); - addMessage(bot2_session_id, from, bot2_id, "Test Message: OMGWTFBBQ."); -} - - -bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, const std::vector<LLUUID>& ids) -{ - if (findIMSession(session_id)) - { - llwarns << "IM Session " << session_id << " already exists" << llendl; - return false; - } - - LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids); - mId2SessionMap[session_id] = session; - - LLIMMgr::getInstance()->notifyObserverSessionAdded(session_id, name, other_participant_id); - - return true; - -} - -bool LLIMModel::clearSession(const LLUUID& session_id) -{ - if (mId2SessionMap.find(session_id) == mId2SessionMap.end()) return false; - delete (mId2SessionMap[session_id]); - mId2SessionMap.erase(session_id); - return true; -} - -void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) -{ - LLIMSession* session = findIMSession(session_id); - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return; - } - - int i = session->mMsgs.size() - start_index; - - for (std::list<LLSD>::iterator iter = session->mMsgs.begin(); - iter != session->mMsgs.end() && i > 0; - iter++) - { - LLSD msg; - msg = *iter; - messages.push_back(*iter); - i--; - } - - session->mNumUnread = 0; - - LLSD arg; - arg["session_id"] = session_id; - arg["num_unread"] = 0; - mNoUnreadMsgsSignal(arg); -} - -bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { - - LLIMSession* session = findIMSession(session_id); - - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return false; - } - - session->addMessage(from, from_id, utf8_text, LLLogChat::timestamp(false)); //might want to add date separately - - return true; -} - -bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) -{ - S32 im_log_option = gSavedPerAccountSettings.getS32("IMLogOptions"); - if (im_log_option != LOG_CHAT) - { - if(im_log_option == LOG_BOTH_TOGETHER) - { - LLLogChat::saveHistory(std::string("chat"), from, from_id, utf8_text); - return true; - } - else - { - LLLogChat::saveHistory(LLIMModel::getInstance()->getName(session_id), from, from_id, utf8_text); - return true; - } - } - return false; -} - -bool LLIMModel::proccessOnlineOfflineNotification( - const LLUUID& session_id, - const std::string& utf8_text) -{ - // Add message to old one floater - LLFloaterIMPanel *floater = gIMMgr->findFloaterBySession(session_id); - if ( floater ) - { - if ( !utf8_text.empty() ) - { - floater->addHistoryLine(utf8_text, LLUIColorTable::instance().getColor("SystemChatColor")); - } - } - // Add system message to history - return addMessage(session_id, SYSTEM_FROM, LLUUID::null, utf8_text); -} - -bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, - const std::string& utf8_text, bool log2file /* = true */) { - LLIMSession* session = findIMSession(session_id); - - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return false; - } - - addToHistory(session_id, from, from_id, utf8_text); - if (log2file) logToFile(session_id, from, from_id, utf8_text); - - session->mNumUnread++; - - // notify listeners - LLSD arg; - arg["session_id"] = session_id; - arg["num_unread"] = session->mNumUnread; - arg["message"] = utf8_text; - arg["from"] = from; - arg["from_id"] = from_id; - arg["time"] = LLLogChat::timestamp(false); - mNewMsgSignal(arg); - - return true; -} - - -const std::string& LLIMModel::getName(const LLUUID& session_id) const -{ - LLIMSession* session = findIMSession(session_id); - - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return LLStringUtil::null; - } - - return session->mName; -} - -const S32 LLIMModel::getNumUnread(const LLUUID& session_id) const -{ - LLIMSession* session = findIMSession(session_id); - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return -1; - } - - return session->mNumUnread; -} - -const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const -{ - LLIMSession* session = findIMSession(session_id); - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return LLUUID::null; - } - - return session->mOtherParticipantID; -} - -EInstantMessage LLIMModel::getType(const LLUUID& session_id) const -{ - LLIMSession* session = findIMSession(session_id); - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return IM_COUNT; - } - - return session->mType; -} - -LLVoiceChannel* LLIMModel::getVoiceChannel( const LLUUID& session_id ) const -{ - LLIMSession* session = findIMSession(session_id); - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return NULL; - } - - return session->mVoiceChannel; -} - -LLIMSpeakerMgr* LLIMModel::getSpeakerManager( const LLUUID& session_id ) const -{ - LLIMSession* session = findIMSession(session_id); - if (!session) - { - llwarns << "session " << session_id << "does not exist " << llendl; - return NULL; - } - - return session->mSpeakers; -} - - -// TODO get rid of other participant ID -void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing) -{ - std::string name; - LLAgentUI::buildFullname(name); - - pack_instant_message( - gMessageSystem, - gAgent.getID(), - FALSE, - gAgent.getSessionID(), - other_participant_id, - name, - std::string("typing"), - IM_ONLINE, - (typing ? IM_TYPING_START : IM_TYPING_STOP), - session_id); - gAgent.sendReliableMessage(); -} - -void LLIMModel::sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id) -{ - if(session_id.notNull()) - { - std::string name; - LLAgentUI::buildFullname(name); - pack_instant_message( - gMessageSystem, - gAgent.getID(), - FALSE, - gAgent.getSessionID(), - other_participant_id, - name, - LLStringUtil::null, - IM_ONLINE, - IM_SESSION_LEAVE, - session_id); - gAgent.sendReliableMessage(); - } -} - -//*TODO this method is better be moved to the LLIMMgr -void LLIMModel::sendMessage(const std::string& utf8_text, - const LLUUID& im_session_id, - const LLUUID& other_participant_id, - EInstantMessage dialog) -{ - std::string name; - bool sent = false; - LLAgentUI::buildFullname(name); - - const LLRelationship* info = NULL; - info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id); - - U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE; - - if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id))) - { - // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice. - sent = gVoiceClient->sendTextMessage(other_participant_id, utf8_text); - } - - if(!sent) - { - // Send message normally. - - // default to IM_SESSION_SEND unless it's nothing special - in - // which case it's probably an IM to everyone. - U8 new_dialog = dialog; - - if ( dialog != IM_NOTHING_SPECIAL ) - { - new_dialog = IM_SESSION_SEND; - } - pack_instant_message( - gMessageSystem, - gAgent.getID(), - FALSE, - gAgent.getSessionID(), - other_participant_id, - name.c_str(), - utf8_text.c_str(), - offline, - (EInstantMessage)new_dialog, - im_session_id); - gAgent.sendReliableMessage(); - } - - // If there is a mute list and this is not a group chat... - if ( LLMuteList::getInstance() ) - { - // ... the target should not be in our mute list for some message types. - // Auto-remove them if present. - switch( dialog ) - { - case IM_NOTHING_SPECIAL: - case IM_GROUP_INVITATION: - case IM_INVENTORY_OFFERED: - case IM_SESSION_INVITE: - case IM_SESSION_P2P_INVITE: - case IM_SESSION_CONFERENCE_START: - case IM_SESSION_SEND: // This one is marginal - erring on the side of hearing. - case IM_LURE_USER: - case IM_GODLIKE_LURE_USER: - case IM_FRIENDSHIP_OFFERED: - LLMuteList::getInstance()->autoRemove(other_participant_id, LLMuteList::AR_IM); - break; - default: ; // do nothing - } - } - - if((dialog == IM_NOTHING_SPECIAL) && - (other_participant_id.notNull())) - { - // Do we have to replace the /me's here? - std::string from; - LLAgentUI::buildFullname(from); - LLIMModel::getInstance()->addMessage(im_session_id, from, gAgentID, utf8_text); - - //local echo for the legacy communicate panel - std::string history_echo; - LLAgentUI::buildFullname(history_echo); - - history_echo += ": " + utf8_text; - - LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(im_session_id); - if (floater) floater->addHistoryLine(history_echo, LLUIColorTable::instance().getColor("IMChatColor"), true, gAgent.getID()); - - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id); - if (speaker_mgr) - { - speaker_mgr->speakerChatted(gAgentID); - speaker_mgr->setSpeakerTyping(gAgentID, FALSE); - } - } - - // Add the recipient to the recent people list. - LLRecentPeople::instance().add(other_participant_id); -} - -void session_starter_helper( - const LLUUID& temp_session_id, - const LLUUID& other_participant_id, - EInstantMessage im_type) -{ - LLMessageSystem *msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_ImprovedInstantMessage); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - - msg->nextBlockFast(_PREHASH_MessageBlock); - msg->addBOOLFast(_PREHASH_FromGroup, FALSE); - msg->addUUIDFast(_PREHASH_ToAgentID, other_participant_id); - msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); - msg->addU8Fast(_PREHASH_Dialog, im_type); - msg->addUUIDFast(_PREHASH_ID, temp_session_id); - msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary - - std::string name; - LLAgentUI::buildFullname(name); - - msg->addStringFast(_PREHASH_FromAgentName, name); - msg->addStringFast(_PREHASH_Message, LLStringUtil::null); - msg->addU32Fast(_PREHASH_ParentEstateID, 0); - msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); - msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); -} - -void start_deprecated_conference_chat( - const LLUUID& temp_session_id, - const LLUUID& creator_id, - const LLUUID& other_participant_id, - const LLSD& agents_to_invite) -{ - U8* bucket; - U8* pos; - S32 count; - S32 bucket_size; - - // *FIX: this could suffer from endian issues - count = agents_to_invite.size(); - bucket_size = UUID_BYTES * count; - bucket = new U8[bucket_size]; - pos = bucket; - - for(S32 i = 0; i < count; ++i) - { - LLUUID agent_id = agents_to_invite[i].asUUID(); - - memcpy(pos, &agent_id, UUID_BYTES); - pos += UUID_BYTES; - } - - session_starter_helper( - temp_session_id, - other_participant_id, - IM_SESSION_CONFERENCE_START); - - gMessageSystem->addBinaryDataFast( - _PREHASH_BinaryBucket, - bucket, - bucket_size); - - gAgent.sendReliableMessage(); - - delete[] bucket; -} - -class LLStartConferenceChatResponder : public LLHTTPClient::Responder -{ -public: - LLStartConferenceChatResponder( - const LLUUID& temp_session_id, - const LLUUID& creator_id, - const LLUUID& other_participant_id, - const LLSD& agents_to_invite) - { - mTempSessionID = temp_session_id; - mCreatorID = creator_id; - mOtherParticipantID = other_participant_id; - mAgents = agents_to_invite; - } - - virtual void error(U32 statusNum, const std::string& reason) - { - //try an "old school" way. - if ( statusNum == 400 ) - { - start_deprecated_conference_chat( - mTempSessionID, - mCreatorID, - mOtherParticipantID, - mAgents); - } - - //else throw an error back to the client? - //in theory we should have just have these error strings - //etc. set up in this file as opposed to the IMMgr, - //but the error string were unneeded here previously - //and it is not worth the effort switching over all - //the possible different language translations - } - -private: - LLUUID mTempSessionID; - LLUUID mCreatorID; - LLUUID mOtherParticipantID; - - LLSD mAgents; -}; - -// Returns true if any messages were sent, false otherwise. -// Is sort of equivalent to "does the server need to do anything?" -bool LLIMModel::sendStartSession( - const LLUUID& temp_session_id, - const LLUUID& other_participant_id, - const std::vector<LLUUID>& ids, - EInstantMessage dialog) -{ - if ( dialog == IM_SESSION_GROUP_START ) - { - session_starter_helper( - temp_session_id, - other_participant_id, - dialog); - gMessageSystem->addBinaryDataFast( - _PREHASH_BinaryBucket, - EMPTY_BINARY_BUCKET, - EMPTY_BINARY_BUCKET_SIZE); - gAgent.sendReliableMessage(); - - return true; - } - else if ( dialog == IM_SESSION_CONFERENCE_START ) - { - LLSD agents; - for (int i = 0; i < (S32) ids.size(); i++) - { - agents.append(ids[i]); - } - - //we have a new way of starting conference calls now - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - std::string url = region->getCapability( - "ChatSessionRequest"); - LLSD data; - data["method"] = "start conference"; - data["session-id"] = temp_session_id; - - data["params"] = agents; - - LLHTTPClient::post( - url, - data, - new LLStartConferenceChatResponder( - temp_session_id, - gAgent.getID(), - other_participant_id, - data["params"])); - } - else - { - start_deprecated_conference_chat( - temp_session_id, - gAgent.getID(), - other_participant_id, - agents); - } - - //we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id) - return true; - } - - return false; -} - -// static -void LLIMModel::sendSessionInitialized(const LLUUID &session_id) -{ - LLIMSession* session = getInstance()->findIMSession(session_id); - if (session) - { - LLSD arg; - arg["session_id"] = session_id; - getInstance()->mSessionInitializedSignal(arg); - } -} - -// -// Helper Functions -// - -class LLViewerChatterBoxInvitationAcceptResponder : - public LLHTTPClient::Responder -{ -public: - LLViewerChatterBoxInvitationAcceptResponder( - const LLUUID& session_id, - LLIMMgr::EInvitationType invitation_type) - { - mSessionID = session_id; - mInvitiationType = invitation_type; - } - - void result(const LLSD& content) - { - if ( gIMMgr) - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - //we've accepted our invitation - //and received a list of agents that were - //currently in the session when the reply was sent - //to us. Now, it is possible that there were some agents - //to slip in/out between when that message was sent to us - //and now. - - //the agent list updates we've received have been - //accurate from the time we were added to the session - //but unfortunately, our base that we are receiving here - //may not be the most up to date. It was accurate at - //some point in time though. - speaker_mgr->setSpeakers(content); - - //we now have our base of users in the session - //that was accurate at some point, but maybe not now - //so now we apply all of the udpates we've received - //in case of race conditions - speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID)); - } - - if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType) - { - gIMMgr->startCall(mSessionID); - } - - if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE - || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE) - && LLIMModel::getInstance()->findIMSession(mSessionID)) - { - // always open IM window when connecting to voice - LLIMFloater::show(mSessionID); - } - - gIMMgr->clearPendingAgentListUpdates(mSessionID); - gIMMgr->clearPendingInvitation(mSessionID); - } - } - - void error(U32 statusNum, const std::string& reason) - { - //throw something back to the viewer here? - if ( gIMMgr ) - { - gIMMgr->clearPendingAgentListUpdates(mSessionID); - gIMMgr->clearPendingInvitation(mSessionID); - if ( 404 == statusNum ) - { - std::string error_string; - error_string = "does not exist"; - gIMMgr->showSessionStartError(error_string, mSessionID); - } - } - } - -private: - LLUUID mSessionID; - LLIMMgr::EInvitationType mInvitiationType; -}; - - -// the other_participant_id is either an agent_id, a group_id, or an inventory -// folder item_id (collection of calling cards) - -// static -LLUUID LLIMMgr::computeSessionID( - 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 if (IM_SESSION_CONFERENCE_START == dialog) - { - session_id.generate(); - } - else if (IM_SESSION_INVITE == dialog) - { - // use provided session id for invites - 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; -} - -inline LLFloater* getFloaterBySessionID(const LLUUID session_id) -{ - LLFloater* floater = NULL; - if ( gIMMgr ) - { - floater = dynamic_cast < LLFloater* > - ( gIMMgr->findFloaterBySession(session_id) ); - } - if ( !floater ) - { - floater = dynamic_cast < LLFloater* > - ( LLIMFloater::findInstance(session_id) ); - } - return floater; -} - -void -LLIMMgr::showSessionStartError( - const std::string& error_string, - const LLUUID session_id) -{ - const LLFloater* floater = getFloaterBySessionID (session_id); - if (!floater) return; - - LLSD args; - args["REASON"] = LLTrans::getString(error_string); - args["RECIPIENT"] = floater->getTitle(); - - LLSD payload; - payload["session_id"] = session_id; - - LLNotifications::instance().add( - "ChatterBoxSessionStartError", - args, - payload, - LLIMMgr::onConfirmForceCloseError); -} - -void -LLIMMgr::showSessionEventError( - const std::string& event_string, - const std::string& error_string, - const LLUUID session_id) -{ - const LLFloater* floater = getFloaterBySessionID (session_id); - if (!floater) return; - - LLSD args; - args["REASON"] = - LLTrans::getString(error_string); - args["EVENT"] = - LLTrans::getString(event_string); - args["RECIPIENT"] = floater->getTitle(); - - LLNotifications::instance().add( - "ChatterBoxSessionEventError", - args); -} - -void -LLIMMgr::showSessionForceClose( - const std::string& reason_string, - const LLUUID session_id) -{ - const LLFloater* floater = getFloaterBySessionID (session_id); - if (!floater) return; - - LLSD args; - - args["NAME"] = floater->getTitle(); - args["REASON"] = LLTrans::getString(reason_string); - - LLSD payload; - payload["session_id"] = session_id; - - LLNotifications::instance().add( - "ForceCloseChatterBoxSession", - args, - payload, - LLIMMgr::onConfirmForceCloseError); -} - -//static -bool -LLIMMgr::onConfirmForceCloseError( - const LLSD& notification, - const LLSD& response) -{ - //only 1 option really - LLUUID session_id = notification["payload"]["session_id"]; - - LLFloater* floater = getFloaterBySessionID (session_id); - if ( floater ) - { - floater->closeFloater(FALSE); - } - return false; -} - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLOutgoingCallDialog -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLOutgoingCallDialog::LLOutgoingCallDialog(const LLSD& payload) : - LLDockableFloater(NULL, false, payload), - mPayload(payload) -{ -} - -void LLOutgoingCallDialog::getAllowedRect(LLRect& rect) -{ - rect = gViewerWindow->getWorldViewRectRaw(); -} - -void LLOutgoingCallDialog::onOpen(const LLSD& key) -{ - // tell the user which voice channel they are leaving - if (!mPayload["old_channel_name"].asString().empty()) - { - childSetTextArg("leaving", "[CURRENT_CHAT]", mPayload["old_channel_name"].asString()); - } - else - { - childSetTextArg("leaving", "[CURRENT_CHAT]", getString("localchat")); - } - - std::string callee_name = mPayload["session_name"].asString(); - if (callee_name == "anonymous") - { - callee_name = getString("anonymous"); - } - - setTitle(callee_name); - - LLSD callee_id = mPayload["other_user_id"]; - childSetTextArg("calling", "[CALLEE_NAME]", callee_name); - childSetTextArg("connecting", "[CALLEE_NAME]", callee_name); - LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); - icon->setValue(callee_id); -} - - -//static -void LLOutgoingCallDialog::onCancel(void* user_data) -{ - LLOutgoingCallDialog* self = (LLOutgoingCallDialog*)user_data; - - if (!gIMMgr) - return; - - LLUUID session_id = self->mPayload["session_id"].asUUID(); - gIMMgr->endCall(session_id); - - self->closeFloater(); -} - - -BOOL LLOutgoingCallDialog::postBuild() -{ - BOOL success = LLDockableFloater::postBuild(); - - childSetAction("Cancel", onCancel, this); - - // dock the dialog to the sys well, where other sys messages appear - setDockControl(new LLDockControl(LLBottomTray::getInstance()->getSysWell(), - this, getDockTongue(), LLDockControl::TOP, - boost::bind(&LLOutgoingCallDialog::getAllowedRect, this, _1))); - - return success; -} - - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLIncomingCallDialog -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) : - LLDockableFloater(NULL, false, payload), - mPayload(payload) -{ -} - -BOOL LLIncomingCallDialog::postBuild() -{ - LLDockableFloater::postBuild(); - - LLSD caller_id = mPayload["caller_id"]; - EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger(); - - std::string call_type = getString("VoiceInviteP2P"); - std::string caller_name = mPayload["caller_name"].asString(); - if (caller_name == "anonymous") - { - caller_name = getString("anonymous"); - } - - setTitle(caller_name + " " + call_type); - - // If it is not a P2P invite, then it's an AdHoc invite - if ( type != IM_SESSION_P2P_INVITE ) - { - call_type = getString("VoiceInviteAdHoc"); - } - - // check to see if this is an Avaline call - LLUUID session_id = mPayload["session_id"].asUUID(); - bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); - childSetVisible("Start IM", is_avatar); // no IM for avaline - - LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name"); - caller_name_widget->setValue(caller_name + " " + call_type); - LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); - icon->setValue(caller_id); - - childSetAction("Accept", onAccept, this); - childSetAction("Reject", onReject, this); - childSetAction("Start IM", onStartIM, this); - childSetFocus("Accept"); - - return TRUE; -} - -void LLIncomingCallDialog::getAllowedRect(LLRect& rect) -{ - rect = gViewerWindow->getWorldViewRectRaw(); -} - -void LLIncomingCallDialog::onOpen(const LLSD& key) -{ - // tell the user which voice channel they would be leaving - LLVoiceChannel *voice = LLVoiceChannel::getCurrentVoiceChannel(); - if (voice && !voice->getSessionName().empty()) - { - childSetTextArg("question", "[CURRENT_CHAT]", voice->getSessionName()); - } - else - { - childSetTextArg("question", "[CURRENT_CHAT]", getString("localchat")); - } - - // dock the dialog to the sys well, where other sys messages appear - setDockControl(new LLDockControl(LLBottomTray::getInstance()->getSysWell(), - this, getDockTongue(), LLDockControl::TOP, - boost::bind(&LLIncomingCallDialog::getAllowedRect, this, _1))); -} - -//static -void LLIncomingCallDialog::onAccept(void* user_data) -{ - LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data; - self->processCallResponse(0); - self->closeFloater(); -} - -//static -void LLIncomingCallDialog::onReject(void* user_data) -{ - LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data; - self->processCallResponse(1); - self->closeFloater(); -} - -//static -void LLIncomingCallDialog::onStartIM(void* user_data) -{ - LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data; - self->processCallResponse(2); - self->closeFloater(); -} - -void LLIncomingCallDialog::processCallResponse(S32 response) -{ - if (!gIMMgr) - return; - - LLUUID session_id = mPayload["session_id"].asUUID(); - EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger(); - LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)mPayload["inv_type"].asInteger(); - bool voice = true; - switch(response) - { - case 2: // start IM: just don't start the voice chat - { - voice = false; - /* FALLTHROUGH */ - } - case 0: // accept - { - if (type == IM_SESSION_P2P_INVITE) - { - // create a normal IM session - session_id = gIMMgr->addP2PSession( - mPayload["session_name"].asString(), - mPayload["caller_id"].asUUID(), - mPayload["session_handle"].asString()); - - if (voice) - { - if (gIMMgr->startCall(session_id)) - { - // always open IM window when connecting to voice - LLIMFloater::show(session_id); - } - } - - gIMMgr->clearPendingAgentListUpdates(session_id); - gIMMgr->clearPendingInvitation(session_id); - } - else - { - LLUUID session_id = gIMMgr->addSession( - mPayload["session_name"].asString(), - type, - session_id); - if (session_id != LLUUID::null) - { - LLIMFloater::show(session_id); - } - - std::string url = gAgent.getRegion()->getCapability( - "ChatSessionRequest"); - - if (voice) - { - LLSD data; - data["method"] = "accept invitation"; - data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - new LLViewerChatterBoxInvitationAcceptResponder( - session_id, - inv_type)); - } - } - if (voice) - { - break; - } - } - case 1: // decline - { - if (type == IM_SESSION_P2P_INVITE) - { - if(gVoiceClient) - { - std::string s = mPayload["session_handle"].asString(); - gVoiceClient->declineInvite(s); - } - } - else - { - std::string url = gAgent.getRegion()->getCapability( - "ChatSessionRequest"); - - LLSD data; - data["method"] = "decline invitation"; - data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - NULL); - } - } - - gIMMgr->clearPendingAgentListUpdates(session_id); - gIMMgr->clearPendingInvitation(session_id); - } -} - -bool inviteUserResponse(const LLSD& notification, const LLSD& response) -{ - if (!gIMMgr) - return false; - - const LLSD& payload = notification["payload"]; - LLUUID session_id = payload["session_id"].asUUID(); - EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); - LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); - S32 option = LLNotification::getSelectedOption(notification, response); - switch(option) - { - case 0: // accept - { - if (type == IM_SESSION_P2P_INVITE) - { - // create a normal IM session - session_id = gIMMgr->addP2PSession( - payload["session_name"].asString(), - payload["caller_id"].asUUID(), - payload["session_handle"].asString(), - payload["session_uri"].asString()); - - if (gIMMgr->startCall(session_id)) - { - // always open IM window when connecting to voice - LLIMFloater::show(session_id); - } - - gIMMgr->clearPendingAgentListUpdates(session_id); - gIMMgr->clearPendingInvitation(session_id); - } - else - { - LLUUID session_id = gIMMgr->addSession( - payload["session_name"].asString(), - type, - session_id); - if (session_id != LLUUID::null) - { - LLIMFloater::show(session_id); - } - - std::string url = gAgent.getRegion()->getCapability( - "ChatSessionRequest"); - - LLSD data; - data["method"] = "accept invitation"; - data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - new LLViewerChatterBoxInvitationAcceptResponder( - session_id, - inv_type)); - } - } - break; - case 2: // mute (also implies ignore, so this falls through to the "ignore" case below) - { - // mute the sender of this invite - if (!LLMuteList::getInstance()->isMuted(payload["caller_id"].asUUID())) - { - LLMute mute(payload["caller_id"].asUUID(), payload["caller_name"].asString(), LLMute::AGENT); - LLMuteList::getInstance()->add(mute); - } - } - /* FALLTHROUGH */ - - case 1: // decline - { - if (type == IM_SESSION_P2P_INVITE) - { - if(gVoiceClient) - { - std::string s = payload["session_handle"].asString(); - gVoiceClient->declineInvite(s); - } - } - else - { - std::string url = gAgent.getRegion()->getCapability( - "ChatSessionRequest"); - - LLSD data; - data["method"] = "decline invitation"; - data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - NULL); - } - } - - gIMMgr->clearPendingAgentListUpdates(session_id); - gIMMgr->clearPendingInvitation(session_id); - break; - } - - return false; -} - -// -// Member Functions -// - -LLIMMgr::LLIMMgr() : - mIMReceived(FALSE) -{ - mPendingInvitations = LLSD::emptyMap(); - mPendingAgentListUpdates = LLSD::emptyMap(); -} - -// Add a message to a session. -void LLIMMgr::addMessage( - const LLUUID& session_id, - const LLUUID& target_id, - const std::string& from, - const std::string& msg, - const std::string& session_name, - EInstantMessage dialog, - U32 parent_estate_id, - const LLUUID& region_id, - const LLVector3& position, - bool link_name) // If this is true, then we insert the name and link it to a profile -{ - LLUUID other_participant_id = target_id; - - // don't process muted IMs - if (LLMuteList::getInstance()->isMuted( - other_participant_id, - LLMute::flagTextChat) && !LLMuteList::getInstance()->isLinden(from)) - { - return; - } - - LLFloaterIMPanel* floater; - LLUUID new_session_id = session_id; - if (new_session_id.isNull()) - { - //no session ID...compute new one - new_session_id = computeSessionID(dialog, other_participant_id); - } - - //*NOTE session_name is empty in case of incoming P2P sessions - std::string fixed_session_name = from; - if(!session_name.empty() && session_name.size()>1) - { - fixed_session_name = session_name; - } - - bool new_session = !hasSession(new_session_id); - if (new_session) - { - LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, 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; - } - } - - // create IM window as necessary - if(!floater) - { - floater = createFloater( - new_session_id, - other_participant_id, - fixed_session_name, - dialog, - FALSE); - } - - if (new_session) - { - // 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()) - { - // *TODO:translate (low priority, god ability) - std::ostringstream bonus_info; - bonus_info << LLTrans::getString("***")+ " "+ LLTrans::getString("IMParentEstate") + ":" + " " - << parent_estate_id - << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "") - << ((parent_estate_id == 5) ? "," + LLTrans::getString ("IMTeen") : ""); - - // 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(), LLUIColorTable::instance().getColor("SystemChatColor")); - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str()); - } - - make_ui_sound("UISndNewIncomingIMSession"); - } - - // now add message to floater - bool is_from_system = target_id.isNull() || (from == SYSTEM_FROM); - const LLColor4& color = ( is_from_system ? - LLUIColorTable::instance().getColor("SystemChatColor") : - LLUIColorTable::instance().getColor("IMChatColor")); - if ( !link_name ) - { - floater->addHistoryLine(msg,color); // No name to prepend, so just add the message normally - } - else - { - floater->addHistoryLine(msg, color, true, other_participant_id, from); // Insert linked name to front of message - } - - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg); - - if( !LLFloaterReg::instanceVisible("communicate") && !floater->getVisible()) - { - LLFloaterChatterBox* chat_floater = LLFloaterChatterBox::getInstance(); - - //if the IM window is not open and the floater is not visible (i.e. not torn off) - LLFloater* previouslyActiveFloater = chat_floater->getActiveFloater(); - - // select the newly added floater (or the floater with the new line added to it). - // it should be there. - chat_floater->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() ) - { - chat_floater->setFloaterFlashing(previouslyActiveFloater, TRUE); - } - - //notify of a new IM - notifyNewIM(); - } -} - -void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args) -{ - LLUIString message; - - // null session id means near me (chat history) - if (session_id.isNull()) - { - message = LLTrans::getString(message_name); - message.setArgs(args); - - LLChat chat(message); - chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloaterChat::addChatHistory(chat); - } - else // going to IM session - { - if (hasSession(session_id)) - { - message = LLTrans::getString(message_name + "-im"); - message.setArgs(args); - gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString()); - } - } -} - -void LLIMMgr::notifyNewIM() -{ - if(!LLFloaterReg::instanceVisible("communicate")) - { - mIMReceived = TRUE; - } -} - -S32 LLIMMgr::getNumberOfUnreadIM() -{ - std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it; - - S32 num = 0; - for(it = LLIMModel::getInstance()->mId2SessionMap.begin(); it != LLIMModel::getInstance()->mId2SessionMap.end(); ++it) - { - num += (*it).second->mNumUnread; - } - - return num; -} - -void LLIMMgr::clearNewIMNotification() -{ - mIMReceived = FALSE; -} - -BOOL LLIMMgr::getIMReceived() const -{ - return mIMReceived; -} - -LLUUID LLIMMgr::addP2PSession(const std::string& name, - const LLUUID& other_participant_id, - const std::string& voice_session_handle, - const std::string& caller_uri) -{ - LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id); - if (session_id != LLUUID::null) - { - LLIMFloater::show(session_id); - } - - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - if (speaker_mgr) - { - LLVoiceChannelP2P* voice_channel = dynamic_cast<LLVoiceChannelP2P*>(speaker_mgr->getVoiceChannel()); - if (voice_channel) - { - voice_channel->setSessionHandle(voice_session_handle, caller_uri); - } - } - return session_id; -} - -// 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 LLIMMgr::addSession( - const std::string& name, - EInstantMessage dialog, - const LLUUID& other_participant_id) -{ - LLDynamicArray<LLUUID> ids; - ids.put(other_participant_id); - return addSession(name, dialog, other_participant_id, ids); -} - -// 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 LLIMMgr::addSession( - const std::string& name, - EInstantMessage dialog, - const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids) -{ - if (0 == ids.getLength()) - { - return LLUUID::null; - } - - LLUUID session_id = computeSessionID(dialog,other_participant_id); - - bool new_session = !LLIMModel::getInstance()->findIMSession(session_id); - - if (new_session) - { - LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids); - } - - - //*TODO remove this "floater" thing when Communicate Floater's gone - LLFloaterIMPanel* floater = findFloaterBySession(session_id); - if(!floater) - { - // On creation, use the first element of ids as the - // "other_participant_id" - floater = createFloater( - session_id, - other_participant_id, - name, - dialog, - TRUE, - ids); - } - - //we don't need to show notes about online/offline, mute/unmute users' statuses for existing sessions - if (!new_session) return session_id; - - noteOfflineUsers(session_id, floater, ids); - - // Only warn for regular IMs - not group IMs - if( dialog == IM_NOTHING_SPECIAL ) - { - noteMutedUsers(session_id, floater, ids); - } - - return session_id; -} - -bool LLIMMgr::leaveSession(const LLUUID& session_id) -{ - LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id); - if (!im_session) return false; - - LLIMModel::getInstance()->sendLeaveSession(session_id, im_session->mOtherParticipantID); - gIMMgr->removeSession(session_id); - return true; -} - -// Removes data associated with a particular session specified by session_id -void LLIMMgr::removeSession(const LLUUID& session_id) -{ - llassert_always(hasSession(session_id)); - - //*TODO remove this floater thing when Communicate Floater is being deleted (IB) - LLFloaterIMPanel* floater = findFloaterBySession(session_id); - if(floater) - { - mFloaters.erase(floater->getHandle()); - LLFloaterChatterBox::getInstance()->removeFloater(floater); - } - - clearPendingInvitation(session_id); - clearPendingAgentListUpdates(session_id); - - LLIMModel::getInstance()->clearSession(session_id); - - notifyObserverSessionRemoved(session_id); -} - -void LLIMMgr::inviteToSession( - const LLUUID& session_id, - const std::string& session_name, - const LLUUID& caller_id, - const std::string& caller_name, - EInstantMessage type, - EInvitationType inv_type, - const std::string& session_handle, - const std::string& session_uri) -{ - //ignore invites from muted residents - if (LLMuteList::getInstance()->isMuted(caller_id)) - { - return; - } - - std::string notify_box_type; - - BOOL ad_hoc_invite = FALSE; - if(type == IM_SESSION_P2P_INVITE) - { - //P2P is different...they only have voice invitations - notify_box_type = "VoiceInviteP2P"; - } - else if ( gAgent.isInGroup(session_id) ) - { - //only really old school groups have voice invitations - notify_box_type = "VoiceInviteGroup"; - } - else if ( inv_type == INVITATION_TYPE_VOICE ) - { - //else it's an ad-hoc - //and a voice ad-hoc - notify_box_type = "VoiceInviteAdHoc"; - ad_hoc_invite = TRUE; - } - else if ( inv_type == INVITATION_TYPE_IMMEDIATE ) - { - notify_box_type = "InviteAdHoc"; - ad_hoc_invite = TRUE; - } - - LLSD payload; - payload["session_id"] = session_id; - payload["session_name"] = session_name; - payload["caller_id"] = caller_id; - payload["caller_name"] = caller_name; - payload["type"] = type; - payload["inv_type"] = inv_type; - payload["session_handle"] = session_handle; - payload["session_uri"] = session_uri; - payload["notify_box_type"] = notify_box_type; - - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id); - if (channelp && channelp->callStarted()) - { - // you have already started a call to the other user, so just accept the invite - LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 0); - return; - } - - if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite) - { - // is the inviter a friend? - if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL) - { - // if not, and we are ignoring voice invites from non-friends - // then silently decline - if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly")) - { - // invite not from a friend, so decline - LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 1); - return; - } - } - } - - if ( !mPendingInvitations.has(session_id.asString()) ) - { - if (caller_name.empty()) - { - gCacheName->get(caller_id, FALSE, boost::bind(&LLIMMgr::onInviteNameLookup, payload, _1, _2, _3, _4)); - } - else - { - if (notify_box_type == "VoiceInviteP2P" || notify_box_type == "VoiceInviteAdHoc") - { - LLFloaterReg::showInstance("incoming_call", payload, TRUE); - } - else - { - LLSD args; - args["NAME"] = caller_name; - args["GROUP"] = session_name; - - LLNotifications::instance().add(notify_box_type, args, payload, &inviteUserResponse); - } - } - mPendingInvitations[session_id.asString()] = LLSD(); - } -} - -void LLIMMgr::onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) -{ - payload["caller_name"] = first + " " + last; - payload["session_name"] = payload["caller_name"].asString(); - - std::string notify_box_type = payload["notify_box_type"].asString(); - - if (notify_box_type == "VoiceInviteP2P" || notify_box_type == "VoiceInviteAdHoc") - { - LLFloaterReg::showInstance("incoming_call", payload, TRUE); - } - else - { - LLSD args; - args["NAME"] = payload["caller_name"].asString(); - - LLNotifications::instance().add( - payload["notify_box_type"].asString(), - args, - payload, - &inviteUserResponse); - } -} - -void LLIMMgr::disconnectAllSessions() -{ - LLFloaterIMPanel* floater = NULL; - std::set<LLHandle<LLFloater> >::iterator handle_it; - for(handle_it = mFloaters.begin(); - handle_it != mFloaters.end(); - ) - { - floater = (LLFloaterIMPanel*)handle_it->get(); - - // MUST do this BEFORE calling floater->onClose() because that may remove the item from the set, causing the subsequent increment to crash. - ++handle_it; - - if (floater) - { - floater->setEnabled(FALSE); - floater->closeFloater(TRUE); - } - } -} - - -// 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* LLIMMgr::findFloaterBySession(const LLUUID& session_id) -{ - LLFloaterIMPanel* rv = NULL; - std::set<LLHandle<LLFloater> >::iterator handle_it; - for(handle_it = mFloaters.begin(); - handle_it != mFloaters.end(); - ++handle_it) - { - rv = (LLFloaterIMPanel*)handle_it->get(); - if(rv && session_id == rv->getSessionID()) - { - break; - } - rv = NULL; - } - return rv; -} - - -BOOL LLIMMgr::hasSession(const LLUUID& session_id) -{ - return LLIMModel::getInstance()->findIMSession(session_id) != NULL; -} - -void LLIMMgr::clearPendingInvitation(const LLUUID& session_id) -{ - if ( mPendingInvitations.has(session_id.asString()) ) - { - mPendingInvitations.erase(session_id.asString()); - } -} - -void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body) -{ - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); - if ( im_floater ) - { - im_floater->processAgentListUpdates(body); - } - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - if (speaker_mgr) - { - speaker_mgr->updateSpeakers(body); - } - else - { - //we don't have a speaker manager yet..something went wrong - //we are probably receiving an update here before - //a start or an acceptance of an invitation. Race condition. - gIMMgr->addPendingAgentListUpdates( - session_id, - body); - } -} - -LLSD LLIMMgr::getPendingAgentListUpdates(const LLUUID& session_id) -{ - if ( mPendingAgentListUpdates.has(session_id.asString()) ) - { - return mPendingAgentListUpdates[session_id.asString()]; - } - else - { - return LLSD(); - } -} - -void LLIMMgr::addPendingAgentListUpdates( - const LLUUID& session_id, - const LLSD& updates) -{ - LLSD::map_const_iterator iter; - - if ( !mPendingAgentListUpdates.has(session_id.asString()) ) - { - //this is a new agent list update for this session - mPendingAgentListUpdates[session_id.asString()] = LLSD::emptyMap(); - } - - if ( - updates.has("agent_updates") && - updates["agent_updates"].isMap() && - updates.has("updates") && - updates["updates"].isMap() ) - { - //new school update - LLSD update_types = LLSD::emptyArray(); - LLSD::array_iterator array_iter; - - update_types.append("agent_updates"); - update_types.append("updates"); - - for ( - array_iter = update_types.beginArray(); - array_iter != update_types.endArray(); - ++array_iter) - { - //we only want to include the last update for a given agent - for ( - iter = updates[array_iter->asString()].beginMap(); - iter != updates[array_iter->asString()].endMap(); - ++iter) - { - mPendingAgentListUpdates[session_id.asString()][array_iter->asString()][iter->first] = - iter->second; - } - } - } - else if ( - updates.has("updates") && - updates["updates"].isMap() ) - { - //old school update where the SD contained just mappings - //of agent_id -> "LEAVE"/"ENTER" - - //only want to keep last update for each agent - for ( - iter = updates["updates"].beginMap(); - iter != updates["updates"].endMap(); - ++iter) - { - mPendingAgentListUpdates[session_id.asString()]["updates"][iter->first] = - iter->second; - } - } -} - -void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id) -{ - if ( mPendingAgentListUpdates.has(session_id.asString()) ) - { - mPendingAgentListUpdates.erase(session_id.asString()); - } -} - -void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) -{ - for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) - { - (*it)->sessionAdded(session_id, name, other_participant_id); - } -} - -void LLIMMgr::notifyObserverSessionRemoved(const LLUUID& session_id) -{ - for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) - { - (*it)->sessionRemoved(session_id); - } -} - -void LLIMMgr::notifyObserverSessionIDUpdated( const LLUUID& old_session_id, const LLUUID& new_session_id ) -{ - for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) - { - (*it)->sessionIDUpdated(old_session_id, new_session_id); - } - -} - -void LLIMMgr::addSessionObserver(LLIMSessionObserver *observer) -{ - mSessionObservers.push_back(observer); -} - -void LLIMMgr::removeSessionObserver(LLIMSessionObserver *observer) -{ - mSessionObservers.remove(observer); -} - -bool LLIMMgr::startCall(const LLUUID& session_id) -{ - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id); - if (!voice_channel) return false; - - voice_channel->activate(); - return true; -} - -bool LLIMMgr::endCall(const LLUUID& session_id) -{ - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id); - if (!voice_channel) return false; - - voice_channel->deactivate(); - return true; -} - -// 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* LLIMMgr::createFloater( - const LLUUID& session_id, - const LLUUID& other_participant_id, - const std::string& session_label, - EInstantMessage dialog, - BOOL user_initiated, - const LLDynamicArray<LLUUID>& ids) -{ - if (session_id.isNull()) - { - llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl; - } - - llinfos << "LLIMMgr::createFloater: from " << other_participant_id - << " in session " << session_id << llendl; - LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label, - session_id, - other_participant_id, - ids, - dialog); - LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; - LLFloaterChatterBox::getInstance()->addFloater(floater, FALSE, i_pt); - mFloaters.insert(floater->getHandle()); - return floater; -} - -void LLIMMgr::noteOfflineUsers( - const LLUUID& session_id, - LLFloaterIMPanel* floater, - const LLDynamicArray<LLUUID>& ids) -{ - S32 count = ids.count(); - if(count == 0) - { - const std::string& only_user = LLTrans::getString("only_user_message"); - if (floater) - { - floater->addHistoryLine(only_user, LLUIColorTable::instance().getColor("SystemChatColor")); - } - LLIMModel::getInstance()->addMessage(session_id, SYSTEM_FROM, LLUUID::null, only_user); - } - else - { - const LLRelationship* info = NULL; - LLAvatarTracker& at = LLAvatarTracker::instance(); - LLIMModel& im_model = LLIMModel::instance(); - for(S32 i = 0; i < count; ++i) - { - info = at.getBuddyInfo(ids.get(i)); - std::string first, last; - if(info && !info->isOnline() - && gCacheName->getName(ids.get(i), first, last)) - { - LLUIString offline = LLTrans::getString("offline_message"); - offline.setArg("[FIRST]", first); - offline.setArg("[LAST]", last); - im_model.proccessOnlineOfflineNotification(session_id, offline); - } - } - } -} - -void LLIMMgr::noteMutedUsers(const LLUUID& session_id, LLFloaterIMPanel* floater, - const LLDynamicArray<LLUUID>& ids) -{ - // Don't do this if we don't have a mute list. - LLMuteList *ml = LLMuteList::getInstance(); - if( !ml ) - { - return; - } - - S32 count = ids.count(); - if(count > 0) - { - LLIMModel* im_model = LLIMModel::getInstance(); - - for(S32 i = 0; i < count; ++i) - { - if( ml->isMuted(ids.get(i)) ) - { - LLUIString muted = LLTrans::getString("muted_message"); - - //*TODO remove this "floater" thing when Communicate Floater's gone - floater->addHistoryLine(muted); - - im_model->addMessage(session_id, SYSTEM_FROM, LLUUID::null, muted); - break; - } - } - } -} - -void LLIMMgr::processIMTypingStart(const LLIMInfo* im_info) -{ - processIMTypingCore(im_info, TRUE); -} - -void LLIMMgr::processIMTypingStop(const LLIMInfo* im_info) -{ - processIMTypingCore(im_info, FALSE); -} - -void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing) -{ - LLUUID session_id = computeSessionID(im_info->mIMType, im_info->mFromID); - LLFloaterIMPanel* floater = findFloaterBySession(session_id); - if (floater) - { - floater->processIMTyping(im_info, typing); - } - - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); - if ( im_floater ) - { - im_floater->processIMTyping(im_info, typing); - } -} - -class LLViewerChatterBoxSessionStartReply : public LLHTTPNode -{ -public: - virtual void describe(Description& desc) const - { - desc.shortInfo("Used for receiving a reply to a request to initialize an ChatterBox session"); - desc.postAPI(); - desc.input( - "{\"client_session_id\": UUID, \"session_id\": UUID, \"success\" boolean, \"reason\": string"); - desc.source(__FILE__, __LINE__); - } - - virtual void post(ResponsePtr response, - const LLSD& context, - const LLSD& input) const - { - LLSD body; - LLUUID temp_session_id; - LLUUID session_id; - bool success; - - body = input["body"]; - success = body["success"].asBoolean(); - temp_session_id = body["temp_session_id"].asUUID(); - - if ( success ) - { - session_id = body["session_id"].asUUID(); - - LLIMModel::getInstance()->processSessionInitializedReply(temp_session_id, session_id); - - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - if (speaker_mgr) - { - speaker_mgr->setSpeakers(body); - speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id)); - } - - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); - if (floaterp) - { - if ( body.has("session_info") ) - { - floaterp->processSessionUpdate(body["session_info"]); - } - } - - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); - if ( im_floater ) - { - if ( body.has("session_info") ) - { - im_floater->processSessionUpdate(body["session_info"]); - } - } - - gIMMgr->clearPendingAgentListUpdates(session_id); - } - else - { - //throw an error dialog and close the temp session's floater - gIMMgr->showSessionStartError(body["error"].asString(), temp_session_id); - } - - gIMMgr->clearPendingAgentListUpdates(session_id); - } -}; - -class LLViewerChatterBoxSessionEventReply : public LLHTTPNode -{ -public: - virtual void describe(Description& desc) const - { - desc.shortInfo("Used for receiving a reply to a ChatterBox session event"); - desc.postAPI(); - desc.input( - "{\"event\": string, \"reason\": string, \"success\": boolean, \"session_id\": UUID"); - desc.source(__FILE__, __LINE__); - } - - virtual void post(ResponsePtr response, - const LLSD& context, - const LLSD& input) const - { - LLUUID session_id; - bool success; - - LLSD body = input["body"]; - success = body["success"].asBoolean(); - session_id = body["session_id"].asUUID(); - - if ( !success ) - { - //throw an error dialog - gIMMgr->showSessionEventError( - body["event"].asString(), - body["error"].asString(), - session_id); - } - } -}; - -class LLViewerForceCloseChatterBoxSession: public LLHTTPNode -{ -public: - virtual void post(ResponsePtr response, - const LLSD& context, - const LLSD& input) const - { - LLUUID session_id; - std::string reason; - - session_id = input["body"]["session_id"].asUUID(); - reason = input["body"]["reason"].asString(); - - gIMMgr->showSessionForceClose(reason, session_id); - } -}; - -class LLViewerChatterBoxSessionAgentListUpdates : public LLHTTPNode -{ -public: - virtual void post( - ResponsePtr responder, - const LLSD& context, - const LLSD& input) const - { - const LLUUID& session_id = input["body"]["session_id"].asUUID(); - gIMMgr->processAgentListUpdates(session_id, input["body"]); - } -}; - -class LLViewerChatterBoxSessionUpdate : public LLHTTPNode -{ -public: - virtual void post( - ResponsePtr responder, - const LLSD& context, - const LLSD& input) const - { - LLUUID session_id = input["body"]["session_id"].asUUID(); - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); - if (floaterp) - { - floaterp->processSessionUpdate(input["body"]["info"]); - } - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); - if ( im_floater ) - { - im_floater->processSessionUpdate(input["body"]["info"]); - } - } -}; - - -class LLViewerChatterBoxInvitation : public LLHTTPNode -{ -public: - - virtual void post( - ResponsePtr response, - const LLSD& context, - const LLSD& input) const - { - //for backwards compatiblity reasons...we need to still - //check for 'text' or 'voice' invitations...bleh - if ( input["body"].has("instantmessage") ) - { - LLSD message_params = - input["body"]["instantmessage"]["message_params"]; - - //do something here to have the IM invite behave - //just like a normal IM - //this is just replicated code from process_improved_im - //and should really go in it's own function -jwolk - if (gNoRender) - { - return; - } - LLChat chat; - - std::string message = message_params["message"].asString(); - std::string name = message_params["from_name"].asString(); - LLUUID from_id = message_params["from_id"].asUUID(); - LLUUID session_id = message_params["id"].asUUID(); - std::vector<U8> bin_bucket = message_params["data"]["binary_bucket"].asBinary(); - U8 offline = (U8)message_params["offline"].asInteger(); - - time_t timestamp = - (time_t) message_params["timestamp"].asInteger(); - - BOOL is_busy = gAgent.getBusy(); - BOOL is_muted = LLMuteList::getInstance()->isMuted( - from_id, - name, - LLMute::flagTextChat); - - BOOL is_linden = LLMuteList::getInstance()->isLinden(name); - std::string separator_string(": "); - - chat.mMuted = is_muted && !is_linden; - chat.mFromID = from_id; - chat.mFromName = name; - - if (!is_linden && (is_busy || is_muted)) - { - return; - } - - // standard message, not from system - std::string saved; - if(offline == IM_OFFLINE) - { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); - } - std::string buffer = saved + message; - - BOOL is_this_agent = FALSE; - if(from_id == gAgentID) - { - is_this_agent = TRUE; - } - gIMMgr->addMessage( - session_id, - from_id, - name, - buffer, - std::string((char*)&bin_bucket[0]), - IM_SESSION_INVITE, - message_params["parent_estate_id"].asInteger(), - message_params["region_id"].asUUID(), - ll_vector3_from_sd(message_params["position"]), - true); - - chat.mText = std::string("IM: ") + name + separator_string + saved + message; - LLFloaterChat::addChat(chat, TRUE, is_this_agent); - - //K now we want to accept the invitation - std::string url = gAgent.getRegion()->getCapability( - "ChatSessionRequest"); - - if ( url != "" ) - { - LLSD data; - data["method"] = "accept invitation"; - data["session-id"] = session_id; - LLHTTPClient::post( - url, - data, - new LLViewerChatterBoxInvitationAcceptResponder( - session_id, - LLIMMgr::INVITATION_TYPE_INSTANT_MESSAGE)); - } - } //end if invitation has instant message - else if ( input["body"].has("voice") ) - { - if (gNoRender) - { - return; - } - - if(!LLVoiceClient::voiceEnabled()) - { - // Don't display voice invites unless the user has voice enabled. - return; - } - - gIMMgr->inviteToSession( - input["body"]["session_id"].asUUID(), - input["body"]["session_name"].asString(), - input["body"]["from_id"].asUUID(), - input["body"]["from_name"].asString(), - IM_SESSION_INVITE, - LLIMMgr::INVITATION_TYPE_VOICE); - } - else if ( input["body"].has("immediate") ) - { - gIMMgr->inviteToSession( - input["body"]["session_id"].asUUID(), - input["body"]["session_name"].asString(), - input["body"]["from_id"].asUUID(), - input["body"]["from_name"].asString(), - IM_SESSION_INVITE, - LLIMMgr::INVITATION_TYPE_IMMEDIATE); - } - } -}; - -LLHTTPRegistration<LLViewerChatterBoxSessionStartReply> - gHTTPRegistrationMessageChatterboxsessionstartreply( - "/message/ChatterBoxSessionStartReply"); - -LLHTTPRegistration<LLViewerChatterBoxSessionEventReply> - gHTTPRegistrationMessageChatterboxsessioneventreply( - "/message/ChatterBoxSessionEventReply"); - -LLHTTPRegistration<LLViewerForceCloseChatterBoxSession> - gHTTPRegistrationMessageForceclosechatterboxsession( - "/message/ForceCloseChatterBoxSession"); - -LLHTTPRegistration<LLViewerChatterBoxSessionAgentListUpdates> - gHTTPRegistrationMessageChatterboxsessionagentlistupdates( - "/message/ChatterBoxSessionAgentListUpdates"); - -LLHTTPRegistration<LLViewerChatterBoxSessionUpdate> - gHTTPRegistrationMessageChatterBoxSessionUpdate( - "/message/ChatterBoxSessionUpdate"); - -LLHTTPRegistration<LLViewerChatterBoxInvitation> - gHTTPRegistrationMessageChatterBoxInvitation( - "/message/ChatterBoxInvitation"); - +#include "llresmgr.h"
+#include "llfloaterchat.h"
+#include "llfloaterchatterbox.h"
+#include "llavataractions.h"
+#include "llhttpnode.h"
+#include "llimfloater.h"
+#include "llimpanel.h"
+#include "llresizebar.h"
+#include "lltabcontainer.h"
+#include "llviewercontrol.h"
+#include "llfloater.h"
+#include "llmutelist.h"
+#include "llresizehandle.h"
+#include "llkeyboard.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llcallingcard.h"
+#include "lltoolbar.h"
+#include "llviewermessage.h"
+#include "llviewerwindow.h"
+#include "llnotify.h"
+#include "llviewerregion.h"
+#include "llvoicechannel.h"
+#include "lltrans.h"
+#include "llrecentpeople.h"
+#include "llsyswellwindow.h"
+
+#include "llfirstuse.h"
+#include "llagentui.h"
+
+//
+// Globals
+//
+LLIMMgr* gIMMgr = NULL;
+
+//
+// Statics
+//
+// *FIXME: make these all either UIStrings or Strings
+
+const static std::string IM_SEPARATOR(": ");
+
+
+void toast_callback(const LLSD& msg){
+ // do not show toast in busy mode or it goes from agent
+ if (gAgent.getBusy() || gAgent.getID() == msg["from_id"])
+ {
+ return;
+ }
+
+ // check whether incoming IM belongs to an active session or not
+ if (LLIMModel::getInstance()->getActiveSessionID() == msg["session_id"])
+ {
+ return;
+ }
+
+ // Skip toasting for system messages
+ if (msg["from_id"].asUUID() == LLUUID::null)
+ {
+ return;
+ }
+
+ LLSD args;
+ args["MESSAGE"] = msg["message"];
+ args["TIME"] = msg["time"];
+ args["FROM"] = msg["from"];
+ args["FROM_ID"] = msg["from_id"];
+ args["SESSION_ID"] = msg["session_id"];
+
+ LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID()));
+}
+
+void LLIMModel::setActiveSessionID(const LLUUID& session_id)
+{
+ // check if such an ID really exists
+ if (!findIMSession(session_id))
+ {
+ llwarns << "Trying to set as active a non-existent session!" << llendl;
+ return;
+ }
+
+ mActiveSessionID = session_id;
+}
+
+LLIMModel::LLIMModel()
+{
+ addNewMsgCallback(LLIMFloater::newIMCallback);
+ addNewMsgCallback(toast_callback);
+}
+
+LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids)
+: mSessionID(session_id),
+ mName(name),
+ mType(type),
+ mNumUnread(0),
+ mOtherParticipantID(other_participant_id),
+ mInitialTargetIDs(ids),
+ mVoiceChannel(NULL),
+ mSpeakers(NULL),
+ mSessionInitialized(false),
+ mCallBackEnabled(true),
+ mTextIMPossible(true),
+ mOtherParticipantIsAvatar(true)
+{
+ if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type)
+ {
+ mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id);
+ }
+ else
+ {
+ mVoiceChannel = new LLVoiceChannelGroup(session_id, name);
+ }
+ mSpeakers = new LLIMSpeakerMgr(mVoiceChannel);
+
+ // All participants will be added to the list of people we've recently interacted with.
+ mSpeakers->addListener(&LLRecentPeople::instance(), "add");
+
+ //we need to wait for session initialization for outgoing ad-hoc and group chat session
+ //correct session id for initiated ad-hoc chat will be received from the server
+ if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID,
+ mInitialTargetIDs, mType))
+ {
+ //we don't need to wait for any responses
+ //so we're already initialized
+ mSessionInitialized = true;
+ }
+
+ if (IM_NOTHING_SPECIAL == type)
+ {
+ mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID);
+ mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID);
+ mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID);
+ }
+
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
+ LLLogChat::loadHistory(mName, &chatFromLogFile, (void *)this);
+}
+
+LLIMModel::LLIMSession::~LLIMSession()
+{
+ delete mSpeakers;
+ mSpeakers = NULL;
+
+ // End the text IM session if necessary
+ if(gVoiceClient && mOtherParticipantID.notNull())
+ {
+ switch(mType)
+ {
+ case IM_NOTHING_SPECIAL:
+ case IM_SESSION_P2P_INVITE:
+ gVoiceClient->endUserIMSession(mOtherParticipantID);
+ break;
+
+ default:
+ // Appease the linux compiler
+ break;
+ }
+ }
+
+ // HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point).
+ mVoiceChannel->deactivate();
+
+ delete mVoiceChannel;
+ mVoiceChannel = NULL;
+}
+
+void LLIMModel::LLIMSession::sessionInitReplyReceived(const LLUUID& new_session_id)
+{
+ mSessionInitialized = true;
+
+ if (new_session_id != mSessionID)
+ {
+ mSessionID = new_session_id;
+ mVoiceChannel->updateSessionID(new_session_id);
+ }
+}
+
+void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time)
+{
+ LLSD message;
+ message["from"] = from;
+ message["from_id"] = from_id;
+ message["message"] = utf8_text;
+ message["time"] = time;
+ message["index"] = (LLSD::Integer)mMsgs.size();
+
+ mMsgs.push_front(message);
+
+ if (mSpeakers && from_id.notNull())
+ {
+ mSpeakers->speakerChatted(from_id);
+ mSpeakers->setSpeakerTyping(from_id, FALSE);
+ }
+}
+
+void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata)
+{
+ if (!userdata) return;
+
+ LLIMSession* self = (LLIMSession*) userdata;
+
+ if (type == LLLogChat::LOG_LINE)
+ {
+ self->addMessage("", LLSD(), msg["message"].asString(), "");
+ }
+ else if (type == LLLogChat::LOG_LLSD)
+ {
+ self->addMessage(msg["from"].asString(), msg["from_id"].asUUID(), msg["message"].asString(), msg["time"].asString());
+ }
+}
+
+LLIMModel::LLIMSession* LLIMModel::findIMSession(const LLUUID& session_id) const
+{
+ return get_if_there(mId2SessionMap, session_id,
+ (LLIMModel::LLIMSession*) NULL);
+}
+
+void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, const LLUUID& new_session_id)
+{
+ LLIMSession* session = findIMSession(old_session_id);
+ if (session)
+ {
+ session->sessionInitReplyReceived(new_session_id);
+
+ if (old_session_id != new_session_id)
+ {
+ mId2SessionMap.erase(old_session_id);
+ mId2SessionMap[new_session_id] = session;
+
+ gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id);
+ }
+
+ LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id);
+ if (im_floater)
+ {
+ im_floater->sessionInitReplyReceived(new_session_id);
+ }
+ }
+
+ //*TODO remove this "floater" stuff when Communicate Floater is gone
+ LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(old_session_id);
+ if (floater)
+ {
+ floater->sessionInitReplyReceived(new_session_id);
+ }
+}
+
+void LLIMModel::testMessages()
+{
+ LLUUID bot1_id("d0426ec6-6535-4c11-a5d9-526bb0c654d9");
+ LLUUID bot1_session_id;
+ std::string from = "IM Tester";
+
+ bot1_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot1_id);
+ newSession(bot1_session_id, from, IM_NOTHING_SPECIAL, bot1_id);
+ addMessage(bot1_session_id, from, bot1_id, "Test Message: Hi from testerbot land!");
+
+ LLUUID bot2_id;
+ std::string firstname[] = {"Roflcopter", "Joe"};
+ std::string lastname[] = {"Linden", "Tester", "Resident", "Schmoe"};
+
+ S32 rand1 = ll_rand(sizeof firstname)/(sizeof firstname[0]);
+ S32 rand2 = ll_rand(sizeof lastname)/(sizeof lastname[0]);
+
+ from = firstname[rand1] + " " + lastname[rand2];
+ bot2_id.generate(from);
+ LLUUID bot2_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot2_id);
+ newSession(bot2_session_id, from, IM_NOTHING_SPECIAL, bot2_id);
+ addMessage(bot2_session_id, from, bot2_id, "Test Message: Hello there, I have a question. Can I bother you for a second? ");
+ addMessage(bot2_session_id, from, bot2_id, "Test Message: OMGWTFBBQ.");
+}
+
+
+bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type,
+ const LLUUID& other_participant_id, const std::vector<LLUUID>& ids)
+{
+ if (findIMSession(session_id))
+ {
+ llwarns << "IM Session " << session_id << " already exists" << llendl;
+ return false;
+ }
+
+ LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids);
+ mId2SessionMap[session_id] = session;
+
+ LLIMMgr::getInstance()->notifyObserverSessionAdded(session_id, name, other_participant_id);
+
+ return true;
+
+}
+
+bool LLIMModel::clearSession(const LLUUID& session_id)
+{
+ if (mId2SessionMap.find(session_id) == mId2SessionMap.end()) return false;
+ delete (mId2SessionMap[session_id]);
+ mId2SessionMap.erase(session_id);
+ return true;
+}
+
+void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index)
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return;
+ }
+
+ int i = session->mMsgs.size() - start_index;
+
+ for (std::list<LLSD>::iterator iter = session->mMsgs.begin();
+ iter != session->mMsgs.end() && i > 0;
+ iter++)
+ {
+ LLSD msg;
+ msg = *iter;
+ messages.push_back(*iter);
+ i--;
+ }
+
+ session->mNumUnread = 0;
+
+ LLSD arg;
+ arg["session_id"] = session_id;
+ arg["num_unread"] = 0;
+ mNoUnreadMsgsSignal(arg);
+}
+
+bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) {
+
+ LLIMSession* session = findIMSession(session_id);
+
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return false;
+ }
+
+ session->addMessage(from, from_id, utf8_text, LLLogChat::timestamp(false)); //might want to add date separately
+
+ return true;
+}
+
+bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)
+{
+ S32 im_log_option = gSavedPerAccountSettings.getS32("IMLogOptions");
+ if (im_log_option != LOG_CHAT)
+ {
+ if(im_log_option == LOG_BOTH_TOGETHER)
+ {
+ LLLogChat::saveHistory(std::string("chat"), from, from_id, utf8_text);
+ return true;
+ }
+ else
+ {
+ LLLogChat::saveHistory(LLIMModel::getInstance()->getName(session_id), from, from_id, utf8_text);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLIMModel::proccessOnlineOfflineNotification(
+ const LLUUID& session_id,
+ const std::string& utf8_text)
+{
+ // Add message to old one floater
+ LLFloaterIMPanel *floater = gIMMgr->findFloaterBySession(session_id);
+ if ( floater )
+ {
+ if ( !utf8_text.empty() )
+ {
+ floater->addHistoryLine(utf8_text, LLUIColorTable::instance().getColor("SystemChatColor"));
+ }
+ }
+ // Add system message to history
+ return addMessage(session_id, SYSTEM_FROM, LLUUID::null, utf8_text);
+}
+
+bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id,
+ const std::string& utf8_text, bool log2file /* = true */) {
+ LLIMSession* session = findIMSession(session_id);
+
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return false;
+ }
+
+ addToHistory(session_id, from, from_id, utf8_text);
+ if (log2file) logToFile(session_id, from, from_id, utf8_text);
+
+ session->mNumUnread++;
+
+ // notify listeners
+ LLSD arg;
+ arg["session_id"] = session_id;
+ arg["num_unread"] = session->mNumUnread;
+ arg["message"] = utf8_text;
+ arg["from"] = from;
+ arg["from_id"] = from_id;
+ arg["time"] = LLLogChat::timestamp(false);
+ mNewMsgSignal(arg);
+
+ return true;
+}
+
+
+const std::string& LLIMModel::getName(const LLUUID& session_id) const
+{
+ LLIMSession* session = findIMSession(session_id);
+
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return LLStringUtil::null;
+ }
+
+ return session->mName;
+}
+
+const S32 LLIMModel::getNumUnread(const LLUUID& session_id) const
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return -1;
+ }
+
+ return session->mNumUnread;
+}
+
+const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return LLUUID::null;
+ }
+
+ return session->mOtherParticipantID;
+}
+
+EInstantMessage LLIMModel::getType(const LLUUID& session_id) const
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return IM_COUNT;
+ }
+
+ return session->mType;
+}
+
+LLVoiceChannel* LLIMModel::getVoiceChannel( const LLUUID& session_id ) const
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return NULL;
+ }
+
+ return session->mVoiceChannel;
+}
+
+LLIMSpeakerMgr* LLIMModel::getSpeakerManager( const LLUUID& session_id ) const
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
+ {
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return NULL;
+ }
+
+ return session->mSpeakers;
+}
+
+
+// TODO get rid of other participant ID
+void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing)
+{
+ std::string name;
+ LLAgentUI::buildFullname(name);
+
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ other_participant_id,
+ name,
+ std::string("typing"),
+ IM_ONLINE,
+ (typing ? IM_TYPING_START : IM_TYPING_STOP),
+ session_id);
+ gAgent.sendReliableMessage();
+}
+
+void LLIMModel::sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id)
+{
+ if(session_id.notNull())
+ {
+ std::string name;
+ LLAgentUI::buildFullname(name);
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ other_participant_id,
+ name,
+ LLStringUtil::null,
+ IM_ONLINE,
+ IM_SESSION_LEAVE,
+ session_id);
+ gAgent.sendReliableMessage();
+ }
+}
+
+//*TODO this method is better be moved to the LLIMMgr
+void LLIMModel::sendMessage(const std::string& utf8_text,
+ const LLUUID& im_session_id,
+ const LLUUID& other_participant_id,
+ EInstantMessage dialog)
+{
+ std::string name;
+ bool sent = false;
+ LLAgentUI::buildFullname(name);
+
+ const LLRelationship* info = NULL;
+ info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id);
+
+ U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
+
+ if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))
+ {
+ // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice.
+ sent = gVoiceClient->sendTextMessage(other_participant_id, utf8_text);
+ }
+
+ if(!sent)
+ {
+ // Send message normally.
+
+ // default to IM_SESSION_SEND unless it's nothing special - in
+ // which case it's probably an IM to everyone.
+ U8 new_dialog = dialog;
+
+ if ( dialog != IM_NOTHING_SPECIAL )
+ {
+ new_dialog = IM_SESSION_SEND;
+ }
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ other_participant_id,
+ name.c_str(),
+ utf8_text.c_str(),
+ offline,
+ (EInstantMessage)new_dialog,
+ im_session_id);
+ gAgent.sendReliableMessage();
+ }
+
+ // If there is a mute list and this is not a group chat...
+ if ( LLMuteList::getInstance() )
+ {
+ // ... the target should not be in our mute list for some message types.
+ // Auto-remove them if present.
+ switch( dialog )
+ {
+ case IM_NOTHING_SPECIAL:
+ case IM_GROUP_INVITATION:
+ case IM_INVENTORY_OFFERED:
+ case IM_SESSION_INVITE:
+ case IM_SESSION_P2P_INVITE:
+ case IM_SESSION_CONFERENCE_START:
+ case IM_SESSION_SEND: // This one is marginal - erring on the side of hearing.
+ case IM_LURE_USER:
+ case IM_GODLIKE_LURE_USER:
+ case IM_FRIENDSHIP_OFFERED:
+ LLMuteList::getInstance()->autoRemove(other_participant_id, LLMuteList::AR_IM);
+ break;
+ default: ; // do nothing
+ }
+ }
+
+ if((dialog == IM_NOTHING_SPECIAL) &&
+ (other_participant_id.notNull()))
+ {
+ // Do we have to replace the /me's here?
+ std::string from;
+ LLAgentUI::buildFullname(from);
+ LLIMModel::getInstance()->addMessage(im_session_id, from, gAgentID, utf8_text);
+
+ //local echo for the legacy communicate panel
+ std::string history_echo;
+ LLAgentUI::buildFullname(history_echo);
+
+ history_echo += ": " + utf8_text;
+
+ LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(im_session_id);
+ if (floater) floater->addHistoryLine(history_echo, LLUIColorTable::instance().getColor("IMChatColor"), true, gAgent.getID());
+
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id);
+ if (speaker_mgr)
+ {
+ speaker_mgr->speakerChatted(gAgentID);
+ speaker_mgr->setSpeakerTyping(gAgentID, FALSE);
+ }
+ }
+
+ // Add the recipient to the recent people list.
+ LLRecentPeople::instance().add(other_participant_id);
+}
+
+void session_starter_helper(
+ const LLUUID& temp_session_id,
+ const LLUUID& other_participant_id,
+ EInstantMessage im_type)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
+ msg->addUUIDFast(_PREHASH_ToAgentID, other_participant_id);
+ msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
+ msg->addU8Fast(_PREHASH_Dialog, im_type);
+ msg->addUUIDFast(_PREHASH_ID, temp_session_id);
+ msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
+
+ std::string name;
+ LLAgentUI::buildFullname(name);
+
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ msg->addStringFast(_PREHASH_Message, LLStringUtil::null);
+ msg->addU32Fast(_PREHASH_ParentEstateID, 0);
+ msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
+ msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
+}
+
+void start_deprecated_conference_chat(
+ const LLUUID& temp_session_id,
+ const LLUUID& creator_id,
+ const LLUUID& other_participant_id,
+ const LLSD& agents_to_invite)
+{
+ U8* bucket;
+ U8* pos;
+ S32 count;
+ S32 bucket_size;
+
+ // *FIX: this could suffer from endian issues
+ count = agents_to_invite.size();
+ bucket_size = UUID_BYTES * count;
+ bucket = new U8[bucket_size];
+ pos = bucket;
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLUUID agent_id = agents_to_invite[i].asUUID();
+
+ memcpy(pos, &agent_id, UUID_BYTES);
+ pos += UUID_BYTES;
+ }
+
+ session_starter_helper(
+ temp_session_id,
+ other_participant_id,
+ IM_SESSION_CONFERENCE_START);
+
+ gMessageSystem->addBinaryDataFast(
+ _PREHASH_BinaryBucket,
+ bucket,
+ bucket_size);
+
+ gAgent.sendReliableMessage();
+
+ delete[] bucket;
+}
+
+class LLStartConferenceChatResponder : public LLHTTPClient::Responder
+{
+public:
+ LLStartConferenceChatResponder(
+ const LLUUID& temp_session_id,
+ const LLUUID& creator_id,
+ const LLUUID& other_participant_id,
+ const LLSD& agents_to_invite)
+ {
+ mTempSessionID = temp_session_id;
+ mCreatorID = creator_id;
+ mOtherParticipantID = other_participant_id;
+ mAgents = agents_to_invite;
+ }
+
+ virtual void error(U32 statusNum, const std::string& reason)
+ {
+ //try an "old school" way.
+ if ( statusNum == 400 )
+ {
+ start_deprecated_conference_chat(
+ mTempSessionID,
+ mCreatorID,
+ mOtherParticipantID,
+ mAgents);
+ }
+
+ //else throw an error back to the client?
+ //in theory we should have just have these error strings
+ //etc. set up in this file as opposed to the IMMgr,
+ //but the error string were unneeded here previously
+ //and it is not worth the effort switching over all
+ //the possible different language translations
+ }
+
+private:
+ LLUUID mTempSessionID;
+ LLUUID mCreatorID;
+ LLUUID mOtherParticipantID;
+
+ LLSD mAgents;
+};
+
+// Returns true if any messages were sent, false otherwise.
+// Is sort of equivalent to "does the server need to do anything?"
+bool LLIMModel::sendStartSession(
+ const LLUUID& temp_session_id,
+ const LLUUID& other_participant_id,
+ const std::vector<LLUUID>& ids,
+ EInstantMessage dialog)
+{
+ if ( dialog == IM_SESSION_GROUP_START )
+ {
+ session_starter_helper(
+ temp_session_id,
+ other_participant_id,
+ dialog);
+ gMessageSystem->addBinaryDataFast(
+ _PREHASH_BinaryBucket,
+ EMPTY_BINARY_BUCKET,
+ EMPTY_BINARY_BUCKET_SIZE);
+ gAgent.sendReliableMessage();
+
+ return true;
+ }
+ else if ( dialog == IM_SESSION_CONFERENCE_START )
+ {
+ LLSD agents;
+ for (int i = 0; i < (S32) ids.size(); i++)
+ {
+ agents.append(ids[i]);
+ }
+
+ //we have a new way of starting conference calls now
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string url = region->getCapability(
+ "ChatSessionRequest");
+ LLSD data;
+ data["method"] = "start conference";
+ data["session-id"] = temp_session_id;
+
+ data["params"] = agents;
+
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLStartConferenceChatResponder(
+ temp_session_id,
+ gAgent.getID(),
+ other_participant_id,
+ data["params"]));
+ }
+ else
+ {
+ start_deprecated_conference_chat(
+ temp_session_id,
+ gAgent.getID(),
+ other_participant_id,
+ agents);
+ }
+
+ //we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id)
+ return true;
+ }
+
+ return false;
+}
+
+// static
+void LLIMModel::sendSessionInitialized(const LLUUID &session_id)
+{
+ LLIMSession* session = getInstance()->findIMSession(session_id);
+ if (session)
+ {
+ LLSD arg;
+ arg["session_id"] = session_id;
+ getInstance()->mSessionInitializedSignal(arg);
+ }
+}
+
+//
+// Helper Functions
+//
+
+class LLViewerChatterBoxInvitationAcceptResponder :
+ public LLHTTPClient::Responder
+{
+public:
+ LLViewerChatterBoxInvitationAcceptResponder(
+ const LLUUID& session_id,
+ LLIMMgr::EInvitationType invitation_type)
+ {
+ mSessionID = session_id;
+ mInvitiationType = invitation_type;
+ }
+
+ void result(const LLSD& content)
+ {
+ if ( gIMMgr)
+ {
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
+ if (speaker_mgr)
+ {
+ //we've accepted our invitation
+ //and received a list of agents that were
+ //currently in the session when the reply was sent
+ //to us. Now, it is possible that there were some agents
+ //to slip in/out between when that message was sent to us
+ //and now.
+
+ //the agent list updates we've received have been
+ //accurate from the time we were added to the session
+ //but unfortunately, our base that we are receiving here
+ //may not be the most up to date. It was accurate at
+ //some point in time though.
+ speaker_mgr->setSpeakers(content);
+
+ //we now have our base of users in the session
+ //that was accurate at some point, but maybe not now
+ //so now we apply all of the udpates we've received
+ //in case of race conditions
+ speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID));
+ }
+
+ if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType)
+ {
+ gIMMgr->startCall(mSessionID);
+ }
+
+ if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE
+ || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE)
+ && LLIMModel::getInstance()->findIMSession(mSessionID))
+ {
+ // always open IM window when connecting to voice
+ LLIMFloater::show(mSessionID);
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(mSessionID);
+ gIMMgr->clearPendingInvitation(mSessionID);
+ }
+ }
+
+ void error(U32 statusNum, const std::string& reason)
+ {
+ //throw something back to the viewer here?
+ if ( gIMMgr )
+ {
+ gIMMgr->clearPendingAgentListUpdates(mSessionID);
+ gIMMgr->clearPendingInvitation(mSessionID);
+ if ( 404 == statusNum )
+ {
+ std::string error_string;
+ error_string = "does not exist";
+ gIMMgr->showSessionStartError(error_string, mSessionID);
+ }
+ }
+ }
+
+private:
+ LLUUID mSessionID;
+ LLIMMgr::EInvitationType mInvitiationType;
+};
+
+
+// the other_participant_id is either an agent_id, a group_id, or an inventory
+// folder item_id (collection of calling cards)
+
+// static
+LLUUID LLIMMgr::computeSessionID(
+ 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 if (IM_SESSION_CONFERENCE_START == dialog)
+ {
+ session_id.generate();
+ }
+ else if (IM_SESSION_INVITE == dialog)
+ {
+ // use provided session id for invites
+ 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;
+}
+
+inline LLFloater* getFloaterBySessionID(const LLUUID session_id)
+{
+ LLFloater* floater = NULL;
+ if ( gIMMgr )
+ {
+ floater = dynamic_cast < LLFloater* >
+ ( gIMMgr->findFloaterBySession(session_id) );
+ }
+ if ( !floater )
+ {
+ floater = dynamic_cast < LLFloater* >
+ ( LLIMFloater::findInstance(session_id) );
+ }
+ return floater;
+}
+
+void
+LLIMMgr::showSessionStartError(
+ const std::string& error_string,
+ const LLUUID session_id)
+{
+ const LLFloater* floater = getFloaterBySessionID (session_id);
+ if (!floater) return;
+
+ LLSD args;
+ args["REASON"] = LLTrans::getString(error_string);
+ args["RECIPIENT"] = floater->getTitle();
+
+ LLSD payload;
+ payload["session_id"] = session_id;
+
+ LLNotifications::instance().add(
+ "ChatterBoxSessionStartError",
+ args,
+ payload,
+ LLIMMgr::onConfirmForceCloseError);
+}
+
+void
+LLIMMgr::showSessionEventError(
+ const std::string& event_string,
+ const std::string& error_string,
+ const LLUUID session_id)
+{
+ const LLFloater* floater = getFloaterBySessionID (session_id);
+ if (!floater) return;
+
+ LLSD args;
+ args["REASON"] =
+ LLTrans::getString(error_string);
+ args["EVENT"] =
+ LLTrans::getString(event_string);
+ args["RECIPIENT"] = floater->getTitle();
+
+ LLNotifications::instance().add(
+ "ChatterBoxSessionEventError",
+ args);
+}
+
+void
+LLIMMgr::showSessionForceClose(
+ const std::string& reason_string,
+ const LLUUID session_id)
+{
+ const LLFloater* floater = getFloaterBySessionID (session_id);
+ if (!floater) return;
+
+ LLSD args;
+
+ args["NAME"] = floater->getTitle();
+ args["REASON"] = LLTrans::getString(reason_string);
+
+ LLSD payload;
+ payload["session_id"] = session_id;
+
+ LLNotifications::instance().add(
+ "ForceCloseChatterBoxSession",
+ args,
+ payload,
+ LLIMMgr::onConfirmForceCloseError);
+}
+
+//static
+bool
+LLIMMgr::onConfirmForceCloseError(
+ const LLSD& notification,
+ const LLSD& response)
+{
+ //only 1 option really
+ LLUUID session_id = notification["payload"]["session_id"];
+
+ LLFloater* floater = getFloaterBySessionID (session_id);
+ if ( floater )
+ {
+ floater->closeFloater(FALSE);
+ }
+ return false;
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLOutgoingCallDialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LLOutgoingCallDialog::LLOutgoingCallDialog(const LLSD& payload) :
+ LLDockableFloater(NULL, false, payload),
+ mPayload(payload)
+{
+}
+
+void LLOutgoingCallDialog::getAllowedRect(LLRect& rect)
+{
+ rect = gViewerWindow->getWorldViewRectScaled();
+}
+
+void LLOutgoingCallDialog::onOpen(const LLSD& key)
+{
+ // tell the user which voice channel they are leaving
+ if (!mPayload["old_channel_name"].asString().empty())
+ {
+ childSetTextArg("leaving", "[CURRENT_CHAT]", mPayload["old_channel_name"].asString());
+ }
+ else
+ {
+ childSetTextArg("leaving", "[CURRENT_CHAT]", getString("localchat"));
+ }
+
+ std::string callee_name = mPayload["session_name"].asString();
+ if (callee_name == "anonymous")
+ {
+ callee_name = getString("anonymous");
+ }
+
+ setTitle(callee_name);
+
+ LLSD callee_id = mPayload["other_user_id"];
+ childSetTextArg("calling", "[CALLEE_NAME]", callee_name);
+ childSetTextArg("connecting", "[CALLEE_NAME]", callee_name);
+ LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon");
+ icon->setValue(callee_id);
+}
+
+
+//static
+void LLOutgoingCallDialog::onCancel(void* user_data)
+{
+ LLOutgoingCallDialog* self = (LLOutgoingCallDialog*)user_data;
+
+ if (!gIMMgr)
+ return;
+
+ LLUUID session_id = self->mPayload["session_id"].asUUID();
+ gIMMgr->endCall(session_id);
+
+ self->closeFloater();
+}
+
+
+BOOL LLOutgoingCallDialog::postBuild()
+{
+ BOOL success = LLDockableFloater::postBuild();
+
+ childSetAction("Cancel", onCancel, this);
+
+ // dock the dialog to the sys well, where other sys messages appear
+ setDockControl(new LLDockControl(LLBottomTray::getInstance()->getSysWell(),
+ this, getDockTongue(), LLDockControl::TOP,
+ boost::bind(&LLOutgoingCallDialog::getAllowedRect, this, _1)));
+
+ return success;
+}
+
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIncomingCallDialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) :
+ LLDockableFloater(NULL, false, payload),
+ mPayload(payload)
+{
+}
+
+BOOL LLIncomingCallDialog::postBuild()
+{
+ LLDockableFloater::postBuild();
+
+ LLSD caller_id = mPayload["caller_id"];
+ EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger();
+
+ std::string call_type = getString("VoiceInviteP2P");
+ std::string caller_name = mPayload["caller_name"].asString();
+ if (caller_name == "anonymous")
+ {
+ caller_name = getString("anonymous");
+ }
+
+ setTitle(caller_name + " " + call_type);
+
+ // If it is not a P2P invite, then it's an AdHoc invite
+ if ( type != IM_SESSION_P2P_INVITE )
+ {
+ call_type = getString("VoiceInviteAdHoc");
+ }
+
+ // check to see if this is an Avaline call
+ LLUUID session_id = mPayload["session_id"].asUUID();
+ bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id);
+ childSetVisible("Start IM", is_avatar); // no IM for avaline
+
+ LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name");
+ caller_name_widget->setValue(caller_name + " " + call_type);
+ LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon");
+ icon->setValue(caller_id);
+
+ childSetAction("Accept", onAccept, this);
+ childSetAction("Reject", onReject, this);
+ childSetAction("Start IM", onStartIM, this);
+ childSetFocus("Accept");
+
+ return TRUE;
+}
+
+void LLIncomingCallDialog::getAllowedRect(LLRect& rect)
+{
+ rect = gViewerWindow->getWorldViewRectScaled();
+}
+
+void LLIncomingCallDialog::onOpen(const LLSD& key)
+{
+ // tell the user which voice channel they would be leaving
+ LLVoiceChannel *voice = LLVoiceChannel::getCurrentVoiceChannel();
+ if (voice && !voice->getSessionName().empty())
+ {
+ childSetTextArg("question", "[CURRENT_CHAT]", voice->getSessionName());
+ }
+ else
+ {
+ childSetTextArg("question", "[CURRENT_CHAT]", getString("localchat"));
+ }
+
+ // dock the dialog to the sys well, where other sys messages appear
+ setDockControl(new LLDockControl(LLBottomTray::getInstance()->getSysWell(),
+ this, getDockTongue(), LLDockControl::TOP,
+ boost::bind(&LLIncomingCallDialog::getAllowedRect, this, _1)));
+}
+
+//static
+void LLIncomingCallDialog::onAccept(void* user_data)
+{
+ LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
+ self->processCallResponse(0);
+ self->closeFloater();
+}
+
+//static
+void LLIncomingCallDialog::onReject(void* user_data)
+{
+ LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
+ self->processCallResponse(1);
+ self->closeFloater();
+}
+
+//static
+void LLIncomingCallDialog::onStartIM(void* user_data)
+{
+ LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
+ self->processCallResponse(2);
+ self->closeFloater();
+}
+
+void LLIncomingCallDialog::processCallResponse(S32 response)
+{
+ if (!gIMMgr)
+ return;
+
+ LLUUID session_id = mPayload["session_id"].asUUID();
+ EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger();
+ LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)mPayload["inv_type"].asInteger();
+ bool voice = true;
+ switch(response)
+ {
+ case 2: // start IM: just don't start the voice chat
+ {
+ voice = false;
+ /* FALLTHROUGH */
+ }
+ case 0: // accept
+ {
+ if (type == IM_SESSION_P2P_INVITE)
+ {
+ // create a normal IM session
+ session_id = gIMMgr->addP2PSession(
+ mPayload["session_name"].asString(),
+ mPayload["caller_id"].asUUID(),
+ mPayload["session_handle"].asString());
+
+ if (voice)
+ {
+ if (gIMMgr->startCall(session_id))
+ {
+ // always open IM window when connecting to voice
+ LLIMFloater::show(session_id);
+ }
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ gIMMgr->clearPendingInvitation(session_id);
+ }
+ else
+ {
+ LLUUID session_id = gIMMgr->addSession(
+ mPayload["session_name"].asString(),
+ type,
+ session_id);
+ if (session_id != LLUUID::null)
+ {
+ LLIMFloater::show(session_id);
+ }
+
+ std::string url = gAgent.getRegion()->getCapability(
+ "ChatSessionRequest");
+
+ if (voice)
+ {
+ LLSD data;
+ data["method"] = "accept invitation";
+ data["session-id"] = session_id;
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLViewerChatterBoxInvitationAcceptResponder(
+ session_id,
+ inv_type));
+ }
+ }
+ if (voice)
+ {
+ break;
+ }
+ }
+ case 1: // decline
+ {
+ if (type == IM_SESSION_P2P_INVITE)
+ {
+ if(gVoiceClient)
+ {
+ std::string s = mPayload["session_handle"].asString();
+ gVoiceClient->declineInvite(s);
+ }
+ }
+ else
+ {
+ std::string url = gAgent.getRegion()->getCapability(
+ "ChatSessionRequest");
+
+ LLSD data;
+ data["method"] = "decline invitation";
+ data["session-id"] = session_id;
+ LLHTTPClient::post(
+ url,
+ data,
+ NULL);
+ }
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ gIMMgr->clearPendingInvitation(session_id);
+ }
+}
+
+bool inviteUserResponse(const LLSD& notification, const LLSD& response)
+{
+ if (!gIMMgr)
+ return false;
+
+ const LLSD& payload = notification["payload"];
+ LLUUID session_id = payload["session_id"].asUUID();
+ EInstantMessage type = (EInstantMessage)payload["type"].asInteger();
+ LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger();
+ S32 option = LLNotification::getSelectedOption(notification, response);
+ switch(option)
+ {
+ case 0: // accept
+ {
+ if (type == IM_SESSION_P2P_INVITE)
+ {
+ // create a normal IM session
+ session_id = gIMMgr->addP2PSession(
+ payload["session_name"].asString(),
+ payload["caller_id"].asUUID(),
+ payload["session_handle"].asString(),
+ payload["session_uri"].asString());
+
+ if (gIMMgr->startCall(session_id))
+ {
+ // always open IM window when connecting to voice
+ LLIMFloater::show(session_id);
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ gIMMgr->clearPendingInvitation(session_id);
+ }
+ else
+ {
+ LLUUID session_id = gIMMgr->addSession(
+ payload["session_name"].asString(),
+ type,
+ session_id);
+ if (session_id != LLUUID::null)
+ {
+ LLIMFloater::show(session_id);
+ }
+
+ std::string url = gAgent.getRegion()->getCapability(
+ "ChatSessionRequest");
+
+ LLSD data;
+ data["method"] = "accept invitation";
+ data["session-id"] = session_id;
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLViewerChatterBoxInvitationAcceptResponder(
+ session_id,
+ inv_type));
+ }
+ }
+ break;
+ case 2: // mute (also implies ignore, so this falls through to the "ignore" case below)
+ {
+ // mute the sender of this invite
+ if (!LLMuteList::getInstance()->isMuted(payload["caller_id"].asUUID()))
+ {
+ LLMute mute(payload["caller_id"].asUUID(), payload["caller_name"].asString(), LLMute::AGENT);
+ LLMuteList::getInstance()->add(mute);
+ }
+ }
+ /* FALLTHROUGH */
+
+ case 1: // decline
+ {
+ if (type == IM_SESSION_P2P_INVITE)
+ {
+ if(gVoiceClient)
+ {
+ std::string s = payload["session_handle"].asString();
+ gVoiceClient->declineInvite(s);
+ }
+ }
+ else
+ {
+ std::string url = gAgent.getRegion()->getCapability(
+ "ChatSessionRequest");
+
+ LLSD data;
+ data["method"] = "decline invitation";
+ data["session-id"] = session_id;
+ LLHTTPClient::post(
+ url,
+ data,
+ NULL);
+ }
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ gIMMgr->clearPendingInvitation(session_id);
+ break;
+ }
+
+ return false;
+}
+
+//
+// Member Functions
+//
+
+LLIMMgr::LLIMMgr() :
+ mIMReceived(FALSE)
+{
+ mPendingInvitations = LLSD::emptyMap();
+ mPendingAgentListUpdates = LLSD::emptyMap();
+}
+
+// Add a message to a session.
+void LLIMMgr::addMessage(
+ const LLUUID& session_id,
+ const LLUUID& target_id,
+ const std::string& from,
+ const std::string& msg,
+ const std::string& session_name,
+ EInstantMessage dialog,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ bool link_name) // If this is true, then we insert the name and link it to a profile
+{
+ LLUUID other_participant_id = target_id;
+
+ // don't process muted IMs
+ if (LLMuteList::getInstance()->isMuted(
+ other_participant_id,
+ LLMute::flagTextChat) && !LLMuteList::getInstance()->isLinden(from))
+ {
+ return;
+ }
+
+ LLFloaterIMPanel* floater;
+ LLUUID new_session_id = session_id;
+ if (new_session_id.isNull())
+ {
+ //no session ID...compute new one
+ new_session_id = computeSessionID(dialog, other_participant_id);
+ }
+
+ //*NOTE session_name is empty in case of incoming P2P sessions
+ std::string fixed_session_name = from;
+ if(!session_name.empty() && session_name.size()>1)
+ {
+ fixed_session_name = session_name;
+ }
+
+ bool new_session = !hasSession(new_session_id);
+ if (new_session)
+ {
+ LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, 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;
+ }
+ }
+
+ // create IM window as necessary
+ if(!floater)
+ {
+ floater = createFloater(
+ new_session_id,
+ other_participant_id,
+ fixed_session_name,
+ dialog,
+ FALSE);
+ }
+
+ if (new_session)
+ {
+ // 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())
+ {
+ // *TODO:translate (low priority, god ability)
+ std::ostringstream bonus_info;
+ bonus_info << LLTrans::getString("***")+ " "+ LLTrans::getString("IMParentEstate") + ":" + " "
+ << parent_estate_id
+ << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "")
+ << ((parent_estate_id == 5) ? "," + LLTrans::getString ("IMTeen") : "");
+
+ // 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(), LLUIColorTable::instance().getColor("SystemChatColor"));
+ LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str());
+ }
+
+ make_ui_sound("UISndNewIncomingIMSession");
+ }
+
+ // now add message to floater
+ bool is_from_system = target_id.isNull() || (from == SYSTEM_FROM);
+ const LLColor4& color = ( is_from_system ?
+ LLUIColorTable::instance().getColor("SystemChatColor") :
+ LLUIColorTable::instance().getColor("IMChatColor"));
+ if ( !link_name )
+ {
+ floater->addHistoryLine(msg,color); // No name to prepend, so just add the message normally
+ }
+ else
+ {
+ floater->addHistoryLine(msg, color, true, other_participant_id, from); // Insert linked name to front of message
+ }
+
+ LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg);
+
+ if( !LLFloaterReg::instanceVisible("communicate") && !floater->getVisible())
+ {
+ LLFloaterChatterBox* chat_floater = LLFloaterChatterBox::getInstance();
+
+ //if the IM window is not open and the floater is not visible (i.e. not torn off)
+ LLFloater* previouslyActiveFloater = chat_floater->getActiveFloater();
+
+ // select the newly added floater (or the floater with the new line added to it).
+ // it should be there.
+ chat_floater->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() )
+ {
+ chat_floater->setFloaterFlashing(previouslyActiveFloater, TRUE);
+ }
+
+ //notify of a new IM
+ notifyNewIM();
+ }
+}
+
+void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args)
+{
+ LLUIString message;
+
+ // null session id means near me (chat history)
+ if (session_id.isNull())
+ {
+ message = LLTrans::getString(message_name);
+ message.setArgs(args);
+
+ LLChat chat(message);
+ chat.mSourceType = CHAT_SOURCE_SYSTEM;
+ LLFloaterChat::addChatHistory(chat);
+ }
+ else // going to IM session
+ {
+ if (hasSession(session_id))
+ {
+ message = LLTrans::getString(message_name + "-im");
+ message.setArgs(args);
+ gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString());
+ }
+ }
+}
+
+void LLIMMgr::notifyNewIM()
+{
+ if(!LLFloaterReg::instanceVisible("communicate"))
+ {
+ mIMReceived = TRUE;
+ }
+}
+
+S32 LLIMMgr::getNumberOfUnreadIM()
+{
+ std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it;
+
+ S32 num = 0;
+ for(it = LLIMModel::getInstance()->mId2SessionMap.begin(); it != LLIMModel::getInstance()->mId2SessionMap.end(); ++it)
+ {
+ num += (*it).second->mNumUnread;
+ }
+
+ return num;
+}
+
+void LLIMMgr::clearNewIMNotification()
+{
+ mIMReceived = FALSE;
+}
+
+BOOL LLIMMgr::getIMReceived() const
+{
+ return mIMReceived;
+}
+
+LLUUID LLIMMgr::addP2PSession(const std::string& name,
+ const LLUUID& other_participant_id,
+ const std::string& voice_session_handle,
+ const std::string& caller_uri)
+{
+ LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id);
+ if (session_id != LLUUID::null)
+ {
+ LLIMFloater::show(session_id);
+ }
+
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
+ if (speaker_mgr)
+ {
+ LLVoiceChannelP2P* voice_channel = dynamic_cast<LLVoiceChannelP2P*>(speaker_mgr->getVoiceChannel());
+ if (voice_channel)
+ {
+ voice_channel->setSessionHandle(voice_session_handle, caller_uri);
+ }
+ }
+ return session_id;
+}
+
+// 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 LLIMMgr::addSession(
+ const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id)
+{
+ LLDynamicArray<LLUUID> ids;
+ ids.put(other_participant_id);
+ return addSession(name, dialog, other_participant_id, ids);
+}
+
+// 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 LLIMMgr::addSession(
+ const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ if (0 == ids.getLength())
+ {
+ return LLUUID::null;
+ }
+
+ LLUUID session_id = computeSessionID(dialog,other_participant_id);
+
+ bool new_session = !LLIMModel::getInstance()->findIMSession(session_id);
+
+ if (new_session)
+ {
+ LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids);
+ }
+
+
+ //*TODO remove this "floater" thing when Communicate Floater's gone
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if(!floater)
+ {
+ // On creation, use the first element of ids as the
+ // "other_participant_id"
+ floater = createFloater(
+ session_id,
+ other_participant_id,
+ name,
+ dialog,
+ TRUE,
+ ids);
+ }
+
+ //we don't need to show notes about online/offline, mute/unmute users' statuses for existing sessions
+ if (!new_session) return session_id;
+
+ noteOfflineUsers(session_id, floater, ids);
+
+ // Only warn for regular IMs - not group IMs
+ if( dialog == IM_NOTHING_SPECIAL )
+ {
+ noteMutedUsers(session_id, floater, ids);
+ }
+
+ return session_id;
+}
+
+bool LLIMMgr::leaveSession(const LLUUID& session_id)
+{
+ LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (!im_session) return false;
+
+ LLIMModel::getInstance()->sendLeaveSession(session_id, im_session->mOtherParticipantID);
+ gIMMgr->removeSession(session_id);
+ return true;
+}
+
+// Removes data associated with a particular session specified by session_id
+void LLIMMgr::removeSession(const LLUUID& session_id)
+{
+ llassert_always(hasSession(session_id));
+
+ //*TODO remove this floater thing when Communicate Floater is being deleted (IB)
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if(floater)
+ {
+ mFloaters.erase(floater->getHandle());
+ LLFloaterChatterBox::getInstance()->removeFloater(floater);
+ }
+
+ clearPendingInvitation(session_id);
+ clearPendingAgentListUpdates(session_id);
+
+ LLIMModel::getInstance()->clearSession(session_id);
+
+ notifyObserverSessionRemoved(session_id);
+}
+
+void LLIMMgr::inviteToSession(
+ const LLUUID& session_id,
+ const std::string& session_name,
+ const LLUUID& caller_id,
+ const std::string& caller_name,
+ EInstantMessage type,
+ EInvitationType inv_type,
+ const std::string& session_handle,
+ const std::string& session_uri)
+{
+ //ignore invites from muted residents
+ if (LLMuteList::getInstance()->isMuted(caller_id))
+ {
+ return;
+ }
+
+ std::string notify_box_type;
+
+ BOOL ad_hoc_invite = FALSE;
+ if(type == IM_SESSION_P2P_INVITE)
+ {
+ //P2P is different...they only have voice invitations
+ notify_box_type = "VoiceInviteP2P";
+ }
+ else if ( gAgent.isInGroup(session_id) )
+ {
+ //only really old school groups have voice invitations
+ notify_box_type = "VoiceInviteGroup";
+ }
+ else if ( inv_type == INVITATION_TYPE_VOICE )
+ {
+ //else it's an ad-hoc
+ //and a voice ad-hoc
+ notify_box_type = "VoiceInviteAdHoc";
+ ad_hoc_invite = TRUE;
+ }
+ else if ( inv_type == INVITATION_TYPE_IMMEDIATE )
+ {
+ notify_box_type = "InviteAdHoc";
+ ad_hoc_invite = TRUE;
+ }
+
+ LLSD payload;
+ payload["session_id"] = session_id;
+ payload["session_name"] = session_name;
+ payload["caller_id"] = caller_id;
+ payload["caller_name"] = caller_name;
+ payload["type"] = type;
+ payload["inv_type"] = inv_type;
+ payload["session_handle"] = session_handle;
+ payload["session_uri"] = session_uri;
+ payload["notify_box_type"] = notify_box_type;
+
+ LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id);
+ if (channelp && channelp->callStarted())
+ {
+ // you have already started a call to the other user, so just accept the invite
+ LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 0);
+ return;
+ }
+
+ if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite)
+ {
+ // is the inviter a friend?
+ if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)
+ {
+ // if not, and we are ignoring voice invites from non-friends
+ // then silently decline
+ if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly"))
+ {
+ // invite not from a friend, so decline
+ LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 1);
+ return;
+ }
+ }
+ }
+
+ if ( !mPendingInvitations.has(session_id.asString()) )
+ {
+ if (caller_name.empty())
+ {
+ gCacheName->get(caller_id, FALSE, boost::bind(&LLIMMgr::onInviteNameLookup, payload, _1, _2, _3, _4));
+ }
+ else
+ {
+ if (notify_box_type == "VoiceInviteP2P" || notify_box_type == "VoiceInviteAdHoc")
+ {
+ LLFloaterReg::showInstance("incoming_call", payload, TRUE);
+ }
+ else
+ {
+ LLSD args;
+ args["NAME"] = caller_name;
+ args["GROUP"] = session_name;
+
+ LLNotifications::instance().add(notify_box_type, args, payload, &inviteUserResponse);
+ }
+ }
+ mPendingInvitations[session_id.asString()] = LLSD();
+ }
+}
+
+void LLIMMgr::onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group)
+{
+ payload["caller_name"] = first + " " + last;
+ payload["session_name"] = payload["caller_name"].asString();
+
+ std::string notify_box_type = payload["notify_box_type"].asString();
+
+ if (notify_box_type == "VoiceInviteP2P" || notify_box_type == "VoiceInviteAdHoc")
+ {
+ LLFloaterReg::showInstance("incoming_call", payload, TRUE);
+ }
+ else
+ {
+ LLSD args;
+ args["NAME"] = payload["caller_name"].asString();
+
+ LLNotifications::instance().add(
+ payload["notify_box_type"].asString(),
+ args,
+ payload,
+ &inviteUserResponse);
+ }
+}
+
+void LLIMMgr::disconnectAllSessions()
+{
+ LLFloaterIMPanel* floater = NULL;
+ std::set<LLHandle<LLFloater> >::iterator handle_it;
+ for(handle_it = mFloaters.begin();
+ handle_it != mFloaters.end();
+ )
+ {
+ floater = (LLFloaterIMPanel*)handle_it->get();
+
+ // MUST do this BEFORE calling floater->onClose() because that may remove the item from the set, causing the subsequent increment to crash.
+ ++handle_it;
+
+ if (floater)
+ {
+ floater->setEnabled(FALSE);
+ floater->closeFloater(TRUE);
+ }
+ }
+}
+
+
+// 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* LLIMMgr::findFloaterBySession(const LLUUID& session_id)
+{
+ LLFloaterIMPanel* rv = NULL;
+ std::set<LLHandle<LLFloater> >::iterator handle_it;
+ for(handle_it = mFloaters.begin();
+ handle_it != mFloaters.end();
+ ++handle_it)
+ {
+ rv = (LLFloaterIMPanel*)handle_it->get();
+ if(rv && session_id == rv->getSessionID())
+ {
+ break;
+ }
+ rv = NULL;
+ }
+ return rv;
+}
+
+
+BOOL LLIMMgr::hasSession(const LLUUID& session_id)
+{
+ return LLIMModel::getInstance()->findIMSession(session_id) != NULL;
+}
+
+void LLIMMgr::clearPendingInvitation(const LLUUID& session_id)
+{
+ if ( mPendingInvitations.has(session_id.asString()) )
+ {
+ mPendingInvitations.erase(session_id.asString());
+ }
+}
+
+void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body)
+{
+ LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ if ( im_floater )
+ {
+ im_floater->processAgentListUpdates(body);
+ }
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
+ if (speaker_mgr)
+ {
+ speaker_mgr->updateSpeakers(body);
+ }
+ else
+ {
+ //we don't have a speaker manager yet..something went wrong
+ //we are probably receiving an update here before
+ //a start or an acceptance of an invitation. Race condition.
+ gIMMgr->addPendingAgentListUpdates(
+ session_id,
+ body);
+ }
+}
+
+LLSD LLIMMgr::getPendingAgentListUpdates(const LLUUID& session_id)
+{
+ if ( mPendingAgentListUpdates.has(session_id.asString()) )
+ {
+ return mPendingAgentListUpdates[session_id.asString()];
+ }
+ else
+ {
+ return LLSD();
+ }
+}
+
+void LLIMMgr::addPendingAgentListUpdates(
+ const LLUUID& session_id,
+ const LLSD& updates)
+{
+ LLSD::map_const_iterator iter;
+
+ if ( !mPendingAgentListUpdates.has(session_id.asString()) )
+ {
+ //this is a new agent list update for this session
+ mPendingAgentListUpdates[session_id.asString()] = LLSD::emptyMap();
+ }
+
+ if (
+ updates.has("agent_updates") &&
+ updates["agent_updates"].isMap() &&
+ updates.has("updates") &&
+ updates["updates"].isMap() )
+ {
+ //new school update
+ LLSD update_types = LLSD::emptyArray();
+ LLSD::array_iterator array_iter;
+
+ update_types.append("agent_updates");
+ update_types.append("updates");
+
+ for (
+ array_iter = update_types.beginArray();
+ array_iter != update_types.endArray();
+ ++array_iter)
+ {
+ //we only want to include the last update for a given agent
+ for (
+ iter = updates[array_iter->asString()].beginMap();
+ iter != updates[array_iter->asString()].endMap();
+ ++iter)
+ {
+ mPendingAgentListUpdates[session_id.asString()][array_iter->asString()][iter->first] =
+ iter->second;
+ }
+ }
+ }
+ else if (
+ updates.has("updates") &&
+ updates["updates"].isMap() )
+ {
+ //old school update where the SD contained just mappings
+ //of agent_id -> "LEAVE"/"ENTER"
+
+ //only want to keep last update for each agent
+ for (
+ iter = updates["updates"].beginMap();
+ iter != updates["updates"].endMap();
+ ++iter)
+ {
+ mPendingAgentListUpdates[session_id.asString()]["updates"][iter->first] =
+ iter->second;
+ }
+ }
+}
+
+void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id)
+{
+ if ( mPendingAgentListUpdates.has(session_id.asString()) )
+ {
+ mPendingAgentListUpdates.erase(session_id.asString());
+ }
+}
+
+void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
+{
+ for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
+ {
+ (*it)->sessionAdded(session_id, name, other_participant_id);
+ }
+}
+
+void LLIMMgr::notifyObserverSessionRemoved(const LLUUID& session_id)
+{
+ for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
+ {
+ (*it)->sessionRemoved(session_id);
+ }
+}
+
+void LLIMMgr::notifyObserverSessionIDUpdated( const LLUUID& old_session_id, const LLUUID& new_session_id )
+{
+ for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
+ {
+ (*it)->sessionIDUpdated(old_session_id, new_session_id);
+ }
+
+}
+
+void LLIMMgr::addSessionObserver(LLIMSessionObserver *observer)
+{
+ mSessionObservers.push_back(observer);
+}
+
+void LLIMMgr::removeSessionObserver(LLIMSessionObserver *observer)
+{
+ mSessionObservers.remove(observer);
+}
+
+bool LLIMMgr::startCall(const LLUUID& session_id)
+{
+ LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id);
+ if (!voice_channel) return false;
+
+ voice_channel->activate();
+ return true;
+}
+
+bool LLIMMgr::endCall(const LLUUID& session_id)
+{
+ LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id);
+ if (!voice_channel) return false;
+
+ voice_channel->deactivate();
+ return true;
+}
+
+// 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* LLIMMgr::createFloater(
+ const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ const std::string& session_label,
+ EInstantMessage dialog,
+ BOOL user_initiated,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ if (session_id.isNull())
+ {
+ llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
+ }
+
+ llinfos << "LLIMMgr::createFloater: from " << other_participant_id
+ << " in session " << session_id << llendl;
+ LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
+ session_id,
+ other_participant_id,
+ ids,
+ dialog);
+ LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END;
+ LLFloaterChatterBox::getInstance()->addFloater(floater, FALSE, i_pt);
+ mFloaters.insert(floater->getHandle());
+ return floater;
+}
+
+void LLIMMgr::noteOfflineUsers(
+ const LLUUID& session_id,
+ LLFloaterIMPanel* floater,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ S32 count = ids.count();
+ if(count == 0)
+ {
+ const std::string& only_user = LLTrans::getString("only_user_message");
+ if (floater)
+ {
+ floater->addHistoryLine(only_user, LLUIColorTable::instance().getColor("SystemChatColor"));
+ }
+ LLIMModel::getInstance()->addMessage(session_id, SYSTEM_FROM, LLUUID::null, only_user);
+ }
+ else
+ {
+ const LLRelationship* info = NULL;
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLIMModel& im_model = LLIMModel::instance();
+ for(S32 i = 0; i < count; ++i)
+ {
+ info = at.getBuddyInfo(ids.get(i));
+ std::string first, last;
+ if(info && !info->isOnline()
+ && gCacheName->getName(ids.get(i), first, last))
+ {
+ LLUIString offline = LLTrans::getString("offline_message");
+ offline.setArg("[FIRST]", first);
+ offline.setArg("[LAST]", last);
+ im_model.proccessOnlineOfflineNotification(session_id, offline);
+ }
+ }
+ }
+}
+
+void LLIMMgr::noteMutedUsers(const LLUUID& session_id, LLFloaterIMPanel* floater,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ // Don't do this if we don't have a mute list.
+ LLMuteList *ml = LLMuteList::getInstance();
+ if( !ml )
+ {
+ return;
+ }
+
+ S32 count = ids.count();
+ if(count > 0)
+ {
+ LLIMModel* im_model = LLIMModel::getInstance();
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ if( ml->isMuted(ids.get(i)) )
+ {
+ LLUIString muted = LLTrans::getString("muted_message");
+
+ //*TODO remove this "floater" thing when Communicate Floater's gone
+ floater->addHistoryLine(muted);
+
+ im_model->addMessage(session_id, SYSTEM_FROM, LLUUID::null, muted);
+ break;
+ }
+ }
+ }
+}
+
+void LLIMMgr::processIMTypingStart(const LLIMInfo* im_info)
+{
+ processIMTypingCore(im_info, TRUE);
+}
+
+void LLIMMgr::processIMTypingStop(const LLIMInfo* im_info)
+{
+ processIMTypingCore(im_info, FALSE);
+}
+
+void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
+{
+ LLUUID session_id = computeSessionID(im_info->mIMType, im_info->mFromID);
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if (floater)
+ {
+ floater->processIMTyping(im_info, typing);
+ }
+
+ LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ if ( im_floater )
+ {
+ im_floater->processIMTyping(im_info, typing);
+ }
+}
+
+class LLViewerChatterBoxSessionStartReply : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Used for receiving a reply to a request to initialize an ChatterBox session");
+ desc.postAPI();
+ desc.input(
+ "{\"client_session_id\": UUID, \"session_id\": UUID, \"success\" boolean, \"reason\": string");
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual void post(ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ LLSD body;
+ LLUUID temp_session_id;
+ LLUUID session_id;
+ bool success;
+
+ body = input["body"];
+ success = body["success"].asBoolean();
+ temp_session_id = body["temp_session_id"].asUUID();
+
+ if ( success )
+ {
+ session_id = body["session_id"].asUUID();
+
+ LLIMModel::getInstance()->processSessionInitializedReply(temp_session_id, session_id);
+
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
+ if (speaker_mgr)
+ {
+ speaker_mgr->setSpeakers(body);
+ speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id));
+ }
+
+ LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id);
+ if (floaterp)
+ {
+ if ( body.has("session_info") )
+ {
+ floaterp->processSessionUpdate(body["session_info"]);
+ }
+ }
+
+ LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ if ( im_floater )
+ {
+ if ( body.has("session_info") )
+ {
+ im_floater->processSessionUpdate(body["session_info"]);
+ }
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ }
+ else
+ {
+ //throw an error dialog and close the temp session's floater
+ gIMMgr->showSessionStartError(body["error"].asString(), temp_session_id);
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ }
+};
+
+class LLViewerChatterBoxSessionEventReply : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Used for receiving a reply to a ChatterBox session event");
+ desc.postAPI();
+ desc.input(
+ "{\"event\": string, \"reason\": string, \"success\": boolean, \"session_id\": UUID");
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual void post(ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ LLUUID session_id;
+ bool success;
+
+ LLSD body = input["body"];
+ success = body["success"].asBoolean();
+ session_id = body["session_id"].asUUID();
+
+ if ( !success )
+ {
+ //throw an error dialog
+ gIMMgr->showSessionEventError(
+ body["event"].asString(),
+ body["error"].asString(),
+ session_id);
+ }
+ }
+};
+
+class LLViewerForceCloseChatterBoxSession: public LLHTTPNode
+{
+public:
+ virtual void post(ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ LLUUID session_id;
+ std::string reason;
+
+ session_id = input["body"]["session_id"].asUUID();
+ reason = input["body"]["reason"].asString();
+
+ gIMMgr->showSessionForceClose(reason, session_id);
+ }
+};
+
+class LLViewerChatterBoxSessionAgentListUpdates : public LLHTTPNode
+{
+public:
+ virtual void post(
+ ResponsePtr responder,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ const LLUUID& session_id = input["body"]["session_id"].asUUID();
+ gIMMgr->processAgentListUpdates(session_id, input["body"]);
+ }
+};
+
+class LLViewerChatterBoxSessionUpdate : public LLHTTPNode
+{
+public:
+ virtual void post(
+ ResponsePtr responder,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ LLUUID session_id = input["body"]["session_id"].asUUID();
+ LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id);
+ if (floaterp)
+ {
+ floaterp->processSessionUpdate(input["body"]["info"]);
+ }
+ LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ if ( im_floater )
+ {
+ im_floater->processSessionUpdate(input["body"]["info"]);
+ }
+ }
+};
+
+
+class LLViewerChatterBoxInvitation : public LLHTTPNode
+{
+public:
+
+ virtual void post(
+ ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ //for backwards compatiblity reasons...we need to still
+ //check for 'text' or 'voice' invitations...bleh
+ if ( input["body"].has("instantmessage") )
+ {
+ LLSD message_params =
+ input["body"]["instantmessage"]["message_params"];
+
+ //do something here to have the IM invite behave
+ //just like a normal IM
+ //this is just replicated code from process_improved_im
+ //and should really go in it's own function -jwolk
+ if (gNoRender)
+ {
+ return;
+ }
+ LLChat chat;
+
+ std::string message = message_params["message"].asString();
+ std::string name = message_params["from_name"].asString();
+ LLUUID from_id = message_params["from_id"].asUUID();
+ LLUUID session_id = message_params["id"].asUUID();
+ std::vector<U8> bin_bucket = message_params["data"]["binary_bucket"].asBinary();
+ U8 offline = (U8)message_params["offline"].asInteger();
+
+ time_t timestamp =
+ (time_t) message_params["timestamp"].asInteger();
+
+ BOOL is_busy = gAgent.getBusy();
+ BOOL is_muted = LLMuteList::getInstance()->isMuted(
+ from_id,
+ name,
+ LLMute::flagTextChat);
+
+ BOOL is_linden = LLMuteList::getInstance()->isLinden(name);
+ std::string separator_string(": ");
+
+ chat.mMuted = is_muted && !is_linden;
+ chat.mFromID = from_id;
+ chat.mFromName = name;
+
+ if (!is_linden && (is_busy || is_muted))
+ {
+ return;
+ }
+
+ // standard message, not from system
+ std::string saved;
+ if(offline == IM_OFFLINE)
+ {
+ saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str());
+ }
+ std::string buffer = saved + message;
+
+ BOOL is_this_agent = FALSE;
+ if(from_id == gAgentID)
+ {
+ is_this_agent = TRUE;
+ }
+ gIMMgr->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ std::string((char*)&bin_bucket[0]),
+ IM_SESSION_INVITE,
+ message_params["parent_estate_id"].asInteger(),
+ message_params["region_id"].asUUID(),
+ ll_vector3_from_sd(message_params["position"]),
+ true);
+
+ chat.mText = std::string("IM: ") + name + separator_string + saved + message;
+ LLFloaterChat::addChat(chat, TRUE, is_this_agent);
+
+ //K now we want to accept the invitation
+ std::string url = gAgent.getRegion()->getCapability(
+ "ChatSessionRequest");
+
+ if ( url != "" )
+ {
+ LLSD data;
+ data["method"] = "accept invitation";
+ data["session-id"] = session_id;
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLViewerChatterBoxInvitationAcceptResponder(
+ session_id,
+ LLIMMgr::INVITATION_TYPE_INSTANT_MESSAGE));
+ }
+ } //end if invitation has instant message
+ else if ( input["body"].has("voice") )
+ {
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if(!LLVoiceClient::voiceEnabled())
+ {
+ // Don't display voice invites unless the user has voice enabled.
+ return;
+ }
+
+ gIMMgr->inviteToSession(
+ input["body"]["session_id"].asUUID(),
+ input["body"]["session_name"].asString(),
+ input["body"]["from_id"].asUUID(),
+ input["body"]["from_name"].asString(),
+ IM_SESSION_INVITE,
+ LLIMMgr::INVITATION_TYPE_VOICE);
+ }
+ else if ( input["body"].has("immediate") )
+ {
+ gIMMgr->inviteToSession(
+ input["body"]["session_id"].asUUID(),
+ input["body"]["session_name"].asString(),
+ input["body"]["from_id"].asUUID(),
+ input["body"]["from_name"].asString(),
+ IM_SESSION_INVITE,
+ LLIMMgr::INVITATION_TYPE_IMMEDIATE);
+ }
+ }
+};
+
+LLHTTPRegistration<LLViewerChatterBoxSessionStartReply>
+ gHTTPRegistrationMessageChatterboxsessionstartreply(
+ "/message/ChatterBoxSessionStartReply");
+
+LLHTTPRegistration<LLViewerChatterBoxSessionEventReply>
+ gHTTPRegistrationMessageChatterboxsessioneventreply(
+ "/message/ChatterBoxSessionEventReply");
+
+LLHTTPRegistration<LLViewerForceCloseChatterBoxSession>
+ gHTTPRegistrationMessageForceclosechatterboxsession(
+ "/message/ForceCloseChatterBoxSession");
+
+LLHTTPRegistration<LLViewerChatterBoxSessionAgentListUpdates>
+ gHTTPRegistrationMessageChatterboxsessionagentlistupdates(
+ "/message/ChatterBoxSessionAgentListUpdates");
+
+LLHTTPRegistration<LLViewerChatterBoxSessionUpdate>
+ gHTTPRegistrationMessageChatterBoxSessionUpdate(
+ "/message/ChatterBoxSessionUpdate");
+
+LLHTTPRegistration<LLViewerChatterBoxInvitation>
+ gHTTPRegistrationMessageChatterBoxInvitation(
+ "/message/ChatterBoxInvitation");
+
diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp index 9ca207a579..f30821cacf 100644 --- a/indra/newview/llmanip.cpp +++ b/indra/newview/llmanip.cpp @@ -265,8 +265,8 @@ BOOL LLManip::getMousePointOnPlaneGlobal(LLVector3d& point, S32 x, S32 y, LLVect if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD) { BOOL result = FALSE; - F32 mouse_x = ((F32)x / gViewerWindow->getWindowWidthScaled() - 0.5f) * LLViewerCamera::getInstance()->getAspect() / gAgent.mHUDCurZoom; - F32 mouse_y = ((F32)y / gViewerWindow->getWindowHeightScaled() - 0.5f) / gAgent.mHUDCurZoom; + F32 mouse_x = ((F32)x / gViewerWindow->getWorldViewWidthScaled() - 0.5f) * LLViewerCamera::getInstance()->getAspect() / gAgent.mHUDCurZoom; + F32 mouse_y = ((F32)y / gViewerWindow->getWorldViewHeightScaled() - 0.5f) / gAgent.mHUDCurZoom; LLVector3 origin_agent = gAgent.getPosAgentFromGlobal(origin); LLVector3 mouse_pos = LLVector3(0.f, -mouse_x, mouse_y); diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp index bcaebb6bbb..8535d52015 100644 --- a/indra/newview/llmaniprotate.cpp +++ b/indra/newview/llmaniprotate.cpp @@ -1107,8 +1107,11 @@ BOOL LLManipRotate::updateVisiblity() mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag; mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm; - mCenterScreen.set((S32)((0.5f - mRotationCenter.mdV[VY]) / gAgent.mHUDCurZoom * gViewerWindow->getWorldViewWidthRaw()), - (S32)((mRotationCenter.mdV[VZ] + 0.5f) / gAgent.mHUDCurZoom * gViewerWindow->getWorldViewHeightRaw())); + // x axis range is (-aspect * 0.5f, +aspect * 0.5) + // y axis range is (-0.5, 0.5) + // so use getWorldViewHeightRaw as scale factor when converting to pixel coordinates + mCenterScreen.set((S32)((0.5f - center.mV[VY]) / gAgent.mHUDCurZoom * gViewerWindow->getWorldViewHeightScaled()), + (S32)((center.mV[VZ] + 0.5f) / gAgent.mHUDCurZoom * gViewerWindow->getWorldViewHeightScaled())); visible = TRUE; } else @@ -1624,8 +1627,8 @@ void LLManipRotate::mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_ { if (LLSelectMgr::getInstance()->getSelection()->getSelectType() == SELECT_TYPE_HUD) { - F32 mouse_x = (((F32)x / gViewerWindow->getWorldViewWidthRaw()) - 0.5f) / gAgent.mHUDCurZoom; - F32 mouse_y = ((((F32)y) / gViewerWindow->getWorldViewHeightRaw()) - 0.5f) / gAgent.mHUDCurZoom; + F32 mouse_x = (((F32)x / gViewerWindow->getWorldViewRectScaled().getWidth()) - 0.5f) / gAgent.mHUDCurZoom; + F32 mouse_y = ((((F32)y) / gViewerWindow->getWorldViewRectScaled().getHeight()) - 0.5f) / gAgent.mHUDCurZoom; *ray_pt = LLVector3(-1.f, -mouse_x, mouse_y); *ray_dir = LLVector3(1.f, 0.f, 0.f); @@ -1699,7 +1702,7 @@ void LLManipRotate::highlightManipulators( S32 x, S32 y ) F32 dist_y = mouse_dir_y.normVec(); F32 dist_z = mouse_dir_z.normVec(); - F32 distance_threshold = (MAX_MANIP_SELECT_DISTANCE * mRadiusMeters) / gViewerWindow->getWorldViewHeightRaw(); + F32 distance_threshold = (MAX_MANIP_SELECT_DISTANCE * mRadiusMeters) / gViewerWindow->getWorldViewHeightScaled(); if (llabs(dist_x - mRadiusMeters) * llmax(0.05f, proj_rot_x_axis) < distance_threshold) { diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 90c009887d..5cd40273f6 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -102,17 +102,17 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : setCaretColor( (unsigned int)color.mV[0], (unsigned int)color.mV[1], (unsigned int)color.mV[2] ); } - setIgnoreUIScale(p.ignore_ui_scale()); + setIgnoreUIScale(p.ignore_ui_scale); - setHomePageUrl(p.start_url()); + setHomePageUrl(p.start_url); - setBorderVisible(p.border_visible()); + setBorderVisible(p.border_visible); - mHideLoading = p.hide_loading(); + mHideLoading = p.hide_loading; - setDecoupleTextureSize(p.decouple_texture_size()); + setDecoupleTextureSize(p.decouple_texture_size); - setTextureSize(p.texture_width(), p.texture_height()); + setTextureSize(p.texture_width, p.texture_height); if(!getDecoupleTextureSize()) { @@ -735,13 +735,13 @@ void LLMediaCtrl::draw() { // max width, adjusted height width = r.getWidth(); - height = llmin(llmax(S32(width / media_aspect), 0), r.getHeight()); + height = llmin(llmax(llround(width / media_aspect), 0), r.getHeight()); } else { // max height, adjusted width height = r.getHeight(); - width = llmin(llmax(S32(height * media_aspect), 0), r.getWidth()); + width = llmin(llmax(llround(height * media_aspect), 0), r.getWidth()); } } else @@ -759,6 +759,14 @@ void LLMediaCtrl::draw() x_offset = (r.getWidth() - width) / 2; y_offset = (r.getHeight() - height) / 2; + if(mIgnoreUIScale) + { + x_offset = llround((F32)x_offset * LLUI::sGLScaleFactor.mV[VX]); + y_offset = llround((F32)y_offset * LLUI::sGLScaleFactor.mV[VY]); + width = llround((F32)width * LLUI::sGLScaleFactor.mV[VX]); + height = llround((F32)height * LLUI::sGLScaleFactor.mV[VY]); + } + // draw the browser gGL.setSceneBlendType(LLRender::BT_REPLACE); gGL.begin( LLRender::QUADS ); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 1f984e3af8..09fd9b2949 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -222,5 +222,5 @@ void LLNearbyChat::setRect (const LLRect &rect) void LLNearbyChat::getAllowedRect(LLRect& rect) { - rect = gViewerWindow->getWorldViewRectRaw(); + rect = gViewerWindow->getWorldViewRectScaled(); } diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 6145588df2..234fe13217 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -92,7 +92,6 @@ LLNetMap::LLNetMap (const Params & p) mObjectImagep(), mClosestAgentToCursor(), mClosestAgentAtLastRightClick(), - mRotateMap(FALSE), mToolTipMsg() { mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS); @@ -175,7 +174,8 @@ void LLNetMap::draw() gGL.translatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f); - if( mRotateMap ) + static LLUICachedControl<bool> rotate_map("MiniMapRotate", true); + if( rotate_map ) { // rotate subsequent draws to agent rotation rotation = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); @@ -408,7 +408,7 @@ void LLNetMap::draw() gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - if( mRotateMap ) + if( rotate_map ) { gGL.color4fv((map_frustum_color()).mV); @@ -454,7 +454,8 @@ LLVector3 LLNetMap::globalPosToView( const LLVector3d& global_pos ) pos_local.mV[VY] *= mPixelsPerMeter; // leave Z component in meters - if( mRotateMap ) + static LLUICachedControl<bool> rotate_map("MiniMapRotate", true); + if( rotate_map ) { F32 radians = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); @@ -502,7 +503,8 @@ LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y ) F32 radians = - atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); - if( mRotateMap ) + static LLUICachedControl<bool> rotate_map("MiniMapRotate", true); + if( rotate_map ) { LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); pos_local.rotVec( rot ); diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h index 7088ab3e70..3d7f3233ac 100644 --- a/indra/newview/llnetmap.h +++ b/indra/newview/llnetmap.h @@ -80,9 +80,7 @@ public: /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); void setScale( F32 scale ); - void setRotateMap( BOOL b ) { mRotateMap = b; } void setToolTipMsg(const std::string& msg) { mToolTipMsg = msg; } - BOOL getRotateMap( ) { return mRotateMap; } void renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius ); private: @@ -122,7 +120,6 @@ private: LLUUID mClosestAgentToCursor; LLUUID mClosestAgentAtLastRightClick; - BOOL mRotateMap; std::string mToolTipMsg; }; diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index 1f68c76bfc..5c11bc7310 100644 --- a/indra/newview/llnotificationalerthandler.cpp +++ b/indra/newview/llnotificationalerthandler.cpp @@ -66,7 +66,7 @@ LLAlertHandler::~LLAlertHandler() //-------------------------------------------------------------------------- void LLAlertHandler::initChannel() { - S32 channel_right_bound = gViewerWindow->getWorldViewRectRaw().getWidth() / 2; + S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().getWidth() / 2; mChannel->init(channel_right_bound, channel_right_bound); } diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp index 26730e1f10..b7466ec6d4 100644 --- a/indra/newview/llnotificationgrouphandler.cpp +++ b/indra/newview/llnotificationgrouphandler.cpp @@ -61,7 +61,7 @@ LLGroupHandler::~LLGroupHandler() //-------------------------------------------------------------------------- void LLGroupHandler::initChannel() { - S32 channel_right_bound = gViewerWindow->getWorldViewRectRaw().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mChannel->init(channel_right_bound - channel_width, channel_right_bound); } diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index 94e733913d..598d021703 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -65,7 +65,7 @@ LLOfferHandler::~LLOfferHandler() //-------------------------------------------------------------------------- void LLOfferHandler::initChannel() { - S32 channel_right_bound = gViewerWindow->getWorldViewRectRaw().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mChannel->init(channel_right_bound - channel_width, channel_right_bound); } diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp index 70b86e8b97..4be98201d1 100644 --- a/indra/newview/llnotificationscripthandler.cpp +++ b/indra/newview/llnotificationscripthandler.cpp @@ -64,7 +64,7 @@ LLScriptHandler::~LLScriptHandler() //-------------------------------------------------------------------------- void LLScriptHandler::initChannel() { - S32 channel_right_bound = gViewerWindow->getWorldViewRectRaw().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mChannel->init(channel_right_bound - channel_width, channel_right_bound); } diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index 60a27d5154..b962fa2184 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -60,7 +60,7 @@ LLTipHandler::~LLTipHandler() //-------------------------------------------------------------------------- void LLTipHandler::initChannel() { - S32 channel_right_bound = gViewerWindow->getWorldViewRectRaw().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mChannel->init(channel_right_bound - channel_width, channel_right_bound); } diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 1c2875bf46..a1d54367c9 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -213,6 +213,7 @@ void LLPanelGroupGeneral::setupCtrls(LLPanel* panel_group) } mFounderName = panel_group->getChild<LLNameBox>("founder_name"); mGroupNameEditor = panel_group->getChild<LLLineEditor>("group_name_editor"); + mGroupNameEditor->setPrevalidate( LLLineEditor::prevalidateASCII ); } // static diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index b3e14eb2fb..78f3469f0e 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -210,8 +210,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, } #if !USE_VIEWER_AUTH - childSetPrevalidate("first_name_edit", LLLineEditor::prevalidatePrintableNoSpace); - childSetPrevalidate("last_name_edit", LLLineEditor::prevalidatePrintableNoSpace); + childSetPrevalidate("first_name_edit", LLLineEditor::prevalidateASCIIPrintableNoSpace); + childSetPrevalidate("last_name_edit", LLLineEditor::prevalidateASCIIPrintableNoSpace); childSetCommitCallback("password_edit", mungePassword, this); getChild<LLLineEditor>("password_edit")->setKeystrokeCallback(onPassKey, this); diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 1051326e72..0dc010e2e7 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -80,9 +80,9 @@ LLPanelPermissions::LLPanelPermissions() : BOOL LLPanelPermissions::postBuild() { childSetCommitCallback("Object Name",LLPanelPermissions::onCommitName,this); - childSetPrevalidate("Object Name",LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("Object Name",LLLineEditor::prevalidateASCIIPrintableNoPipe); childSetCommitCallback("Object Description",LLPanelPermissions::onCommitDesc,this); - childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("Object Description",LLLineEditor::prevalidateASCIIPrintableNoPipe); getChild<LLUICtrl>("button set group")->setCommitCallback(boost::bind(&LLPanelPermissions::onClickGroup,this)); diff --git a/indra/newview/llpreviewanim.cpp b/indra/newview/llpreviewanim.cpp index 604faf8eb4..92bd4dc62b 100644 --- a/indra/newview/llpreviewanim.cpp +++ b/indra/newview/llpreviewanim.cpp @@ -79,7 +79,7 @@ BOOL LLPreviewAnim::postBuild() childSetAction("Anim audition btn",auditionAnim, this); childSetCommitCallback("desc", LLPreview::onText, this); - childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("desc", &LLLineEditor::prevalidateASCIIPrintableNoPipe); return LLPreview::postBuild(); } diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 7b3a20d102..49a2a3723d 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -493,7 +493,7 @@ BOOL LLPreviewGesture::postBuild() { childSetCommitCallback("desc", LLPreview::onText, this); childSetText("desc", item->getDescription()); - childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("desc", &LLLineEditor::prevalidateASCIIPrintableNoPipe); } return LLPreview::postBuild(); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index ab9cfbf850..ce81077d80 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -96,7 +96,7 @@ BOOL LLPreviewNotecard::postBuild() childSetCommitCallback("desc", LLPreview::onText, this); if (item) childSetText("desc", item->getDescription()); - childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("desc", &LLLineEditor::prevalidateASCIIPrintableNoPipe); return LLPreview::postBuild(); } diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 2382befcfa..4e4711f8fb 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -956,7 +956,7 @@ BOOL LLPreviewLSL::postBuild() childSetCommitCallback("desc", LLPreview::onText, this); childSetText("desc", item->getDescription()); - childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("desc", &LLLineEditor::prevalidateASCIIPrintableNoPipe); return LLPreview::postBuild(); } diff --git a/indra/newview/llpreviewsound.cpp b/indra/newview/llpreviewsound.cpp index 7659c50ed3..d7fd252fb6 100644 --- a/indra/newview/llpreviewsound.cpp +++ b/indra/newview/llpreviewsound.cpp @@ -75,7 +75,7 @@ BOOL LLPreviewSound::postBuild() button->setSoundFlags(LLView::SILENT); childSetCommitCallback("desc", LLPreview::onText, this); - childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("desc", &LLLineEditor::prevalidateASCIIPrintableNoPipe); return LLPreview::postBuild(); } diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 13d02b7dec..41cf402d6f 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -152,7 +152,7 @@ BOOL LLPreviewTexture::postBuild() { childSetCommitCallback("desc", LLPreview::onText, this); childSetText("desc", item->getDescription()); - childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("desc", &LLLineEditor::prevalidateASCIIPrintableNoPipe); } } diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index 81eb133b07..d1cf4a0810 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -103,8 +103,8 @@ void LLScreenChannelBase::updatePositionAndSize(LLRect old_world_rect, LLRect ne void LLScreenChannelBase::init(S32 channel_left, S32 channel_right) { - S32 channel_top = gViewerWindow->getWorldViewRectRaw().getHeight(); - S32 channel_bottom = gViewerWindow->getWorldViewRectRaw().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin"); + S32 channel_top = gViewerWindow->getWorldViewRectScaled().getHeight(); + S32 channel_bottom = gViewerWindow->getWorldViewRectScaled().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin"); setRect(LLRect(channel_left, channel_top, channel_right, channel_bottom)); setVisible(TRUE); } @@ -710,7 +710,7 @@ void LLScreenChannel::updateShowToastsState() // for Message Well floater showed in a docked state - adjust channel's height if(dynamic_cast<LLSysWellWindow*>(floater)) { - S32 channel_bottom = gViewerWindow->getWorldViewRectRaw().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin");; + S32 channel_bottom = gViewerWindow->getWorldViewRectScaled().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin");; LLRect this_rect = getRect(); if(floater->getVisible() && floater->isDocked()) { diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index a3efea7b7e..ff6e2d7363 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -111,9 +111,9 @@ BOOL LLSidepanelItemInfo::postBuild() // build the UI
// item name & description
- childSetPrevalidate("LabelItemName",&LLLineEditor::prevalidatePrintableNotPipe);
+ childSetPrevalidate("LabelItemName",&LLLineEditor::prevalidateASCIIPrintableNoPipe);
//getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onCommitName,this));
- childSetPrevalidate("LabelItemDesc",&LLLineEditor::prevalidatePrintableNotPipe);
+ childSetPrevalidate("LabelItemDesc",&LLLineEditor::prevalidateASCIIPrintableNoPipe);
//getChild<LLUICtrl>("LabelItemDesc")->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this));
// Creator information
diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp index 01c832d7d5..4396cce545 100644 --- a/indra/newview/llsidepaneltaskinfo.cpp +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -100,8 +100,8 @@ BOOL LLSidepanelTaskInfo::postBuild() mBuyBtn = getChild<LLButton>("buy_btn"); mBuyBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onBuyButtonClicked, this)); - childSetPrevalidate("Object Name",LLLineEditor::prevalidatePrintableNotPipe); - childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("Object Name",LLLineEditor::prevalidateASCIIPrintableNoPipe); + childSetPrevalidate("Object Description",LLLineEditor::prevalidateASCIIPrintableNoPipe); // getChild<LLUICtrl>("button set group")->setCommitCallback(boost::bind(&LLSidepanelTaskInfo::onClickGroup,this)); // childSetAction("button deed",LLSidepanelTaskInfo::onClickDeedToGroup,this); diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 04ecf769d5..539536b527 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -238,7 +238,7 @@ void LLSysWellWindow::initChannel() //--------------------------------------------------------------------------------- void LLSysWellWindow::getAllowedRect(LLRect& rect) { - rect = gViewerWindow->getWorldViewRectRaw(); + rect = gViewerWindow->getWorldViewRectScaled(); } //--------------------------------------------------------------------------------- diff --git a/indra/newview/lltoolfocus.cpp b/indra/newview/lltoolfocus.cpp index e2ccc05e30..2320ae57df 100644 --- a/indra/newview/lltoolfocus.cpp +++ b/indra/newview/lltoolfocus.cpp @@ -365,7 +365,7 @@ BOOL LLToolCamera::handleHover(S32 x, S32 y, MASK mask) // Orbit tool if (hasMouseCapture()) { - const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWorldViewWidthRaw(); + const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWorldViewWidthScaled(); if (dx != 0) { @@ -393,7 +393,7 @@ BOOL LLToolCamera::handleHover(S32 x, S32 y, MASK mask) F32 dist = (F32) camera_to_focus.normVec(); // Fudge factor for pan - F32 meters_per_pixel = 3.f * dist / gViewerWindow->getWorldViewWidthRaw(); + F32 meters_per_pixel = 3.f * dist / gViewerWindow->getWorldViewWidthScaled(); if (dx != 0) { @@ -415,7 +415,7 @@ BOOL LLToolCamera::handleHover(S32 x, S32 y, MASK mask) if (hasMouseCapture()) { - const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWorldViewWidthRaw(); + const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWorldViewWidthScaled(); if (dx != 0) { diff --git a/indra/newview/lltoolgrab.cpp b/indra/newview/lltoolgrab.cpp index 44fb6e9271..26dbe6a489 100644 --- a/indra/newview/lltoolgrab.cpp +++ b/indra/newview/lltoolgrab.cpp @@ -510,8 +510,8 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask) const F32 RADIANS_PER_PIXEL_X = 0.01f; const F32 RADIANS_PER_PIXEL_Y = 0.01f; - S32 dx = x - (gViewerWindow->getWorldViewWidthRaw() / 2); - S32 dy = y - (gViewerWindow->getWorldViewHeightRaw() / 2); + S32 dx = x - (gViewerWindow->getWorldViewWidthScaled() / 2); + S32 dy = y - (gViewerWindow->getWorldViewHeightScaled() / 2); if (dx != 0 || dy != 0) { @@ -631,10 +631,10 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask) // Handle auto-rotation at screen edge. LLVector3 grab_pos_agent = gAgent.getPosAgentFromGlobal( grab_point_global ); - LLCoordGL grab_center_gl( gViewerWindow->getWorldViewWidthRaw() / 2, gViewerWindow->getWorldViewHeightRaw() / 2); + LLCoordGL grab_center_gl( gViewerWindow->getWorldViewWidthScaled() / 2, gViewerWindow->getWorldViewHeightScaled() / 2); LLViewerCamera::getInstance()->projectPosAgentToScreen(grab_pos_agent, grab_center_gl); - const S32 ROTATE_H_MARGIN = gViewerWindow->getWorldViewWidthRaw() / 20; + const S32 ROTATE_H_MARGIN = gViewerWindow->getWorldViewWidthScaled() / 20; const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD; const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped; // ...build mode moves camera about focus point @@ -649,7 +649,7 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask) gAgent.cameraOrbitAround(rotate_angle); } } - else if (grab_center_gl.mX > gViewerWindow->getWorldViewWidthRaw() - ROTATE_H_MARGIN) + else if (grab_center_gl.mX > gViewerWindow->getWorldViewWidthScaled() - ROTATE_H_MARGIN) { if (gAgent.getFocusOnAvatar()) { @@ -662,7 +662,7 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask) } // Don't move above top of screen or below bottom - if ((grab_center_gl.mY < gViewerWindow->getWorldViewHeightRaw() - 6) + if ((grab_center_gl.mY < gViewerWindow->getWorldViewHeightScaled() - 6) && (grab_center_gl.mY > 24)) { // Transmit update to simulator @@ -893,7 +893,7 @@ void LLToolGrab::handleHoverInactive(S32 x, S32 y, MASK mask) gAgent.yaw(rotate_angle); //gAgent.setControlFlags(AGENT_CONTROL_YAW_POS); } - else if (x == (gViewerWindow->getWorldViewWidthRaw() - 1) ) + else if (x == (gViewerWindow->getWorldViewWidthScaled() - 1) ) { gAgent.yaw(-rotate_angle); //gAgent.setControlFlags(AGENT_CONTROL_YAW_NEG); diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 49506db173..e7f904023a 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -86,16 +86,6 @@ void init_audio() gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectDelete"))); gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectRezIn"))); gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectRezOut"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuAppear"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuHide"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight0"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight1"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight2"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight3"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight4"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight5"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight6"))); - gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndPieMenuSliceHighlight7"))); gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndSnapshot"))); //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStartAutopilot"))); //gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStartFollowpilot"))); @@ -231,9 +221,9 @@ void audio_update_wind(bool force_update) } } // this line rotates the wind vector to be listener (agent) relative - // unfortunately we have to pre-translate to undo the translation that - // occurs in the transform call - gRelativeWindVec = gAgent.getFrameAgent().rotateToLocal(gWindVec - gAgent.getVelocity()); + // Only use the agent's motion to compute wind noise, otherwise the world + // feels desolate on login when you are standing still. + gRelativeWindVec = gAgent.getFrameAgent().rotateToLocal( -gAgent.getVelocity() ); // don't use the setter setMaxWindGain() because we don't // want to screw up the fade-in on startup by setting actual source gain diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp index 5566fea89f..f3c64088c9 100644 --- a/indra/newview/llviewercamera.cpp +++ b/indra/newview/llviewercamera.cpp @@ -485,7 +485,7 @@ BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoord y /= gViewerWindow->getDisplayScale().mV[VY]; // should now have the x,y coords of grab_point in screen space - LLRect world_view_rect = gViewerWindow->getWorldViewRectScaled(); + LLRect world_rect = gViewerWindow->getWorldViewRectScaled(); // convert to pixel coordinates S32 int_x = lltrunc(x); @@ -495,14 +495,14 @@ BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoord if (clamp) { - if (int_x < world_view_rect.mLeft) + if (int_x < world_rect.mLeft) { - out_point.mX = world_view_rect.mLeft; + out_point.mX = world_rect.mLeft; valid = FALSE; } - else if (int_x > world_view_rect.mRight) + else if (int_x > world_rect.mRight) { - out_point.mX = world_view_rect.mRight; + out_point.mX = world_rect.mRight; valid = FALSE; } else @@ -510,14 +510,14 @@ BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoord out_point.mX = int_x; } - if (int_y < world_view_rect.mBottom) + if (int_y < world_rect.mBottom) { - out_point.mY = world_view_rect.mBottom; + out_point.mY = world_rect.mBottom; valid = FALSE; } - else if (int_y > world_view_rect.mTop) + else if (int_y > world_rect.mTop) { - out_point.mY = world_view_rect.mTop; + out_point.mY = world_rect.mTop; valid = FALSE; } else @@ -531,19 +531,19 @@ BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoord out_point.mX = int_x; out_point.mY = int_y; - if (int_x < world_view_rect.mLeft) + if (int_x < world_rect.mLeft) { valid = FALSE; } - else if (int_x > world_view_rect.mRight) + else if (int_x > world_rect.mRight) { valid = FALSE; } - if (int_y < world_view_rect.mBottom) + if (int_y < world_rect.mBottom) { valid = FALSE; } - else if (int_y > world_view_rect.mTop) + else if (int_y > world_rect.mTop) { valid = FALSE; } diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 703a13976c..dbcf563010 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1467,14 +1467,14 @@ struct UIImageDeclaration : public LLInitParam::Block<UIImageDeclaration> Mandatory<std::string> name; Optional<std::string> file_name; Optional<bool> preload; - Optional<LLRect> scale_rect; + Optional<LLRect> scale; Optional<bool> use_mips; UIImageDeclaration() : name("name"), file_name("file_name"), preload("preload", false), - scale_rect("scale"), + scale("scale"), use_mips("use_mips", false) {} }; @@ -1564,7 +1564,7 @@ bool LLUIImageList::initFromFile() { continue; } - preloadUIImage(image_it->name, file_name, image_it->use_mips, image_it->scale_rect); + preloadUIImage(image_it->name, file_name, image_it->use_mips, image_it->scale); } if (cur_pass == PASS_DECODE_NOW && !gSavedSettings.getBOOL("NoPreload")) diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index c21c287fb6..db66faef81 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2278,7 +2278,7 @@ void LLViewerWindow::handleScrollWheel(S32 clicks) // Zoom the camera in and out behavior - if(top_ctrl == 0 && mWorldViewRectRaw.pointInRect(mCurrentMousePoint.mX, mCurrentMousePoint.mY) ) + if(top_ctrl == 0 && getWorldViewRectScaled().pointInRect(mCurrentMousePoint.mX, mCurrentMousePoint.mY) ) gAgent.handleScrollWheel(clicks); return; @@ -2286,8 +2286,8 @@ void LLViewerWindow::handleScrollWheel(S32 clicks) void LLViewerWindow::moveCursorToCenter() { - S32 x = mWorldViewRectRaw.getWidth() / 2; - S32 y = mWorldViewRectRaw.getHeight() / 2; + S32 x = getWorldViewWidthScaled() / 2; + S32 y = getWorldViewHeightScaled() / 2; //on a forced move, all deltas get zeroed out to prevent jumping mCurrentMousePoint.set(x,y); @@ -2858,6 +2858,11 @@ void LLViewerWindow::updateWorldViewRect(bool use_full_window) // clamp to at least a 1x1 rect so we don't try to allocate zero width gl buffers new_world_rect.mTop = llmax(new_world_rect.mTop, new_world_rect.mBottom + 1); new_world_rect.mRight = llmax(new_world_rect.mRight, new_world_rect.mLeft + 1); + + new_world_rect.mLeft = llround((F32)new_world_rect.mLeft * mDisplayScale.mV[VX]); + new_world_rect.mRight = llround((F32)new_world_rect.mRight * mDisplayScale.mV[VX]); + new_world_rect.mBottom = llround((F32)new_world_rect.mBottom * mDisplayScale.mV[VY]); + new_world_rect.mTop = llround((F32)new_world_rect.mTop * mDisplayScale.mV[VY]); } if (mWorldViewRectRaw != new_world_rect) @@ -2869,6 +2874,12 @@ void LLViewerWindow::updateWorldViewRect(bool use_full_window) gResizeScreenTexture = TRUE; LLViewerCamera::getInstance()->setViewHeightInPixels( mWorldViewRectRaw.getHeight() ); LLViewerCamera::getInstance()->setAspect( getWorldViewAspectRatio() ); + + mWorldViewRectScaled = mWorldViewRectRaw; + mWorldViewRectScaled.mLeft = llround((F32)mWorldViewRectScaled.mLeft / mDisplayScale.mV[VX]); + mWorldViewRectScaled.mRight = llround((F32)mWorldViewRectScaled.mRight / mDisplayScale.mV[VX]); + mWorldViewRectScaled.mBottom = llround((F32)mWorldViewRectScaled.mBottom / mDisplayScale.mV[VY]); + mWorldViewRectScaled.mTop = llround((F32)mWorldViewRectScaled.mTop / mDisplayScale.mV[VY]); } } @@ -3140,10 +3151,10 @@ void LLViewerWindow::pickAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback { mPickScreenRegion.setCenterAndSize(x, y_from_bot, PICK_DIAMETER, PICK_DIAMETER); - if (mPickScreenRegion.mLeft < mWorldViewRectRaw.mLeft) mPickScreenRegion.translate(mWorldViewRectRaw.mLeft - mPickScreenRegion.mLeft, 0); - if (mPickScreenRegion.mBottom < mWorldViewRectRaw.mBottom) mPickScreenRegion.translate(0, mWorldViewRectRaw.mBottom - mPickScreenRegion.mBottom); - if (mPickScreenRegion.mRight > mWorldViewRectRaw.mRight ) mPickScreenRegion.translate(mWorldViewRectRaw.mRight - mPickScreenRegion.mRight, 0); - if (mPickScreenRegion.mTop > mWorldViewRectRaw.mTop ) mPickScreenRegion.translate(0, mWorldViewRectRaw.mTop - mPickScreenRegion.mTop); + if (mPickScreenRegion.mLeft < mWorldViewRectScaled.mLeft) mPickScreenRegion.translate(mWorldViewRectScaled.mLeft - mPickScreenRegion.mLeft, 0); + if (mPickScreenRegion.mBottom < mWorldViewRectScaled.mBottom) mPickScreenRegion.translate(0, mWorldViewRectScaled.mBottom - mPickScreenRegion.mBottom); + if (mPickScreenRegion.mRight > mWorldViewRectScaled.mRight ) mPickScreenRegion.translate(mWorldViewRectScaled.mRight - mPickScreenRegion.mRight, 0); + if (mPickScreenRegion.mTop > mWorldViewRectScaled.mTop ) mPickScreenRegion.translate(0, mWorldViewRectScaled.mTop - mPickScreenRegion.mTop); } // set frame buffer region for picking results @@ -3349,11 +3360,11 @@ LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const F32 fov = LLViewerCamera::getInstance()->getView(); // find world view center in scaled ui coordinates - F32 center_x = (F32)getWorldViewRectRaw().getCenterX() / mDisplayScale.mV[VX]; - F32 center_y = (F32)getWorldViewRectRaw().getCenterY() / mDisplayScale.mV[VY]; + F32 center_x = getWorldViewRectScaled().getCenterX(); + F32 center_y = getWorldViewRectScaled().getCenterY(); // calculate pixel distance to screen - F32 distance = ((F32)getWorldViewHeightRaw() / (mDisplayScale.mV[VY] * 2.f)) / (tan(fov / 2.f)); + F32 distance = ((F32)getWorldViewHeightScaled() * 0.5f) / (tan(fov / 2.f)); // calculate click point relative to middle of screen F32 click_x = x - center_x; @@ -3372,11 +3383,11 @@ LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const LLVector3 LLViewerWindow::mousePointHUD(const S32 x, const S32 y) const { // find screen resolution - S32 height = llround((F32)getWorldViewHeightRaw() / mDisplayScale.mV[VY]); + S32 height = getWorldViewHeightScaled(); // find world view center - F32 center_x = (F32)getWorldViewRectRaw().getCenterX() / mDisplayScale.mV[VX]; - F32 center_y = (F32)getWorldViewRectRaw().getCenterY() / mDisplayScale.mV[VY]; + F32 center_x = getWorldViewRectScaled().getCenterX(); + F32 center_y = getWorldViewRectScaled().getCenterY(); // remap with uniform scale (1/height) so that top is -0.5, bottom is +0.5 F32 hud_x = -((F32)x - center_x) / height; @@ -3394,12 +3405,12 @@ LLVector3 LLViewerWindow::mouseDirectionCamera(const S32 x, const S32 y) const F32 fov_width = fov_height * LLViewerCamera::getInstance()->getAspect(); // find screen resolution - S32 height = llround((F32)getWorldViewHeightRaw() / mDisplayScale.mV[VY]); - S32 width = llround((F32)getWorldViewWidthRaw() / mDisplayScale.mV[VX]); + S32 height = getWorldViewHeightScaled(); + S32 width = getWorldViewWidthScaled(); // find world view center - F32 center_x = (F32)getWorldViewRectRaw().getCenterX() / mDisplayScale.mV[VX]; - F32 center_y = (F32)getWorldViewRectRaw().getCenterY() / mDisplayScale.mV[VY]; + F32 center_x = getWorldViewRectScaled().getCenterX(); + F32 center_y = getWorldViewRectScaled().getCenterY(); // calculate click point relative to middle of screen F32 click_x = (((F32)x - center_x) / (F32)width) * fov_width * -1.f; @@ -4166,14 +4177,20 @@ LLRootView* LLViewerWindow::getRootView() const LLRect LLViewerWindow::getWorldViewRectScaled() const { - LLRect world_view_rect = mWorldViewRectRaw; - world_view_rect.mLeft = llround((F32)world_view_rect.mLeft / mDisplayScale.mV[VX]); - world_view_rect.mRight = llround((F32)world_view_rect.mRight / mDisplayScale.mV[VX]); - world_view_rect.mBottom = llround((F32)world_view_rect.mBottom / mDisplayScale.mV[VY]); - world_view_rect.mTop = llround((F32)world_view_rect.mTop / mDisplayScale.mV[VY]); - return world_view_rect; + return mWorldViewRectScaled; +} + +S32 LLViewerWindow::getWorldViewHeightScaled() const +{ + return mWorldViewRectScaled.getHeight(); } +S32 LLViewerWindow::getWorldViewWidthScaled() const +{ + return mWorldViewRectScaled.getWidth(); +} + + S32 LLViewerWindow::getWorldViewHeightRaw() const { return mWorldViewRectRaw.getHeight(); diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index ce71ef6173..517993182b 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -212,6 +212,8 @@ public: // 3D world area in scaled pixels (via UI scale), use for most UI computations LLRect getWorldViewRectScaled() const; + S32 getWorldViewHeightScaled() const; + S32 getWorldViewWidthScaled() const; // 3D world area, in raw unscaled pixels LLRect getWorldViewRectRaw() const { return mWorldViewRectRaw; } @@ -415,6 +417,7 @@ protected: LLRect mWindowRectRaw; // whole window, including UI LLRect mWindowRectScaled; // whole window, scaled by UI size LLRect mWorldViewRectRaw; // area of screen for 3D world + LLRect mWorldViewRectScaled; // area of screen for 3D world scaled by UI size LLRootView* mRootView; // a view of size mWindowRectRaw, containing all child views LLVector2 mDisplayScale; diff --git a/indra/newview/skins/default/textures/icons/MinusItem_Disabled.png b/indra/newview/skins/default/textures/icons/MinusItem_Disabled.png Binary files differnew file mode 100644 index 0000000000..75bd73cef1 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/MinusItem_Disabled.png diff --git a/indra/newview/skins/default/textures/icons/MinusItem_Off.png b/indra/newview/skins/default/textures/icons/MinusItem_Off.png Binary files differnew file mode 100644 index 0000000000..6f285f3546 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/MinusItem_Off.png diff --git a/indra/newview/skins/default/textures/icons/MinusItem_Press.png b/indra/newview/skins/default/textures/icons/MinusItem_Press.png Binary files differnew file mode 100644 index 0000000000..50f65765d5 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/MinusItem_Press.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 8af65b25e9..f4a239be62 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -1,3 +1,33 @@ +<!-- +This file contains metadata about how to load, display, and scale textures for rendering in the UI. +Images do *NOT* have to appear in this file in order to use them as textures in the UI...simply refer +to them by filename (relative to textures directory). +NOTE: if you want to reuse an image file with different metadata, simply create a new texture entry +with the same filename but different name + +<texture + name="MyTexture" (mandatory) + - this is the name you reference the texture by in XUI. For example, <button image_unselected="MyTexture"/> + file_name="images/my_texture.png" (optional) + - this is the path to the actual file asset, relative to the current skins "textures" directory. + If not supplied, the filename will be taken from the texture name itself, "MyTexture" in this case. + NOTE: you need to provide an extension on the filename (".png", ".tga", ".jpg") for us to decode the image properly + preload="true" (optional, false by default) + - If true, we will attempt to load the image before displaying any UI. + If false, we will load in the background after initializing the UI. + use_mips="true" (currently unused) + scale.left="1" + scale.bottom="1" + scale.top="15" + scale.right="31" + - Specifies the segmentation for 9-slice image scaling. Specifically, the pixel offsets from the LOWER LEFT corner + that define the region of the image that is stretched to make the whole image fit in the required space. + In this example, if the source image is 32x16 pixels, we have defined a center region that starts one pixel up + and to the right from the bottom left corner and extends to 31 pixels right and 15 pixels up from the bottom left + corner. The end result is that the image will keep a 1 pixel border all around while stretching to fit the required + region. +--> + <textures version="101"> <!-- Please add new files alphabetically to prevent merge conflicts. JC --> <texture name="Accordion_ArrowClosed_Off" file_name="containers/Accordion_ArrowClosed_Off.png" preload="false" /> @@ -106,7 +136,6 @@ <texture name="DropDown_Press" file_name="widgets/DropDown_Press.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="DropDown_Selected" file_name="widgets/DropDown_Selected.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="DropDown_Off" file_name="widgets/DropDown_Off.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> - <texture name="DropDown_On" file_name="widgets/DropDown_On.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="DropTarget" file_name="widgets/DropTarget.png" preload="false" /> @@ -231,6 +260,10 @@ <texture name="Microphone_Mute" file_name="icons/Microphone_Mute.png" preload="false" /> <texture name="Microphone_On" file_name="icons/Microphone_On.png" preload="false" /> + <texture name="MinusItem_Disabled" file_name="icons/MinusItem_Disabled.png" preload="false" /> + <texture name="MinusItem_Off" file_name="icons/MinusItem_Off.png" preload="false" /> + <texture name="MinusItem_Press" file_name="icons/MinusItem_Press.png" preload="false" /> + <texture name="menu_separator" file_name="navbar/FileMenu_Divider.png" scale.left="4" scale.top="166" scale.right="0" scale.bottom="0" /> <texture name="Move_Fly_Disabled" file_name="bottomtray/Move_Fly_Disabled.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/en/floater_aaa.xml b/indra/newview/skins/default/xui/en/floater_aaa.xml index e4ab533bc5..4bbd561882 100644 --- a/indra/newview/skins/default/xui/en/floater_aaa.xml +++ b/indra/newview/skins/default/xui/en/floater_aaa.xml @@ -5,6 +5,6 @@ name="floater_aaa" can_resize="true" width="1024"> - <string name="Nudge Parabuild">1</string> + <string name="Nudge Parabuild">2</string> <panel filename="main_view.xml" follows="all" width="1024" height="768" top="0"/> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_camera.xml b/indra/newview/skins/default/xui/en/floater_camera.xml index f553184c19..69f9f6a2f8 100644 --- a/indra/newview/skins/default/xui/en/floater_camera.xml +++ b/indra/newview/skins/default/xui/en/floater_camera.xml @@ -87,9 +87,9 @@ <button follows="top|left" height="18" - image_disabled="AddItem_Disabled" - image_selected="AddItem_Press" - image_unselected="AddItem_Off" + image_disabled="MinusItem_Disabled" + image_selected="MinusItem_Press" + image_unselected="MinusItem_Off" layout="topleft" name="zoom_minus_btn" top_pad="0" diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index 4792a1a476..e972cf39bf 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -8,67 +8,71 @@ left="0" name="panel_im" top="0" - can_close="false" + can_close="true" can_dock="true" - can_minimize="false" + can_minimize="true" visible="true" - width="320" + width="350" can_resize="true" min_width="300" min_height="350"> - <layout_stack follows="all" - height="350" - width="300" - layout="topleft" - orientation="horizontal" - name="im_panels" - top="20" - left="0"> + <layout_stack + follows="all" + height="350" + width="300" + layout="topleft" + orientation="horizontal" + name="im_panels" + top="20" + left="0"> <layout_panel name="panel_im_control_panel" layout="topleft" - top_delta="-3" - height="350" follows="left" label="IM Control Panel" auto_resize="false" user_resize="false" /> - <layout_panel height="350" - width="" - left_delta="110" - top="0" - user_resize="false"> - <button height="20" - follows="left|top" - top="0" - image_overlay="TabIcon_Open_Off" - layout="topleft" - width="25" - name="slide_left_btn" /> - <button height="20" - follows="left|top" - top="0" - image_overlay="TabIcon_Close_Off" - width="25" - name="slide_right_btn" /> - <chat_history - length="1" - follows="all" - height="275" - layout="topleft" - name="chat_history" - parse_highlights="true" - allow_html="true" - width="160"> - </chat_history> - <line_editor - follows="left|right" - height="20" - label="To" - layout="topleft" - name="chat_editor" - width="160"> - </line_editor> + <layout_panel + left="0" + top="0" + user_resize="false"> + <button + height="20" + follows="left|top" + top="0" + left="2" + image_overlay="TabIcon_Open_Off" + layout="topleft" + width="25" + name="slide_left_btn" /> + <button + height="20" + follows="left|top" + top="0" + left="2" + image_overlay="TabIcon_Close_Off" + width="25" + name="slide_right_btn" /> + <chat_history + length="1" + font="SansSerifSmall" + follows="left|right|top" + height="280" + name="chat_history" + parse_highlights="true" + allow_html="true" + left="1" + width="180"> + </chat_history> + <line_editor + follows="left|right|top" + font="SansSerifSmall" + height="20" + label="To" + name="chat_editor" + top_pad="1" + width="180"> + </line_editor> </layout_panel> </layout_stack> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_land_holdings.xml b/indra/newview/skins/default/xui/en/floater_land_holdings.xml index 2c04a94613..e3751f2bd9 100644 --- a/indra/newview/skins/default/xui/en/floater_land_holdings.xml +++ b/indra/newview/skins/default/xui/en/floater_land_holdings.xml @@ -42,7 +42,7 @@ </scroll_list> <button height="23" - font="SansSerifBold" + font="SansSerif" label="Teleport" label_selected="Teleport" layout="topleft" @@ -53,7 +53,7 @@ width="80" /> <button height="23" - font="SansSerifBold" + font="SansSerif" label="Map" label_selected="Map" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml index 69b015a727..d8534bfe0b 100644 --- a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml @@ -5,7 +5,7 @@ can_tear_off="false" can_resize="true" can_drag_on_left="false" - can_close="false" + can_close="true" can_dock="true" bevel_style="in" height="300" diff --git a/indra/newview/skins/default/xui/en/floater_perm_prefs.xml b/indra/newview/skins/default/xui/en/floater_perm_prefs.xml index 6faab84159..4909b8988f 100644 --- a/indra/newview/skins/default/xui/en/floater_perm_prefs.xml +++ b/indra/newview/skins/default/xui/en/floater_perm_prefs.xml @@ -22,10 +22,10 @@ height="16" label="Share with group" layout="topleft" - left_delta="-250" + left="10" name="share_with_group" - top_delta="2" - width="106" /> + top="5" + width="150" /> <check_box control_name="EveryoneCopy" height="16" @@ -33,8 +33,8 @@ layout="topleft" left_delta="0" name="everyone_copy" - top_pad="16" - width="130" /> + top_pad="5" + width="150" /> <text type="string" length="1" @@ -43,8 +43,8 @@ layout="topleft" left_delta="0" name="NextOwnerLabel" - top_pad="16" - width="88"> + top_pad="5" + width="150"> Next owner can: </text> <check_box @@ -54,17 +54,17 @@ layout="topleft" left_delta="0" name="next_owner_modify" - top_pad="14" - width="78" /> + top_pad="5" + width="150" /> <check_box control_name="NextOwnerCopy" height="16" label="Copy" layout="topleft" - left_delta="78" + left_delta="0" name="next_owner_copy" - top_delta="0" - width="88" > + top_pad="5" + width="150" > <check_box.commit_callback function="Perms.Copy" /> </check_box> @@ -76,10 +76,10 @@ initial_value="true" label="Resell/Give away" layout="topleft" - left_delta="88" + left_delta="0" name="next_owner_transfer" - top_delta="0" - width="106" /> + top_pad="5" + width="150" /> </panel> <button height="20" diff --git a/indra/newview/skins/default/xui/en/floater_test_line_editor.xml b/indra/newview/skins/default/xui/en/floater_test_line_editor.xml index e017d404c6..0531b52e5a 100644 --- a/indra/newview/skins/default/xui/en/floater_test_line_editor.xml +++ b/indra/newview/skins/default/xui/en/floater_test_line_editor.xml @@ -18,7 +18,18 @@ Enabled line editor </line_editor> <line_editor - enabled="false" + height="20" + layout="topleft" + left_delta="0" + name="ascii_line_editor" + prevalidate_callback="ascii" + tool_tip="ascii line editor" + top_pad="10" + width="200"> + ASCII only line editor + </line_editor> + <line_editor + enabled="false" height="20" layout="topleft" left_delta="0" diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index bfbfe7de7c..a626e1b353 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -811,11 +811,8 @@ Mixed Sale </panel.string> <text - type="string" - length="1" follows="left|top" height="10" - layout="topleft" left="10" name="Name:" top="0" @@ -825,7 +822,6 @@ <line_editor follows="left|top|right" height="19" - layout="topleft" left_pad="0" max_length="63" name="Object Name" @@ -833,11 +829,8 @@ top_delta="0" width="170" /> <text - type="string" - length="1" follows="left|top" height="10" - layout="topleft" left="10" name="Description:" top_pad="3" @@ -847,7 +840,6 @@ <line_editor follows="left|top|right" height="19" - layout="topleft" left_pad="0" max_length="127" name="Object Description" diff --git a/indra/newview/skins/default/xui/en/inspect_object.xml b/indra/newview/skins/default/xui/en/inspect_object.xml index 83570e2528..16f6e49092 100644 --- a/indra/newview/skins/default/xui/en/inspect_object.xml +++ b/indra/newview/skins/default/xui/en/inspect_object.xml @@ -38,14 +38,14 @@ owner [OWNER] width="220" /> <text follows="all" - height="30" + height="33" left="8" name="object_creator" top_pad="0" use_ellipses="true" width="220"> -by Longavatarname Johnsonlongstonnammer -owner James Linden + by secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +owner secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about </text> <!-- *TODO: Replace this icon --> <icon @@ -54,7 +54,7 @@ owner James Linden right="-5" width="16" height="16" - top="50" + top="56" follows="left|top" /> <text @@ -66,7 +66,7 @@ owner James Linden left="5" name="price_text" text_color="white" - top="53" + top="58" font_shadow="none" width="196"> L$300,000 diff --git a/indra/newview/skins/default/xui/en/menu_mini_map.xml b/indra/newview/skins/default/xui/en/menu_mini_map.xml index 5cbbd71bfe..8d0edf018d 100644 --- a/indra/newview/skins/default/xui/en/menu_mini_map.xml +++ b/indra/newview/skins/default/xui/en/menu_mini_map.xml @@ -10,7 +10,6 @@ width="128"> <menu_item_call label="Zoom Close" - layout="topleft" name="Zoom Close"> <menu_item_call.on_click function="Minimap.Zoom" @@ -18,7 +17,6 @@ </menu_item_call> <menu_item_call label="Zoom Medium" - layout="topleft" name="Zoom Medium"> <menu_item_call.on_click function="Minimap.Zoom" @@ -26,29 +24,31 @@ </menu_item_call> <menu_item_call label="Zoom Far" - layout="topleft" name="Zoom Far"> <menu_item_call.on_click function="Minimap.Zoom" parameter="far" /> </menu_item_call> - <menu_item_separator - layout="topleft" - name="Tracking Separator" /> + <menu_item_check + label="Rotate Map" + name="Rotate Map"> + <menu_item_check.on_check + control="MiniMapRotate" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="MiniMapRotate" /> + </menu_item_check> + <menu_item_separator /> <menu_item_call label="Stop Tracking" - layout="topleft" name="Stop Tracking"> <menu_item_call.on_click function="Minimap.Tracker" parameter="task_properties" /> </menu_item_call> - <menu_item_separator - layout="topleft" - name="Tracking Separator" /> + <menu_item_separator /> <menu_item_call label="World Map" - layout="topleft" name="World Map"> <menu_item_call.on_click function="ShowFloater" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 56cb54c975..0d1ed6fc64 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -5747,7 +5747,7 @@ Are you sure you want to delete your teleport history? name="BottomTrayButtonCanNotBeShown" type="alert"> Selected button can not be shown right now. -The button will be shown when there is enough space for it.
+The button will be shown when there is enough space for it. </notification> diff --git a/indra/newview/skins/default/xui/en/panel_activeim_row.xml b/indra/newview/skins/default/xui/en/panel_activeim_row.xml index 38294c907a..3ed91cb48f 100644 --- a/indra/newview/skins/default/xui/en/panel_activeim_row.xml +++ b/indra/newview/skins/default/xui/en/panel_activeim_row.xml @@ -7,8 +7,8 @@ left="0" height="35" width="318" - background_opaque="false"
- background_visible="true"
+ background_opaque="false" + background_visible="true" bg_alpha_color="0.0 0.0 0.0 0.0" > <chiclet_im_p2p name="p2p_chiclet" diff --git a/indra/newview/skins/default/xui/en/panel_chat_header.xml b/indra/newview/skins/default/xui/en/panel_chat_header.xml index 323ee957e7..7a3eae35a9 100644 --- a/indra/newview/skins/default/xui/en/panel_chat_header.xml +++ b/indra/newview/skins/default/xui/en/panel_chat_header.xml @@ -3,13 +3,13 @@ background_visible="true" bevel_style="in" bg_alpha_color="black" - follows="top" + follows="top|left|right" height="24" - label="im_header" - layout="topleft" - name="im_header" - width="300"> - <avatar_icon + label="im_header" + layout="topleft" + name="im_header" + width="300"> + <avatar_icon follows="left" height="18" image_name="Generic_Person" diff --git a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml index 9573904c93..73d843e6dd 100644 --- a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml +++ b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml @@ -38,28 +38,29 @@ label="Teleport" name="teleport_btn" width="100" /> - <button + <!-- <button follows="left|top" height="20" label="Share" name="share_btn" - width="100" /> - <button + width="100" />--> + <!--Removing pay button to save space - will update spec - verified by Erica/Steve --> + <!-- <button follows="left|top" height="20" label="Pay" name="pay_btn" - width="100" /> + width="100" />--> <panel background_visible="true" bg_alpha_color="DkGray2" border="false" - follows="left|top" + top_pad="10" + follows="left|bottom" height="70" - left="0" - left_pad="0" + left="1" name="panel_call_buttons" - width="110"> + width="109"> <button bottom="10" height="20" diff --git a/indra/newview/skins/default/xui/en/panel_picks.xml b/indra/newview/skins/default/xui/en/panel_picks.xml index 962dad3363..5cefe3e4ab 100644 --- a/indra/newview/skins/default/xui/en/panel_picks.xml +++ b/indra/newview/skins/default/xui/en/panel_picks.xml @@ -131,7 +131,7 @@ <button enabled="false" follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Info" layout="topleft" @@ -143,7 +143,7 @@ <button enabled="false" follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Teleport" layout="topleft" @@ -155,7 +155,7 @@ <button enabled="false" follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Map" layout="topleft" @@ -167,7 +167,7 @@ <button enabled="false" follows="bottom|right" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="▼" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml index 8fc78c6701..b1afe76500 100644 --- a/indra/newview/skins/default/xui/en/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml @@ -48,38 +48,40 @@ image_unselected="BuyArrow_Off" image_pressed="BuyArrow_Press" height="16" - left="-245" + left="-200" name="buycurrency" - pad_right="22px" + pad_right="20px" tool_tip="My Balance: Click to buy more L$" top="1" - width="117" /> - <button - follows="right|bottom" - height="16" - image_selected="parcel_drk_VoiceNo" - image_unselected="parcel_drk_Voice" - is_toggle="true" - left_pad="15" - top="1" - name="volume_btn" - width="16" /> + width="90" /> <text type="string" length="1" + font="SansSerifSmall" text_readonly_color="TimeTextColor" follows="right|bottom" halign="right" height="16" top="5" layout="topleft" - left_pad="7" + left_pad="-5" name="TimeText" text_color="TimeTextColor" tool_tip="Current time (Pacific)" width="80"> 12:00 AM </text> + <button + follows="right|bottom" + height="16" + image_selected="parcel_drk_VoiceNo" + image_unselected="parcel_drk_Voice" + is_toggle="true" + left_pad="5" + top="1" + name="volume_btn" + tool_tip="Global Volume Control" + width="16" /> <text enabled="true" follows="right|bottom" diff --git a/indra/newview/skins/default/xui/en/panel_sys_well_item.xml b/indra/newview/skins/default/xui/en/panel_sys_well_item.xml index 63e60aab78..ccb57b6552 100644 --- a/indra/newview/skins/default/xui/en/panel_sys_well_item.xml +++ b/indra/newview/skins/default/xui/en/panel_sys_well_item.xml @@ -10,8 +10,8 @@ height="35" layout="topleft" follows="left|right" - background_opaque="false"
- background_visible="true"
+ background_opaque="false" + background_visible="true" bg_alpha_color="0.0 0.0 0.0 0.0" > <text top="2" diff --git a/indra/newview/skins/default/xui/en/sidepanel_appearance.xml b/indra/newview/skins/default/xui/en/sidepanel_appearance.xml index 2ce156cfda..041297a542 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_appearance.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_appearance.xml @@ -62,7 +62,7 @@ width="313" /> <button follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Wear" layout="topleft" @@ -72,7 +72,7 @@ width="80" /> <button follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="New Outfit" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml index cbcc3f10ad..377d34cdcc 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml @@ -41,7 +41,7 @@ <button
enabled="true"
follows="bottom|left"
- font="SansSerifSmallBold"
+ font="SansSerifSmall"
height="25"
label="Info"
layout="topleft"
@@ -52,7 +52,7 @@ <button
enabled="true"
follows="bottom|left"
- font="SansSerifSmallBold"
+ font="SansSerifSmall"
height="25"
label="Share"
layout="topleft"
@@ -63,7 +63,7 @@ <button
enabled="false"
follows="bottom|left"
- font="SansSerifSmallBold"
+ font="SansSerifSmall"
height="25"
label="Wear"
layout="topleft"
@@ -74,7 +74,7 @@ <button
enabled="false"
follows="bottom|left"
- font="SansSerifSmallBold"
+ font="SansSerifSmall"
height="25"
label="Play"
layout="topleft"
@@ -85,7 +85,7 @@ <button
enabled="false"
follows="bottom|left"
- font="SansSerifSmallBold"
+ font="SansSerifSmall"
height="25"
label="Teleport"
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml index 39cd75074e..0c605650fc 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -479,7 +479,7 @@ width="313">
<button
follows="bottom|left"
- font="SansSerifSmallBold"
+ font="SansSerifSmall"
height="25"
label="Edit"
layout="topleft"
@@ -489,7 +489,7 @@ width="50" />
<button
follows="bottom|right"
- font="SansSerifSmallBold"
+ font="SansSerifSmall"
height="25"
label="Cancel"
layout="topleft"
@@ -499,7 +499,7 @@ width="70" />
<button
follows="bottom|right"
- font="SansSerifSmallBold"
+ font="SansSerifSmall"
height="25"
label="Save"
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml index 8eb2254112..7647be7830 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml @@ -485,7 +485,7 @@ width="313"> <button follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Edit" layout="topleft" @@ -495,7 +495,7 @@ width="50" /> <button follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Open" layout="topleft" @@ -505,7 +505,7 @@ width="60" /> <button follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Pay" layout="topleft" @@ -515,7 +515,7 @@ width="50" /> <button follows="bottom|left" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Buy" layout="topleft" @@ -525,7 +525,7 @@ width="60" /> <button follows="bottom|right" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Cancel" layout="topleft" @@ -535,7 +535,7 @@ width="70" /> <button follows="bottom|right" - font="SansSerifSmallBold" + font="SansSerifSmall" height="25" label="Save" layout="topleft" |