This works for notifications, and profiles, and chats, and even updates! Amazing, right? SinguReplaceLinks is on by default, URL highlighting works regardless, no rogue labels with this off. Select, right click, Copy Raw lets you copy the link with its hidden url shown and you can paste it back the same way if you're wanting to paste it all over! LLTextEditor overhaul synced the code with upstream LLTextBase as much as we could. appendStyledText is now just appendText Every setText appendText's now and adds a style based on the texteditor's color and font, this could slightly increase the weight of text editors (one extra segment) but it fixes a nasty bug with running past segmentation. Also no longer update the utf8 Removed append() which was no longer being used LLTextEditor and LLViewerTextEditor now have an extra boolean at the end of their constructors to have this replacement turned on at construction time (this also sets them read only) Update TextSegments to have a Tooltip string, like upstream. Hardened notecard previews so they don't accidentally replace the text when they go read only. Thanks to Deltek and Router Gray for helping me test and debug this commit!
321 lines
9.8 KiB
C++
321 lines
9.8 KiB
C++
/**
|
|
* @file llfloatervoiceeffect.cpp
|
|
* @author Aimee
|
|
* @brief Selection and preview of voice effect.
|
|
*
|
|
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llfloatervoiceeffect.h"
|
|
|
|
#include "llscrolllistctrl.h"
|
|
#include "llscrolllistitem.h"
|
|
#include "lltexteditor.h" // For linked text hack
|
|
#include "lltrans.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "llweb.h"
|
|
|
|
LLFloaterVoiceEffect::LLFloaterVoiceEffect(const LLSD& key)
|
|
: LLFloater(/*key*/)
|
|
, mVoiceEffectList(nullptr)
|
|
{
|
|
mCommitCallbackRegistrar.add("VoiceEffect.Record", boost::bind(&LLFloaterVoiceEffect::onClickRecord, this));
|
|
mCommitCallbackRegistrar.add("VoiceEffect.Play", boost::bind(&LLFloaterVoiceEffect::onClickPlay, this));
|
|
mCommitCallbackRegistrar.add("VoiceEffect.Stop", boost::bind(&LLFloaterVoiceEffect::onClickStop, this));
|
|
mCommitCallbackRegistrar.add("VoiceEffect.Activate", boost::bind(&LLFloaterVoiceEffect::onClickActivate, this));
|
|
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_voice_effect.xml");
|
|
}
|
|
|
|
// virtual
|
|
LLFloaterVoiceEffect::~LLFloaterVoiceEffect()
|
|
{
|
|
if(LLVoiceClient::instanceExists())
|
|
{
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (effect_interface)
|
|
{
|
|
effect_interface->removeObserver(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLFloaterVoiceEffect::postBuild()
|
|
{
|
|
setDefaultBtn("record_btn");
|
|
getChild<LLButton>("record_btn")->setFocus(true);
|
|
|
|
// Singu Note: Here we must hack together a linked piece of text
|
|
if (LLTextEditor* editor = getChild<LLTextEditor>("voice_morphing_link"))
|
|
{
|
|
editor->setParseHTML(true); // For some reason, adding style doesn't work unless this is true.
|
|
const std::string text = editor->getValue();
|
|
editor->clear();
|
|
LLStyleSP link(new LLStyle);
|
|
link->setLinkHREF(LLTrans::getString("voice_morphing_url"));
|
|
link->setColor(gSavedSettings.getColor4("HTMLLinkColor"));
|
|
editor->appendText(text, false, false, link);
|
|
}
|
|
|
|
mVoiceEffectList = getChild<LLScrollListCtrl>("voice_effect_list");
|
|
if (mVoiceEffectList)
|
|
{
|
|
mVoiceEffectList->setCommitCallback(boost::bind(&LLFloaterVoiceEffect::onClickPlay, this));
|
|
// mVoiceEffectList->setDoubleClickCallback(boost::bind(&LLFloaterVoiceEffect::onClickActivate, this));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// virtual
|
|
void LLFloaterVoiceEffect::onOpen()
|
|
{
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (effect_interface)
|
|
{
|
|
effect_interface->addObserver(this);
|
|
|
|
// Disconnect from the current voice channel ready to record a voice sample for previewing
|
|
effect_interface->enablePreviewBuffer(true);
|
|
}
|
|
|
|
refreshEffectList();
|
|
updateControls();
|
|
}
|
|
|
|
// virtual
|
|
void LLFloaterVoiceEffect::onClose(bool app_quitting)
|
|
{
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (effect_interface)
|
|
{
|
|
effect_interface->enablePreviewBuffer(false);
|
|
}
|
|
setVisible(false);
|
|
}
|
|
|
|
void LLFloaterVoiceEffect::refreshEffectList()
|
|
{
|
|
if (!mVoiceEffectList)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (!effect_interface)
|
|
{
|
|
mVoiceEffectList->setEnabled(false);
|
|
return;
|
|
}
|
|
|
|
LL_DEBUGS("Voice")<< "Rebuilding Voice Morph list."<< LL_ENDL;
|
|
|
|
// Preserve selected items and scroll position
|
|
S32 scroll_pos = mVoiceEffectList->getScrollPos();
|
|
uuid_vec_t selected_items;
|
|
std::vector<LLScrollListItem*> items = mVoiceEffectList->getAllSelected();
|
|
for(std::vector<LLScrollListItem*>::const_iterator it = items.begin(); it != items.end(); it++)
|
|
{
|
|
selected_items.push_back((*it)->getUUID());
|
|
}
|
|
|
|
mVoiceEffectList->deleteAllItems();
|
|
|
|
{
|
|
// Add the "No Voice Morph" entry
|
|
LLSD element;
|
|
|
|
element["id"] = LLUUID::null;
|
|
element["columns"][NAME_COLUMN]["column"] = "name";
|
|
element["columns"][NAME_COLUMN]["value"] = getString("no_voice_effect");
|
|
element["columns"][NAME_COLUMN]["font"] = "SANSSERIF";
|
|
element["columns"][NAME_COLUMN]["font-style"] = "BOLD";
|
|
|
|
/*LLScrollListItem* sl_item =*/ mVoiceEffectList->addElement(element, ADD_BOTTOM);
|
|
/* Singu Note: Ours works
|
|
// *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :(
|
|
if(sl_item)
|
|
{
|
|
((LLScrollListText*)sl_item->getColumn(0))->setFontStyle(LLFontGL::BOLD);
|
|
}
|
|
*/
|
|
}
|
|
|
|
// Add each Voice Morph template, if there are any (template list includes all usable effects)
|
|
const voice_effect_list_t& template_list = effect_interface->getVoiceEffectTemplateList();
|
|
if (!template_list.empty())
|
|
{
|
|
for (voice_effect_list_t::const_iterator it = template_list.begin(); it != template_list.end(); ++it)
|
|
{
|
|
const LLUUID& effect_id = it->second;
|
|
|
|
std::string localized_effect = "effect_" + it->first;
|
|
std::string effect_name = hasString(localized_effect) ? getString(localized_effect) : it->first; // XML contains localized effects names
|
|
|
|
LLSD effect_properties = effect_interface->getVoiceEffectProperties(effect_id);
|
|
|
|
// Tag the active effect.
|
|
if (effect_id == LLVoiceClient::instance().getVoiceEffectDefault())
|
|
{
|
|
effect_name += " " + getString("active_voice_effect");
|
|
}
|
|
|
|
// Tag available effects that are new this session
|
|
if (effect_properties["is_new"].asBoolean())
|
|
{
|
|
effect_name += " " + getString("new_voice_effect");
|
|
}
|
|
|
|
LLDate expiry_date = effect_properties["expiry_date"].asDate();
|
|
bool is_template_only = effect_properties["template_only"].asBoolean();
|
|
|
|
std::string font_style = "NORMAL";
|
|
if (!is_template_only)
|
|
{
|
|
font_style = "BOLD";
|
|
}
|
|
|
|
LLSD element;
|
|
element["id"] = effect_id;
|
|
|
|
element["columns"][NAME_COLUMN]["column"] = "name";
|
|
element["columns"][NAME_COLUMN]["value"] = effect_name;
|
|
element["columns"][NAME_COLUMN]["font"] = "SANSSERIF";
|
|
element["columns"][NAME_COLUMN]["font-style"] = font_style;
|
|
|
|
element["columns"][1]["column"] = "expires";
|
|
if (!is_template_only)
|
|
{
|
|
element["columns"][DATE_COLUMN]["value"] = expiry_date;
|
|
element["columns"][DATE_COLUMN]["type"] = "date";
|
|
}
|
|
else {
|
|
element["columns"][DATE_COLUMN]["value"] = getString("unsubscribed_voice_effect");
|
|
}
|
|
element["columns"][DATE_COLUMN]["font"] = "SANSSERIF";
|
|
element["columns"][DATE_COLUMN]["font-style"] = "NORMAL";
|
|
|
|
/*LLScrollListItem* sl_item =*/ mVoiceEffectList->addElement(element, ADD_BOTTOM);
|
|
/* Singu Note: Ours works
|
|
// *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :(
|
|
if(sl_item)
|
|
{
|
|
LLFontGL::StyleFlags style = is_template_only ? LLFontGL::NORMAL : LLFontGL::BOLD;
|
|
LLScrollListText* slt = dynamic_cast<LLScrollListText*>(sl_item->getColumn(0));
|
|
llassert(slt);
|
|
if (slt)
|
|
{
|
|
slt->setFontStyle(style);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
// Re-select items that were selected before, and restore the scroll position
|
|
for(uuid_vec_t::iterator it = selected_items.begin(); it != selected_items.end(); it++)
|
|
{
|
|
mVoiceEffectList->selectByID(*it);
|
|
}
|
|
mVoiceEffectList->setScrollPos(scroll_pos);
|
|
mVoiceEffectList->setEnabled(true);
|
|
}
|
|
|
|
void LLFloaterVoiceEffect::updateControls()
|
|
{
|
|
bool recording = false;
|
|
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (effect_interface)
|
|
{
|
|
recording = effect_interface->isPreviewRecording();
|
|
}
|
|
|
|
getChild<LLButton>("record_btn")->setVisible(!recording);
|
|
getChild<LLButton>("record_stop_btn")->setVisible(recording);
|
|
}
|
|
|
|
// virtual
|
|
void LLFloaterVoiceEffect::onVoiceEffectChanged(bool effect_list_updated)
|
|
{
|
|
if (effect_list_updated)
|
|
{
|
|
refreshEffectList();
|
|
}
|
|
updateControls();
|
|
}
|
|
|
|
void LLFloaterVoiceEffect::onClickRecord()
|
|
{
|
|
LL_DEBUGS("Voice") << "Record clicked" << LL_ENDL;
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (effect_interface)
|
|
{
|
|
effect_interface->recordPreviewBuffer();
|
|
}
|
|
updateControls();
|
|
}
|
|
|
|
void LLFloaterVoiceEffect::onClickPlay()
|
|
{
|
|
LL_DEBUGS("Voice") << "Play clicked" << LL_ENDL;
|
|
if (!mVoiceEffectList)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const LLUUID& effect_id = mVoiceEffectList->getCurrentID();
|
|
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (effect_interface)
|
|
{
|
|
effect_interface->playPreviewBuffer(effect_id);
|
|
}
|
|
updateControls();
|
|
}
|
|
|
|
void LLFloaterVoiceEffect::onClickStop()
|
|
{
|
|
LL_DEBUGS("Voice") << "Stop clicked" << LL_ENDL;
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (effect_interface)
|
|
{
|
|
effect_interface->stopPreviewBuffer();
|
|
}
|
|
updateControls();
|
|
}
|
|
|
|
void LLFloaterVoiceEffect::onClickActivate()
|
|
{
|
|
LL_DEBUGS("Voice") << "Activate clicked" << LL_ENDL;
|
|
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
|
|
if (effect_interface && mVoiceEffectList)
|
|
{
|
|
//effect_interface->setVoiceEffect(mVoiceEffectList->getCurrentID()); // Singu Note: This would return early, since we're disconnected from voice, just set the string and refresh list
|
|
gSavedPerAccountSettings.setString("VoiceEffectDefault", mVoiceEffectList->getCurrentID().asString());
|
|
refreshEffectList();
|
|
}
|
|
}
|
|
|