summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorAimee Linden <aimee@lindenlab.com>2010-04-07 15:16:32 +0100
committerAimee Linden <aimee@lindenlab.com>2010-04-07 15:16:32 +0100
commit762672ad63f981377d83c148dc49a08c317383b2 (patch)
tree6ea66624fcd1beb845065a98e8d6c4925e85ee7a /indra/newview
parentadce2ecdf8f3a0efcd4907699d286012124ac496 (diff)
parent9f9b240dcee53a50b56a0ccb8d255a86d0ce62c9 (diff)
Automated merge with ssh://hg.lindenlab.com/q/viewer-trunk
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/app_settings/logcontrol.xml1
-rw-r--r--indra/newview/llcallfloater.cpp53
-rw-r--r--indra/newview/llcallfloater.h21
-rw-r--r--indra/newview/llvoiceclient.cpp182
-rw-r--r--indra/newview/llvoiceclient.h49
-rw-r--r--indra/newview/skins/default/xui/en/floater_voice_controls.xml38
6 files changed, 323 insertions, 21 deletions
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml
index d7bb64ce8a..d3fb958638 100644
--- a/indra/newview/app_settings/logcontrol.xml
+++ b/indra/newview/app_settings/logcontrol.xml
@@ -40,6 +40,7 @@
</array>
<key>tags</key>
<array>
+ <string>Voice</string>
</array>
</map>
</array>
diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp
index 44f1cefafe..6dfa8397a0 100644
--- a/indra/newview/llcallfloater.cpp
+++ b/indra/newview/llcallfloater.cpp
@@ -52,6 +52,7 @@
#include "lltransientfloatermgr.h"
#include "llviewerwindow.h"
#include "llvoicechannel.h"
+#include "llvoiceclient.h" // for Voice font list types
#include "llviewerparcelmgr.h"
static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids);
@@ -95,7 +96,7 @@ static void* create_non_avatar_caller(void*)
return new LLNonAvatarCaller;
}
-LLVoiceChannel* LLCallFloater::sCurrentVoiceCanel = NULL;
+LLVoiceChannel* LLCallFloater::sCurrentVoiceChannel = NULL;
LLCallFloater::LLCallFloater(const LLSD& key)
: LLTransientDockableFloater(NULL, false, key)
@@ -105,6 +106,7 @@ LLCallFloater::LLCallFloater(const LLSD& key)
, mNonAvatarCaller(NULL)
, mVoiceType(VC_LOCAL_CHAT)
, mAgentPanel(NULL)
+, mVoiceFont(NULL)
, mSpeakingIndicator(NULL)
, mIsModeratorMutedVoice(false)
, mInitParticipantsVoiceState(false)
@@ -147,6 +149,9 @@ BOOL LLCallFloater::postBuild()
childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this));
+ mVoiceFont = getChild<LLComboBox>("voice_font");
+ childSetCommitCallback("voice_font", commitVoiceFont, this); // *FIX: childSetCommitCallback deprecated
+
mNonAvatarCaller = getChild<LLNonAvatarCaller>("non_avatar_caller");
mNonAvatarCaller->setVisible(FALSE);
@@ -158,7 +163,6 @@ BOOL LLCallFloater::postBuild()
initAgentData();
-
connectToChannel(LLVoiceChannel::getCurrentVoiceChannel());
setIsChrome(true);
@@ -206,6 +210,9 @@ void LLCallFloater::draw()
// virtual
void LLCallFloater::onChange()
{
+ // *FIX: Temporarily dumping this here till I decide where it should go
+ updateVoiceFont();
+
if (NULL == mParticipants) return;
updateParticipantsVoiceState();
@@ -232,6 +239,37 @@ void LLCallFloater::leaveCall()
}
}
+/* static */
+void LLCallFloater::commitVoiceFont(LLUICtrl* ctrl, void* userdata)
+{
+ LLVoiceClient::getInstance()->setVoiceFont(ctrl->getValue());
+}
+
+void LLCallFloater::updateVoiceFont()
+{
+ if (mVoiceFont)
+ {
+ mVoiceFont->removeall();
+ mVoiceFont->add(getString("no_voice_font"), 0);
+
+ if (LLVoiceClient::getInstance()->getVoiceFontsAvailable())
+ {
+ const LLVoiceClient::voice_font_list_t font_list = LLVoiceClient::getInstance()->getVoiceFontList();
+
+ for (LLVoiceClient::voice_font_list_t::const_iterator it = font_list.begin(); it != font_list.end(); ++it)
+ {
+ mVoiceFont->add(*(it->second), it->first, ADD_BOTTOM);
+ }
+ mVoiceFont->setEnabled(true);
+ mVoiceFont->setValue(LLVoiceClient::getInstance()->getVoiceFont());
+ }
+ else
+ {
+ mVoiceFont->setEnabled(false);
+ }
+ }
+}
+
void LLCallFloater::updateSession()
{
LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
@@ -370,7 +408,7 @@ void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
// *NOTE: if signal was sent for voice channel with LLVoiceChannel::STATE_NO_CHANNEL_INFO
// it sill be sent for the same channel again (when state is changed).
// So, lets ignore this call.
- if (channel == sCurrentVoiceCanel) return;
+ if (channel == sCurrentVoiceChannel) return;
LLCallFloater* call_floater = LLFloaterReg::getTypedInstance<LLCallFloater>("voice_controls");
@@ -715,9 +753,9 @@ void LLCallFloater::connectToChannel(LLVoiceChannel* channel)
{
mVoiceChannelStateChangeConnection.disconnect();
- sCurrentVoiceCanel = channel;
+ sCurrentVoiceChannel = channel;
- mVoiceChannelStateChangeConnection = sCurrentVoiceCanel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2));
+ mVoiceChannelStateChangeConnection = sCurrentVoiceChannel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2));
updateState(channel->getState());
}
@@ -737,7 +775,7 @@ void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old
void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state)
{
- LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceCanel->getSessionName() << LL_ENDL;
+ LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceChannel->getSessionName() << LL_ENDL;
if (LLVoiceChannel::STATE_CONNECTED == new_state)
{
updateSession();
@@ -746,6 +784,9 @@ void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state)
{
reset(new_state);
}
+
+ // *FIX: Dumped here till I decide where to put it
+ updateVoiceFont();
}
void LLCallFloater::reset(const LLVoiceChannel::EState& new_state)
diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h
index 0a8ea7de39..9a13d853c9 100644
--- a/indra/newview/llcallfloater.h
+++ b/indra/newview/llcallfloater.h
@@ -40,6 +40,7 @@
class LLAvatarList;
class LLAvatarListItem;
+class LLComboBox;
class LLNonAvatarCaller;
class LLOutputMonitorCtrl;
class LLParticipantList;
@@ -47,15 +48,15 @@ class LLSpeakerMgr;
class LLSpeakersDelayActionsStorage;
/**
- * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron on the Speak button.
- * It can be torn-off and freely positioned onscreen.
+ * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron
+ * on the Speak button. It can be torn-off and freely positioned onscreen.
*
- * When the Resident is engaged in Nearby Voice Chat, the Voice Control Panel provides control over
- * the Resident's own microphone input volume, the audible volume of each of the other participants,
- * the Resident's own Voice Morphing settings (if she has subscribed to enable the feature), and Voice Recording.
+ * When the Resident is engaged in Voice Chat, the Voice Control Panel provides control
+ * over the audible volume of each of the other participants, the Resident's own Voice
+ * Morphing settings (if she has subscribed to enable the feature), and Voice Recording.
*
- * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel also provides an
- * 'Leave Call' button to allow the Resident to leave that voice channel.
+ * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel
+ * also provides a 'Leave Call' button to allow the Resident to leave that voice channel.
*/
class LLCallFloater : public LLTransientDockableFloater, LLVoiceClientParticipantObserver
{
@@ -101,6 +102,9 @@ private:
void leaveCall();
+ static void commitVoiceFont(LLUICtrl*,void* userdata);
+ void updateVoiceFont();
+
/**
* Updates mSpeakerManager and list according to current Voice Channel
*
@@ -230,6 +234,7 @@ private:
LLNonAvatarCaller* mNonAvatarCaller;
EVoiceControls mVoiceType;
LLPanel* mAgentPanel;
+ LLComboBox* mVoiceFont;
LLOutputMonitorCtrl* mSpeakingIndicator;
bool mIsModeratorMutedVoice;
@@ -259,7 +264,7 @@ private:
*
* @see sOnCurrentChannelChanged()
*/
- static LLVoiceChannel* sCurrentVoiceCanel;
+ static LLVoiceChannel* sCurrentVoiceChannel;
/* virtual */
LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; }
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 2238acd643..4ea4d9c16e 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -254,6 +254,12 @@ protected:
std::string audioMediaString;
std::string displayNameString;
std::string deviceString;
+ S32 id;
+ std::string descriptionString;
+ std::string expirationDateString;
+ bool hasExpired;
+ std::string fontTypeString;
+ std::string fontStatusString;
int participantType;
bool isLocallyMuted;
bool isModeratorMuted;
@@ -511,7 +517,20 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
{
gVoiceClient->deleteAllAutoAcceptRules();
}
-
+ else if (!stricmp("SessionFonts", tag))
+ {
+ LLVoiceClient::getInstance()->clearSessionFonts();
+ }
+ else if (!stricmp("SessionFont", tag))
+ {
+ id = 0;
+ nameString.clear();
+ descriptionString.clear();
+ expirationDateString.clear();
+ hasExpired.clear();
+ fontTypeString.clear();
+ fontStatusString.clear();
+ }
}
}
responseDepth++;
@@ -639,6 +658,34 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
autoAcceptMask = string;
else if (!stricmp("AutoAddAsBuddy", tag))
autoAddAsBuddy = string;
+ else if (!stricmp("SessionFont", tag))
+ {
+ LLVoiceClient::getInstance()->addSessionFont(id, nameString, descriptionString, expirationDateString, hasExpired, fontTypeString, fontStatusString);
+ }
+ else if (!stricmp("ID", tag))
+ {
+ id = strtol(string.c_str(), NULL, 10);
+ }
+ else if (!stricmp("Description", tag))
+ {
+ descriptionString = string;
+ }
+ else if (!stricmp("ExpirationDate", tag))
+ {
+ expirationDateString = string;
+ }
+ else if (!stricmp("Expired", tag))
+ {
+ hasExpired = !stricmp(string.c_str(), "1");
+ }
+ else if (!stricmp("Type", tag))
+ {
+ fontTypeString = string;
+ }
+ else if (!stricmp("Status", tag))
+ {
+ fontStatusString = string;
+ }
else if (!stricmp("MessageHeader", tag))
messageHeader = string;
else if (!stricmp("MessageBody", tag))
@@ -840,6 +887,8 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
}
else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))
{
+ // *TODO: Receive the current SessionFontID?
+
/*
<Event type="SessionUpdatedEvent">
<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
@@ -909,6 +958,10 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
{
gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString);
}
+ else if (!stricmp(actionCstr, "Account.GetSessionFonts.1"))
+ {
+ LLVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString);
+ }
else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
{
// We don't need to process these, but they're so spammy we don't want to log them.
@@ -1337,6 +1390,10 @@ LLVoiceClient::LLVoiceClient() :
mBuddyListMapPopulated(false),
mBlockRulesListReceived(false),
mAutoAcceptRulesListReceived(false),
+
+ mSessionFontsReceived(false),
+ mFontID(0),
+
mCaptureDeviceDirty(false),
mRenderDeviceDirty(false),
mSpatialCoordsDirty(false),
@@ -1675,6 +1732,7 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
CASE(stateNeedsLogin);
CASE(stateLoggingIn);
CASE(stateLoggedIn);
+ CASE(stateFontListReceived);
CASE(stateCreatingSessionGroup);
CASE(stateNoChannel);
CASE(stateJoiningSession);
@@ -2250,6 +2308,9 @@ void LLVoiceClient::stateMachine()
notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+ // request the set of available voice fonts
+ accountGetSessionFontsSendMessage();
+
// request the current set of block rules (we'll need them when updating the friends list)
accountListBlockRulesSendMessage();
@@ -2281,6 +2342,11 @@ void LLVoiceClient::stateMachine()
writeString(stream.str());
}
}
+
+ // accountGetSessionFontsResponse() will transition from here to stateFontListReceived.
+
+ //MARK: stateFontListReceived
+ case stateFontListReceived: // font list received
#if USE_SESSION_GROUPS
// create the main session group
@@ -2757,6 +2823,24 @@ void LLVoiceClient::accountListAutoAcceptRulesSendMessage()
}
}
+void LLVoiceClient::accountGetSessionFontsSendMessage()
+{
+ if(!mAccountHandle.empty())
+ {
+ std::ostringstream stream;
+
+ LL_DEBUGS("Voice") << "requesting session font list" << LL_ENDL;
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.GetSessionFonts.1\">"
+ << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+ << "</Request>"
+ << "\n\n\n";
+
+ writeString(stream.str());
+ }
+}
+
void LLVoiceClient::sessionGroupCreateSendMessage()
{
if(!mAccountHandle.empty())
@@ -2807,6 +2891,7 @@ void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAu
stream
<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
+ << "<VoiceFontID>" << mFontID << "</VoiceFontID>"
<< "<Name>" << mChannelName << "</Name>"
<< "</Request>\n\n\n";
writeString(stream.str());
@@ -2882,6 +2967,22 @@ void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session)
writeString(stream.str());
}
+void LLVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session)
+{
+ llassert(session);
+ LL_DEBUGS("Voice") << "requesting voice font: " << mFontID << " in session handle: " << session->mHandle << LL_ENDL;
+
+ std::ostringstream stream;
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetVoiceFont.1\">"
+ << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+ << "<SessionFontID>" << mFontID << "</SessionFontID>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
void LLVoiceClient::sessionTerminate()
{
mSessionTerminateRequested = true;
@@ -7050,6 +7151,74 @@ void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const s
}
}
+LLVoiceClient::voiceFontEntry::voiceFontEntry(S32 id) :
+ mID(id),
+ mHasExpired(false)
+{
+}
+
+LLVoiceClient::voiceFontEntry *LLVoiceClient::addSessionFont(const voice_font_id_t &id,
+ const std::string &name,
+ const std::string &description,
+ const std::string &expirationDate,
+ const bool hasExpired,
+ const std::string &fontType,
+ const std::string &fontStatus)
+{
+ voiceFontEntry *font = NULL;
+
+ voice_font_map_t::iterator iter = mSessionFontMap.find(id);
+ if(iter != mSessionFontMap.end())
+ {
+ // Found session font already in the map.
+ LL_DEBUGS("Voice") << "existing session font " << id << " : " << name << LL_ENDL;
+ font = iter->second;
+ }
+
+ if(font == NULL)
+ {
+ LL_DEBUGS("Voice") << "adding session font " << id << " : " << name << (hasExpired?" (Expired)":"") << LL_ENDL;
+ font = new voiceFontEntry(id);
+ font->mName = name;
+ font->mDescription = description;
+ font->mExpirationDate = expirationDate;
+ font->mHasExpired = hasExpired;
+ font->mFontType = fontType;
+ font->mFontStatus = fontStatus;
+
+ mSessionFontList.insert(voice_font_list_t::value_type(font->mID, &(font->mName)));
+ mSessionFontMap.insert(voice_font_map_t::value_type(font->mID, font));
+ }
+ return font;
+}
+
+bool LLVoiceClient::setVoiceFont(voice_font_id_t id)
+{
+ if (!mAudioSession || !mAudioSession->mVoiceEnabled || !mSessionFontsReceived)
+ {
+ LL_DEBUGS("Voice") << "Session fonts not available" << LL_ENDL;
+ return false;
+ }
+
+ if(id == 0 || mSessionFontMap.find(id) != mSessionFontMap.end())
+ {
+ mFontID = id;
+ }
+ else
+ {
+ LL_DEBUGS("Voice") << "Invalid session font " << id << LL_ENDL;
+ return false;
+ }
+
+ sessionSetVoiceFontSendMessage(mAudioSession);
+ return true;
+}
+
+const LLVoiceClient::voice_font_id_t LLVoiceClient::getVoiceFont() const
+{
+ return mFontID;
+}
+
void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString)
{
// Block list entries were updated via addBlockRule() during parsing. Just flag that we're done.
@@ -7062,6 +7231,17 @@ void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std
mAutoAcceptRulesListReceived = true;
}
+void LLVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString)
+{
+ // Session font list entries were updated via addSessionFont() during parsing. Just flag that we're done.
+ mSessionFontsReceived = true;
+
+ if(getState() == stateLoggedIn)
+ {
+ setState(stateFontListReceived);
+ }
+}
+
void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
{
mParticipantObservers.insert(observer);
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index a29c386182..0df4e4bb3b 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -429,8 +429,38 @@ static void updatePosition(void);
void deleteAllAutoAcceptRules(void);
void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy);
void accountListBlockRulesResponse(int statusCode, const std::string &statusString);
- void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString);
-
+ void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString);
+
+ struct voiceFontEntry
+ {
+ voiceFontEntry(S32 id);
+ S32 mID;
+ std::string mName;
+ std::string mDescription;
+ std::string mExpirationDate;
+ bool mHasExpired;
+ std::string mFontType;
+ std::string mFontStatus;
+ };
+
+ typedef S32 voice_font_id_t;
+ typedef std::map<voice_font_id_t, std::string*> voice_font_list_t;
+ typedef std::map<voice_font_id_t, voiceFontEntry*> voice_font_map_t;
+
+ bool getVoiceFontsAvailable() const { return mSessionFontsReceived; };
+ bool setVoiceFont(voice_font_id_t id);
+ const voice_font_id_t getVoiceFont() const;
+ const voice_font_list_t &getVoiceFontList() const { return mSessionFontList; };
+
+ voiceFontEntry *addSessionFont(const voice_font_id_t &id,
+ const std::string &name,
+ const std::string &description,
+ const std::string &expirationDate,
+ const bool hasExpired,
+ const std::string &fontType,
+ const std::string &fontStatus);
+ void accountGetSessionFontsResponse(int statusCode, const std::string &statusString);
+
/////////////////////////////
// session control messages
void connectorCreate();
@@ -452,12 +482,15 @@ static void updatePosition(void);
void accountListBlockRulesSendMessage();
void accountListAutoAcceptRulesSendMessage();
-
+
+ void accountGetSessionFontsSendMessage();
+
void sessionGroupCreateSendMessage();
void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false);
void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false);
void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session
void sessionTextConnectSendMessage(sessionState *session); // just joins the text session
+ void sessionSetVoiceFontSendMessage(sessionState *session);
void sessionTerminateSendMessage(sessionState *session);
void sessionGroupTerminateSendMessage(sessionState *session);
void sessionMediaDisconnectSendMessage(sessionState *session);
@@ -489,7 +522,7 @@ static void updatePosition(void);
deviceList *getCaptureDevices();
deviceList *getRenderDevices();
-
+
void setNonSpatialChannel(
const std::string &uri,
const std::string &credentials);
@@ -562,6 +595,7 @@ static void updatePosition(void);
stateNeedsLogin, // send login request
stateLoggingIn, // waiting for account handle
stateLoggedIn, // account handle received
+ stateFontListReceived, // List of available voice fonts received
stateCreatingSessionGroup, // Creating the main session group
stateNoChannel, //
stateJoiningSession, // waiting for session handle
@@ -662,7 +696,12 @@ static void updatePosition(void);
bool mBlockRulesListReceived;
bool mAutoAcceptRulesListReceived;
buddyListMap mBuddyListMap;
-
+
+ bool mSessionFontsReceived;
+ S32 mFontID;
+ voice_font_list_t mSessionFontList;
+ voice_font_map_t mSessionFontMap; // *TODO: make private
+
deviceList mCaptureDevices;
deviceList mRenderDevices;
diff --git a/indra/newview/skins/default/xui/en/floater_voice_controls.xml b/indra/newview/skins/default/xui/en/floater_voice_controls.xml
index 114b9a84e3..f5aaedad4f 100644
--- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml
+++ b/indra/newview/skins/default/xui/en/floater_voice_controls.xml
@@ -33,6 +33,9 @@
name="no_one_near">
No one near has voice enabled
</string>
+ <floater.string name="no_voice_font">
+ No Voice Effect
+ </floater.string>
<layout_stack
clip="false"
follows="all"
@@ -84,7 +87,39 @@
visible="true"
width="20" />
</layout_panel>
- <layout_panel
+ <layout_stack
+ clip="false"
+ auto_resize="false"
+ follows="left|right"
+ height="23"
+ layout="topleft"
+ left="10"
+ mouse_opaque="false"
+ name="voice_font_and_leave_call_stack"
+ orientation="horizontal"
+ width="263">
+ <layout_panel
+ auto_resize="false"
+ user_resize="false"
+ follows="top|left"
+ height="26"
+ visible="true"
+ layout="topleft"
+ name="voice_font_panel"
+ width="120">
+ <combo_box
+ follows="left|top"
+ height="23"
+ name="voice_font"
+ top_pad="0"
+ width="120">
+ <combo_box.item
+ label="No Voice Effect"
+ name="no_voice_font"
+ value="0" />
+ </combo_box>
+ </layout_panel>
+ <layout_panel
auto_resize="false"
user_resize="false"
follows="top|left"
@@ -101,6 +136,7 @@
name="leave_call_btn"
width="100" />
</layout_panel>
+ </layout_stack>
<layout_panel
follows="all"
layout="topleft"