1795 lines
44 KiB
C++
1795 lines
44 KiB
C++
/**
|
|
* @file llpreviewgesture.cpp
|
|
* @brief Editing UI for inventory-based gestures.
|
|
*
|
|
* $LicenseInfo:firstyear=2004&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2004-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 <algorithm>
|
|
|
|
#include "llpreviewgesture.h"
|
|
|
|
// libraries
|
|
#include "lldatapacker.h"
|
|
#include "lldarray.h"
|
|
#include "llinventorydefines.h"
|
|
#include "llstring.h"
|
|
#include "lldir.h"
|
|
#include "llmultigesture.h"
|
|
#include "llvfile.h"
|
|
|
|
// newview
|
|
#include "llagent.h" // todo: remove
|
|
#include "llanimationstates.h"
|
|
#include "llassetuploadresponders.h"
|
|
#include "llbutton.h"
|
|
#include "llcheckboxctrl.h"
|
|
#include "llcombobox.h"
|
|
#include "lldelayedgestureerror.h"
|
|
#include "llfloatergesture.h" // for some label constants
|
|
#include "llgesturemgr.h"
|
|
#include "llinventorymodel.h"
|
|
#include "llkeyboard.h"
|
|
#include "lllineeditor.h"
|
|
#include "llnotify.h"
|
|
#include "llradiogroup.h"
|
|
#include "llscrolllistctrl.h"
|
|
#include "lltextbox.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "llviewerinventory.h"
|
|
#include "llviewerobject.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerregion.h"
|
|
#include "llviewerstats.h"
|
|
#include "llviewerwindow.h" // busycount
|
|
#include "llappviewer.h" // gVFS
|
|
#include "llanimstatelabels.h"
|
|
#include "llresmgr.h"
|
|
|
|
|
|
// *TODO: Translate?
|
|
const std::string NONE_LABEL = "---";
|
|
const std::string SHIFT_LABEL = "Shift";
|
|
const std::string CTRL_LABEL = "Ctrl";
|
|
|
|
void dialog_refresh_all();
|
|
|
|
// used for getting
|
|
|
|
class LLInventoryGestureAvailable : public LLInventoryCompletionObserver
|
|
{
|
|
public:
|
|
LLInventoryGestureAvailable() {}
|
|
|
|
protected:
|
|
virtual void done();
|
|
};
|
|
|
|
void LLInventoryGestureAvailable::done()
|
|
{
|
|
LLPreview* preview = NULL;
|
|
item_ref_t::iterator it = mComplete.begin();
|
|
item_ref_t::iterator end = mComplete.end();
|
|
for(; it < end; ++it)
|
|
{
|
|
preview = LLPreview::find((*it));
|
|
if(preview)
|
|
{
|
|
preview->refresh();
|
|
}
|
|
}
|
|
gInventory.removeObserver(this);
|
|
delete this;
|
|
}
|
|
|
|
// Used for sorting
|
|
struct SortItemPtrsByName
|
|
{
|
|
bool operator()(const LLInventoryItem* i1, const LLInventoryItem* i2)
|
|
{
|
|
return (LLStringUtil::compareDict(i1->getName(), i2->getName()) < 0);
|
|
}
|
|
};
|
|
|
|
// static
|
|
LLPreviewGesture* LLPreviewGesture::show(const std::string& title, const LLUUID& item_id, const LLUUID& object_id, BOOL take_focus)
|
|
{
|
|
LLPreviewGesture* previewp = (LLPreviewGesture*)LLPreview::find(item_id);
|
|
if (previewp)
|
|
{
|
|
previewp->open(); /*Flawfinder: ignore*/
|
|
if (take_focus)
|
|
{
|
|
previewp->setFocus(TRUE);
|
|
}
|
|
return previewp;
|
|
}
|
|
|
|
LLPreviewGesture* self = new LLPreviewGesture();
|
|
|
|
// Finish internal construction
|
|
self->init(item_id, object_id);
|
|
|
|
// Builds and adds to gFloaterView
|
|
LLUICtrlFactory::getInstance()->buildFloater(self, "floater_preview_gesture.xml");
|
|
self->setTitle(title);
|
|
|
|
// Move window to top-left of screen
|
|
LLMultiFloater* hostp = self->getHost();
|
|
if (hostp == NULL)
|
|
{
|
|
LLRect r = self->getRect();
|
|
LLRect screen = gFloaterView->getRect();
|
|
r.setLeftTopAndSize(0, screen.getHeight(), r.getWidth(), r.getHeight());
|
|
self->setRect(r);
|
|
}
|
|
else
|
|
{
|
|
// re-add to host to update title
|
|
hostp->addFloater(self, TRUE);
|
|
}
|
|
|
|
// Start speculative download of sounds and animations
|
|
LLUUID animation_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_ANIMATION);
|
|
gInventory.startBackgroundFetch(animation_folder_id);
|
|
|
|
LLUUID sound_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_SOUND);
|
|
gInventory.startBackgroundFetch(sound_folder_id);
|
|
|
|
// this will call refresh when we have everything.
|
|
LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
|
|
if(item && !item->isComplete())
|
|
{
|
|
LLInventoryGestureAvailable* observer;
|
|
observer = new LLInventoryGestureAvailable();
|
|
observer->watchItem(item_id);
|
|
gInventory.addObserver(observer);
|
|
item->fetchFromServer();
|
|
}
|
|
else
|
|
{
|
|
// not sure this is necessary.
|
|
self->refresh();
|
|
}
|
|
|
|
if (take_focus)
|
|
{
|
|
self->setFocus(TRUE);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
|
|
// virtual
|
|
BOOL LLPreviewGesture::handleKeyHere(KEY key, MASK mask)
|
|
{
|
|
if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL)))
|
|
{
|
|
saveIfNeeded();
|
|
return TRUE;
|
|
}
|
|
|
|
return LLPreview::handleKeyHere(key, mask);
|
|
}
|
|
|
|
|
|
// virtual
|
|
BOOL LLPreviewGesture::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
|
|
EDragAndDropType cargo_type,
|
|
void* cargo_data,
|
|
EAcceptance* accept,
|
|
std::string& tooltip_msg)
|
|
{
|
|
BOOL handled = TRUE;
|
|
switch(cargo_type)
|
|
{
|
|
case DAD_ANIMATION:
|
|
case DAD_SOUND:
|
|
{
|
|
// TODO: Don't allow this if you can't transfer the sound/animation
|
|
|
|
// make a script step
|
|
LLInventoryItem* item = (LLInventoryItem*)cargo_data;
|
|
if (item
|
|
&& gInventory.getItem(item->getUUID()))
|
|
{
|
|
LLPermissions perm = item->getPermissions();
|
|
if (!((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED))
|
|
{
|
|
*accept = ACCEPT_NO;
|
|
if (tooltip_msg.empty())
|
|
{
|
|
tooltip_msg.assign("Only animations and sounds\n"
|
|
"with unrestricted permissions\n"
|
|
"can be added to a gesture.");
|
|
}
|
|
break;
|
|
}
|
|
else if (drop)
|
|
{
|
|
LLScrollListItem* line = NULL;
|
|
if (cargo_type == DAD_ANIMATION)
|
|
{
|
|
line = addStep( STEP_ANIMATION );
|
|
LLGestureStepAnimation* anim = (LLGestureStepAnimation*)line->getUserdata();
|
|
anim->mAnimAssetID = item->getAssetUUID();
|
|
anim->mAnimName = item->getName();
|
|
}
|
|
else if (cargo_type == DAD_SOUND)
|
|
{
|
|
line = addStep( STEP_SOUND );
|
|
LLGestureStepSound* sound = (LLGestureStepSound*)line->getUserdata();
|
|
sound->mSoundAssetID = item->getAssetUUID();
|
|
sound->mSoundName = item->getName();
|
|
}
|
|
updateLabel(line);
|
|
mDirty = TRUE;
|
|
refresh();
|
|
}
|
|
*accept = ACCEPT_YES_COPY_MULTI;
|
|
}
|
|
else
|
|
{
|
|
// Not in user's inventory means it was in object inventory
|
|
*accept = ACCEPT_NO;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
*accept = ACCEPT_NO;
|
|
if (tooltip_msg.empty())
|
|
{
|
|
tooltip_msg.assign("Only animations and sounds\n"
|
|
"can be added to a gesture.");
|
|
}
|
|
break;
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
|
|
// virtual
|
|
BOOL LLPreviewGesture::canClose()
|
|
{
|
|
|
|
if(!mDirty || mForceClose)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Bring up view-modal dialog: Save changes? Yes, No, Cancel
|
|
LLNotifications::instance().add("SaveChanges", LLSD(), LLSD(),
|
|
boost::bind(&LLPreviewGesture::handleSaveChangesDialog, this, _1, _2) );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// virtual
|
|
void LLPreviewGesture::onClose(bool app_quitting)
|
|
{
|
|
gGestureManager.stopGesture(mPreviewGesture);
|
|
LLPreview::onClose(app_quitting);
|
|
}
|
|
|
|
// virtual
|
|
void LLPreviewGesture::onUpdateSucceeded()
|
|
{
|
|
refresh();
|
|
}
|
|
|
|
// virtual
|
|
void LLPreviewGesture::setMinimized(BOOL minimize)
|
|
{
|
|
if (minimize != isMinimized())
|
|
{
|
|
LLFloater::setMinimized(minimize);
|
|
|
|
// We're being restored
|
|
if (!minimize)
|
|
{
|
|
refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool LLPreviewGesture::handleSaveChangesDialog(const LLSD& notification, const LLSD& response)
|
|
{
|
|
S32 option = LLNotification::getSelectedOption(notification, response);
|
|
switch(option)
|
|
{
|
|
case 0: // "Yes"
|
|
gGestureManager.stopGesture(mPreviewGesture);
|
|
mCloseAfterSave = TRUE;
|
|
onClickSave(this);
|
|
break;
|
|
|
|
case 1: // "No"
|
|
gGestureManager.stopGesture(mPreviewGesture);
|
|
mDirty = FALSE; // Force the dirty flag because user has clicked NO on confirm save dialog...
|
|
close();
|
|
break;
|
|
|
|
case 2: // "Cancel"
|
|
default:
|
|
// If we were quitting, we didn't really mean it.
|
|
LLAppViewer::instance()->abortQuit();
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
LLPreviewGesture::LLPreviewGesture()
|
|
: LLPreview("Gesture Preview"),
|
|
mTriggerEditor(NULL),
|
|
mModifierCombo(NULL),
|
|
mKeyCombo(NULL),
|
|
mLibraryList(NULL),
|
|
mAddBtn(NULL),
|
|
mUpBtn(NULL),
|
|
mDownBtn(NULL),
|
|
mDeleteBtn(NULL),
|
|
mStepList(NULL),
|
|
mOptionsText(NULL),
|
|
mAnimationRadio(NULL),
|
|
mAnimationCombo(NULL),
|
|
mSoundCombo(NULL),
|
|
mChatEditor(NULL),
|
|
mSaveBtn(NULL),
|
|
mPreviewBtn(NULL),
|
|
mPreviewGesture(NULL),
|
|
mDirty(FALSE)
|
|
{
|
|
}
|
|
|
|
|
|
LLPreviewGesture::~LLPreviewGesture()
|
|
{
|
|
// Userdata for all steps is a LLGestureStep we need to clean up
|
|
std::vector<LLScrollListItem*> data_list = mStepList->getAllData();
|
|
std::vector<LLScrollListItem*>::iterator data_itor;
|
|
for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
|
|
{
|
|
LLScrollListItem* item = *data_itor;
|
|
LLGestureStep* step = (LLGestureStep*)item->getUserdata();
|
|
delete step;
|
|
step = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL LLPreviewGesture::postBuild()
|
|
{
|
|
LLLineEditor* edit;
|
|
LLComboBox* combo;
|
|
LLButton* btn;
|
|
LLScrollListCtrl* list;
|
|
LLTextBox* text;
|
|
LLCheckBoxCtrl* check;
|
|
|
|
edit = getChild<LLLineEditor>("trigger_editor");
|
|
edit->setKeystrokeCallback(onKeystrokeCommit);
|
|
edit->setCommitCallback(onCommitSetDirty);
|
|
edit->setCommitOnFocusLost(TRUE);
|
|
edit->setCallbackUserData(this);
|
|
edit->setIgnoreTab(TRUE);
|
|
mTriggerEditor = edit;
|
|
|
|
text = getChild<LLTextBox>("replace_text");
|
|
text->setEnabled(FALSE);
|
|
mReplaceText = text;
|
|
|
|
edit = getChild<LLLineEditor>("replace_editor");
|
|
edit->setEnabled(FALSE);
|
|
edit->setKeystrokeCallback(onKeystrokeCommit);
|
|
edit->setCommitCallback(onCommitSetDirty);
|
|
edit->setCommitOnFocusLost(TRUE);
|
|
edit->setCallbackUserData(this);
|
|
edit->setIgnoreTab(TRUE);
|
|
mReplaceEditor = edit;
|
|
|
|
combo = getChild<LLComboBox>( "modifier_combo");
|
|
combo->setCommitCallback(onCommitSetDirty);
|
|
combo->setCallbackUserData(this);
|
|
mModifierCombo = combo;
|
|
|
|
combo = getChild<LLComboBox>( "key_combo");
|
|
combo->setCommitCallback(onCommitSetDirty);
|
|
combo->setCallbackUserData(this);
|
|
mKeyCombo = combo;
|
|
|
|
list = getChild<LLScrollListCtrl>("library_list");
|
|
list->setCommitCallback(onCommitLibrary);
|
|
list->setDoubleClickCallback(onClickAdd);
|
|
list->setCallbackUserData(this);
|
|
mLibraryList = list;
|
|
|
|
btn = getChild<LLButton>( "add_btn");
|
|
btn->setClickedCallback(onClickAdd);
|
|
btn->setCallbackUserData(this);
|
|
btn->setEnabled(FALSE);
|
|
mAddBtn = btn;
|
|
|
|
btn = getChild<LLButton>( "up_btn");
|
|
btn->setClickedCallback(onClickUp);
|
|
btn->setCallbackUserData(this);
|
|
btn->setEnabled(FALSE);
|
|
mUpBtn = btn;
|
|
|
|
btn = getChild<LLButton>( "down_btn");
|
|
btn->setClickedCallback(onClickDown);
|
|
btn->setCallbackUserData(this);
|
|
btn->setEnabled(FALSE);
|
|
mDownBtn = btn;
|
|
|
|
btn = getChild<LLButton>( "delete_btn");
|
|
btn->setClickedCallback(onClickDelete);
|
|
btn->setCallbackUserData(this);
|
|
btn->setEnabled(FALSE);
|
|
mDeleteBtn = btn;
|
|
|
|
list = getChild<LLScrollListCtrl>("step_list");
|
|
list->setCommitCallback(onCommitStep);
|
|
list->setCallbackUserData(this);
|
|
mStepList = list;
|
|
|
|
// Options
|
|
text = getChild<LLTextBox>("options_text");
|
|
text->setBorderVisible(TRUE);
|
|
mOptionsText = text;
|
|
|
|
combo = getChild<LLComboBox>( "animation_list");
|
|
combo->setVisible(FALSE);
|
|
combo->setCommitCallback(onCommitAnimation);
|
|
combo->setCallbackUserData(this);
|
|
mAnimationCombo = combo;
|
|
|
|
LLRadioGroup* group;
|
|
group = getChild<LLRadioGroup>("animation_trigger_type");
|
|
group->setVisible(FALSE);
|
|
group->setCommitCallback(onCommitAnimationTrigger);
|
|
group->setCallbackUserData(this);
|
|
mAnimationRadio = group;
|
|
|
|
combo = getChild<LLComboBox>( "sound_list");
|
|
combo->setVisible(FALSE);
|
|
combo->setCommitCallback(onCommitSound);
|
|
combo->setCallbackUserData(this);
|
|
mSoundCombo = combo;
|
|
|
|
edit = getChild<LLLineEditor>("chat_editor");
|
|
edit->setVisible(FALSE);
|
|
edit->setCommitCallback(onCommitChat);
|
|
//edit->setKeystrokeCallback(onKeystrokeCommit);
|
|
edit->setCommitOnFocusLost(TRUE);
|
|
edit->setCallbackUserData(this);
|
|
edit->setIgnoreTab(TRUE);
|
|
mChatEditor = edit;
|
|
|
|
check = getChild<LLCheckBoxCtrl>( "wait_anim_check");
|
|
check->setVisible(FALSE);
|
|
check->setCommitCallback(onCommitWait);
|
|
check->setCallbackUserData(this);
|
|
mWaitAnimCheck = check;
|
|
|
|
check = getChild<LLCheckBoxCtrl>( "wait_time_check");
|
|
check->setVisible(FALSE);
|
|
check->setCommitCallback(onCommitWait);
|
|
check->setCallbackUserData(this);
|
|
mWaitTimeCheck = check;
|
|
|
|
edit = getChild<LLLineEditor>("wait_time_editor");
|
|
edit->setEnabled(FALSE);
|
|
edit->setVisible(FALSE);
|
|
edit->setPrevalidate(LLLineEditor::prevalidateFloat);
|
|
// edit->setKeystrokeCallback(onKeystrokeCommit);
|
|
edit->setCommitOnFocusLost(TRUE);
|
|
edit->setCommitCallback(onCommitWaitTime);
|
|
edit->setCallbackUserData(this);
|
|
edit->setIgnoreTab(TRUE);
|
|
mWaitTimeEditor = edit;
|
|
|
|
// Buttons at the bottom
|
|
check = getChild<LLCheckBoxCtrl>( "active_check");
|
|
check->setCommitCallback(onCommitActive);
|
|
check->setCallbackUserData(this);
|
|
mActiveCheck = check;
|
|
|
|
btn = getChild<LLButton>( "save_btn");
|
|
btn->setClickedCallback(onClickSave);
|
|
btn->setCallbackUserData(this);
|
|
mSaveBtn = btn;
|
|
|
|
btn = getChild<LLButton>( "preview_btn");
|
|
btn->setClickedCallback(onClickPreview);
|
|
btn->setCallbackUserData(this);
|
|
mPreviewBtn = btn;
|
|
|
|
|
|
// Populate the comboboxes
|
|
addModifiers();
|
|
addKeys();
|
|
addAnimations();
|
|
addSounds();
|
|
|
|
|
|
const LLInventoryItem* item = getItem();
|
|
|
|
if (item)
|
|
{
|
|
childSetCommitCallback("desc", LLPreview::onText, this);
|
|
childSetText("desc", item->getDescription());
|
|
childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void LLPreviewGesture::addModifiers()
|
|
{
|
|
LLComboBox* combo = mModifierCombo;
|
|
|
|
combo->add( NONE_LABEL, ADD_BOTTOM );
|
|
combo->add( SHIFT_LABEL, ADD_BOTTOM );
|
|
combo->add( CTRL_LABEL, ADD_BOTTOM );
|
|
combo->setCurrentByIndex(0);
|
|
}
|
|
|
|
void LLPreviewGesture::addKeys()
|
|
{
|
|
LLComboBox* combo = mKeyCombo;
|
|
|
|
combo->add( NONE_LABEL );
|
|
|
|
// <edit>
|
|
//define for the lulz
|
|
#define addKey(k) combo->add( LLKeyboard::stringFromKey(k), ADD_BOTTOM );
|
|
for (KEY key = KEY_F2; key <= KEY_F12; key++)
|
|
addKey(key)
|
|
for (KEY key = ' '; key <= '~'; key++)
|
|
addKey(key)
|
|
// </edit>
|
|
combo->setCurrentByIndex(0);
|
|
}
|
|
|
|
|
|
// TODO: Sort the legacy and non-legacy together?
|
|
void LLPreviewGesture::addAnimations()
|
|
{
|
|
LLComboBox* combo = mAnimationCombo;
|
|
|
|
combo->removeall();
|
|
|
|
std::string none_text = getString("none_text");
|
|
|
|
combo->add(none_text, LLUUID::null);
|
|
|
|
// Add all the default (legacy) animations
|
|
S32 i;
|
|
for (i = 0; i < gUserAnimStatesCount; ++i)
|
|
{
|
|
// Use the user-readable name
|
|
std::string label = LLAnimStateLabels::getStateLabel( gUserAnimStates[i].mName );
|
|
const LLUUID& id = gUserAnimStates[i].mID;
|
|
combo->add(label, id);
|
|
}
|
|
|
|
// Get all inventory items that are animations
|
|
LLViewerInventoryCategory::cat_array_t cats;
|
|
LLViewerInventoryItem::item_array_t items;
|
|
LLIsTypeWithPermissions is_copyable_animation(LLAssetType::AT_ANIMATION,
|
|
PERM_ITEM_UNRESTRICTED,
|
|
gAgent.getID(),
|
|
gAgent.getGroupID());
|
|
gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
|
|
cats,
|
|
items,
|
|
LLInventoryModel::EXCLUDE_TRASH,
|
|
is_copyable_animation);
|
|
|
|
// Copy into something we can sort
|
|
std::vector<LLInventoryItem*> animations;
|
|
|
|
S32 count = items.count();
|
|
for(i = 0; i < count; ++i)
|
|
{
|
|
animations.push_back( items.get(i) );
|
|
}
|
|
|
|
// Do the sort
|
|
std::sort(animations.begin(), animations.end(), SortItemPtrsByName());
|
|
|
|
// And load up the combobox
|
|
std::vector<LLInventoryItem*>::iterator it;
|
|
for (it = animations.begin(); it != animations.end(); ++it)
|
|
{
|
|
LLInventoryItem* item = *it;
|
|
|
|
combo->add(item->getName(), item->getAssetUUID(), ADD_BOTTOM);
|
|
}
|
|
}
|
|
|
|
|
|
void LLPreviewGesture::addSounds()
|
|
{
|
|
LLComboBox* combo = mSoundCombo;
|
|
combo->removeall();
|
|
|
|
std::string none_text = getString("none_text");
|
|
|
|
combo->add(none_text, LLUUID::null);
|
|
|
|
// Get all inventory items that are sounds
|
|
LLViewerInventoryCategory::cat_array_t cats;
|
|
LLViewerInventoryItem::item_array_t items;
|
|
LLIsTypeWithPermissions is_copyable_sound(LLAssetType::AT_SOUND,
|
|
PERM_ITEM_UNRESTRICTED,
|
|
gAgent.getID(),
|
|
gAgent.getGroupID());
|
|
gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
|
|
cats,
|
|
items,
|
|
LLInventoryModel::EXCLUDE_TRASH,
|
|
is_copyable_sound);
|
|
|
|
// Copy sounds into something we can sort
|
|
std::vector<LLInventoryItem*> sounds;
|
|
|
|
S32 i;
|
|
S32 count = items.count();
|
|
for(i = 0; i < count; ++i)
|
|
{
|
|
sounds.push_back( items.get(i) );
|
|
}
|
|
|
|
// Do the sort
|
|
std::sort(sounds.begin(), sounds.end(), SortItemPtrsByName());
|
|
|
|
// And load up the combobox
|
|
std::vector<LLInventoryItem*>::iterator it;
|
|
for (it = sounds.begin(); it != sounds.end(); ++it)
|
|
{
|
|
LLInventoryItem* item = *it;
|
|
|
|
combo->add(item->getName(), item->getAssetUUID(), ADD_BOTTOM);
|
|
}
|
|
}
|
|
|
|
|
|
void LLPreviewGesture::init(const LLUUID& item_id, const LLUUID& object_id)
|
|
{
|
|
// Sets ID and adds to instance list
|
|
setItemID(item_id);
|
|
setObjectID(object_id);
|
|
}
|
|
|
|
|
|
void LLPreviewGesture::refresh()
|
|
{
|
|
// If previewing or item is incomplete, all controls are disabled
|
|
LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
|
|
bool is_complete = (item && item->isComplete()) ? true : false;
|
|
if (mPreviewGesture || !is_complete)
|
|
{
|
|
|
|
childSetEnabled("desc", FALSE);
|
|
//mDescEditor->setEnabled(FALSE);
|
|
mTriggerEditor->setEnabled(FALSE);
|
|
mReplaceText->setEnabled(FALSE);
|
|
mReplaceEditor->setEnabled(FALSE);
|
|
mModifierCombo->setEnabled(FALSE);
|
|
mKeyCombo->setEnabled(FALSE);
|
|
mLibraryList->setEnabled(FALSE);
|
|
mAddBtn->setEnabled(FALSE);
|
|
mUpBtn->setEnabled(FALSE);
|
|
mDownBtn->setEnabled(FALSE);
|
|
mDeleteBtn->setEnabled(FALSE);
|
|
mStepList->setEnabled(FALSE);
|
|
mOptionsText->setEnabled(FALSE);
|
|
mAnimationCombo->setEnabled(FALSE);
|
|
mAnimationRadio->setEnabled(FALSE);
|
|
mSoundCombo->setEnabled(FALSE);
|
|
mChatEditor->setEnabled(FALSE);
|
|
mWaitAnimCheck->setEnabled(FALSE);
|
|
mWaitTimeCheck->setEnabled(FALSE);
|
|
mWaitTimeEditor->setEnabled(FALSE);
|
|
mActiveCheck->setEnabled(FALSE);
|
|
mSaveBtn->setEnabled(FALSE);
|
|
// <edit>
|
|
mStepList->setEnabled(TRUE);
|
|
// </edit>
|
|
|
|
// Make sure preview button is enabled, so we can stop it
|
|
mPreviewBtn->setEnabled(TRUE);
|
|
return;
|
|
}
|
|
|
|
BOOL modifiable = item->getPermissions().allowModifyBy(gAgent.getID());
|
|
|
|
childSetEnabled("desc", modifiable);
|
|
mTriggerEditor->setEnabled(TRUE);
|
|
mLibraryList->setEnabled(modifiable);
|
|
mStepList->setEnabled(modifiable);
|
|
mOptionsText->setEnabled(modifiable);
|
|
mAnimationCombo->setEnabled(modifiable);
|
|
mAnimationRadio->setEnabled(modifiable);
|
|
mSoundCombo->setEnabled(modifiable);
|
|
mChatEditor->setEnabled(modifiable);
|
|
mWaitAnimCheck->setEnabled(modifiable);
|
|
mWaitTimeCheck->setEnabled(modifiable);
|
|
mWaitTimeEditor->setEnabled(modifiable);
|
|
mActiveCheck->setEnabled(TRUE);
|
|
|
|
const std::string& trigger = mTriggerEditor->getText();
|
|
BOOL have_trigger = !trigger.empty();
|
|
|
|
const std::string& replace = mReplaceEditor->getText();
|
|
BOOL have_replace = !replace.empty();
|
|
|
|
LLScrollListItem* library_item = mLibraryList->getFirstSelected();
|
|
BOOL have_library = (library_item != NULL);
|
|
|
|
LLScrollListItem* step_item = mStepList->getFirstSelected();
|
|
S32 step_index = mStepList->getFirstSelectedIndex();
|
|
S32 step_count = mStepList->getItemCount();
|
|
BOOL have_step = (step_item != NULL);
|
|
|
|
mReplaceText->setEnabled(have_trigger || have_replace);
|
|
mReplaceEditor->setEnabled(have_trigger || have_replace);
|
|
|
|
mModifierCombo->setEnabled(TRUE);
|
|
mKeyCombo->setEnabled(TRUE);
|
|
|
|
mAddBtn->setEnabled(modifiable && have_library);
|
|
mUpBtn->setEnabled(modifiable && have_step && step_index > 0);
|
|
mDownBtn->setEnabled(modifiable && have_step && step_index < step_count-1);
|
|
mDeleteBtn->setEnabled(modifiable && have_step);
|
|
|
|
// Assume all not visible
|
|
mAnimationCombo->setVisible(FALSE);
|
|
mAnimationRadio->setVisible(FALSE);
|
|
mSoundCombo->setVisible(FALSE);
|
|
mChatEditor->setVisible(FALSE);
|
|
mWaitAnimCheck->setVisible(FALSE);
|
|
mWaitTimeCheck->setVisible(FALSE);
|
|
mWaitTimeEditor->setVisible(FALSE);
|
|
|
|
std::string optionstext;
|
|
|
|
if (have_step)
|
|
{
|
|
// figure out the type, show proper options, update text
|
|
LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
|
|
EStepType type = step->getType();
|
|
|
|
switch(type)
|
|
{
|
|
case STEP_ANIMATION:
|
|
{
|
|
LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
|
|
optionstext = getString("step_anim");
|
|
mAnimationCombo->setVisible(TRUE);
|
|
mAnimationRadio->setVisible(TRUE);
|
|
mAnimationRadio->setSelectedIndex((anim_step->mFlags & ANIM_FLAG_STOP) ? 1 : 0);
|
|
mAnimationCombo->setCurrentByID(anim_step->mAnimAssetID);
|
|
break;
|
|
}
|
|
case STEP_SOUND:
|
|
{
|
|
LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
|
|
optionstext = getString("step_sound");
|
|
mSoundCombo->setVisible(TRUE);
|
|
mSoundCombo->setCurrentByID(sound_step->mSoundAssetID);
|
|
break;
|
|
}
|
|
case STEP_CHAT:
|
|
{
|
|
LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
|
|
optionstext = getString("step_chat");
|
|
mChatEditor->setVisible(TRUE);
|
|
mChatEditor->setText(chat_step->mChatText);
|
|
break;
|
|
}
|
|
case STEP_WAIT:
|
|
{
|
|
LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
|
|
optionstext = getString("step_wait");
|
|
mWaitAnimCheck->setVisible(TRUE);
|
|
mWaitAnimCheck->set(wait_step->mFlags & WAIT_FLAG_ALL_ANIM);
|
|
mWaitTimeCheck->setVisible(TRUE);
|
|
mWaitTimeCheck->set(wait_step->mFlags & WAIT_FLAG_TIME);
|
|
mWaitTimeEditor->setVisible(TRUE);
|
|
std::string buffer = llformat("%.1f", (double)wait_step->mWaitSeconds);
|
|
mWaitTimeEditor->setText(buffer);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
mOptionsText->setText(optionstext);
|
|
|
|
BOOL active = gGestureManager.isGestureActive(mItemUUID);
|
|
mActiveCheck->set(active);
|
|
|
|
// Can only preview if there are steps
|
|
mPreviewBtn->setEnabled(step_count > 0);
|
|
|
|
// And can only save if changes have been made
|
|
mSaveBtn->setEnabled(mDirty);
|
|
addAnimations();
|
|
addSounds();
|
|
}
|
|
|
|
|
|
void LLPreviewGesture::initDefaultGesture()
|
|
{
|
|
LLScrollListItem* item;
|
|
item = addStep( STEP_ANIMATION );
|
|
LLGestureStepAnimation* anim = (LLGestureStepAnimation*)item->getUserdata();
|
|
anim->mAnimAssetID = ANIM_AGENT_HELLO;
|
|
anim->mAnimName = "Wave";
|
|
updateLabel(item);
|
|
|
|
item = addStep( STEP_WAIT );
|
|
LLGestureStepWait* wait = (LLGestureStepWait*)item->getUserdata();
|
|
wait->mFlags = WAIT_FLAG_ALL_ANIM;
|
|
updateLabel(item);
|
|
|
|
item = addStep( STEP_CHAT );
|
|
LLGestureStepChat* chat_step = (LLGestureStepChat*)item->getUserdata();
|
|
chat_step->mChatText = "Hello, avatar!";
|
|
updateLabel(item);
|
|
|
|
// Start with item list selected
|
|
mStepList->selectFirstItem();
|
|
|
|
// this is *new* content, so we are dirty
|
|
mDirty = TRUE;
|
|
}
|
|
|
|
|
|
void LLPreviewGesture::loadAsset()
|
|
{
|
|
const LLInventoryItem* item = getItem();
|
|
if (!item) return;
|
|
|
|
LLUUID asset_id = item->getAssetUUID();
|
|
if (asset_id.isNull())
|
|
{
|
|
// Freshly created gesture, don't need to load asset.
|
|
// Blank gesture will be fine.
|
|
initDefaultGesture();
|
|
refresh();
|
|
return;
|
|
}
|
|
|
|
// TODO: Based on item->getPermissions().allow*
|
|
// could enable/disable UI.
|
|
|
|
// Copy the UUID, because the user might close the preview
|
|
// window if the download gets stalled.
|
|
LLUUID* item_idp = new LLUUID(mItemUUID);
|
|
|
|
const BOOL high_priority = TRUE;
|
|
gAssetStorage->getAssetData(asset_id,
|
|
LLAssetType::AT_GESTURE,
|
|
onLoadComplete,
|
|
(void**)item_idp,
|
|
high_priority);
|
|
mAssetStatus = PREVIEW_ASSET_LOADING;
|
|
}
|
|
|
|
|
|
// static
|
|
void LLPreviewGesture::onLoadComplete(LLVFS *vfs,
|
|
const LLUUID& asset_uuid,
|
|
LLAssetType::EType type,
|
|
void* user_data, S32 status, LLExtStat ext_status)
|
|
{
|
|
LLUUID* item_idp = (LLUUID*)user_data;
|
|
LLPreview* preview = LLPreview::find(*item_idp);
|
|
if (preview)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)preview;
|
|
|
|
if (0 == status)
|
|
{
|
|
LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
|
|
S32 size = file.getSize();
|
|
|
|
char* buffer = new char[size+1];
|
|
file.read((U8*)buffer, size); /*Flawfinder: ignore*/
|
|
buffer[size] = '\0';
|
|
|
|
LLMultiGesture* gesture = new LLMultiGesture();
|
|
|
|
LLDataPackerAsciiBuffer dp(buffer, size+1);
|
|
BOOL ok = gesture->deserialize(dp);
|
|
|
|
if (ok)
|
|
{
|
|
// Everything has been successful. Load up the UI.
|
|
self->loadUIFromGesture(gesture);
|
|
|
|
self->mStepList->selectFirstItem();
|
|
|
|
self->mDirty = FALSE;
|
|
self->refresh();
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Unable to load gesture" << llendl;
|
|
}
|
|
|
|
delete gesture;
|
|
gesture = NULL;
|
|
|
|
delete [] buffer;
|
|
buffer = NULL;
|
|
|
|
self->mAssetStatus = PREVIEW_ASSET_LOADED;
|
|
}
|
|
else
|
|
{
|
|
LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
|
|
|
|
if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
|
|
LL_ERR_FILE_EMPTY == status)
|
|
{
|
|
LLDelayedGestureError::gestureMissing( *item_idp );
|
|
}
|
|
else
|
|
{
|
|
LLDelayedGestureError::gestureFailedToLoad( *item_idp );
|
|
}
|
|
|
|
llwarns << "Problem loading gesture: " << status << llendl;
|
|
self->mAssetStatus = PREVIEW_ASSET_ERROR;
|
|
}
|
|
}
|
|
delete item_idp;
|
|
item_idp = NULL;
|
|
}
|
|
|
|
|
|
void LLPreviewGesture::loadUIFromGesture(LLMultiGesture* gesture)
|
|
{
|
|
/*LLInventoryItem* item = getItem();
|
|
|
|
|
|
|
|
if (item)
|
|
{
|
|
LLLineEditor* descEditor = getChild<LLLineEditor>("desc");
|
|
descEditor->setText(item->getDescription());
|
|
}*/
|
|
|
|
mTriggerEditor->setText(gesture->mTrigger);
|
|
|
|
mReplaceEditor->setText(gesture->mReplaceText);
|
|
|
|
switch (gesture->mMask)
|
|
{
|
|
default:
|
|
case MASK_NONE:
|
|
mModifierCombo->setSimple( NONE_LABEL );
|
|
break;
|
|
case MASK_SHIFT:
|
|
mModifierCombo->setSimple( SHIFT_LABEL );
|
|
break;
|
|
case MASK_CONTROL:
|
|
mModifierCombo->setSimple( CTRL_LABEL );
|
|
break;
|
|
}
|
|
|
|
mKeyCombo->setCurrentByIndex(0);
|
|
if (gesture->mKey != KEY_NONE)
|
|
{
|
|
mKeyCombo->setSimple(LLKeyboard::stringFromKey(gesture->mKey));
|
|
}
|
|
|
|
// Make UI steps for each gesture step
|
|
S32 i;
|
|
S32 count = gesture->mSteps.size();
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
LLGestureStep* step = gesture->mSteps[i];
|
|
|
|
LLGestureStep* new_step = NULL;
|
|
|
|
switch(step->getType())
|
|
{
|
|
case STEP_ANIMATION:
|
|
{
|
|
LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
|
|
LLGestureStepAnimation* new_anim_step =
|
|
new LLGestureStepAnimation(*anim_step);
|
|
new_step = new_anim_step;
|
|
break;
|
|
}
|
|
case STEP_SOUND:
|
|
{
|
|
LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
|
|
LLGestureStepSound* new_sound_step =
|
|
new LLGestureStepSound(*sound_step);
|
|
new_step = new_sound_step;
|
|
break;
|
|
}
|
|
case STEP_CHAT:
|
|
{
|
|
LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
|
|
LLGestureStepChat* new_chat_step =
|
|
new LLGestureStepChat(*chat_step);
|
|
new_step = new_chat_step;
|
|
break;
|
|
}
|
|
case STEP_WAIT:
|
|
{
|
|
LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
|
|
LLGestureStepWait* new_wait_step =
|
|
new LLGestureStepWait(*wait_step);
|
|
new_step = new_wait_step;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!new_step) continue;
|
|
|
|
// Create an enabled item with this step
|
|
LLSD row;
|
|
row["columns"][0]["value"] = new_step->getLabel();
|
|
row["columns"][0]["font"] = "SANSSERIF_SMALL";
|
|
LLScrollListItem* item = mStepList->addElement(row);
|
|
item->setUserdata(new_step);
|
|
}
|
|
}
|
|
|
|
// Helpful structure so we can look up the inventory item
|
|
// after the save finishes.
|
|
struct LLSaveInfo
|
|
{
|
|
LLSaveInfo(const LLUUID& item_id, const LLUUID& object_id, const std::string& desc,
|
|
const LLTransactionID tid)
|
|
: mItemUUID(item_id), mObjectUUID(object_id), mDesc(desc), mTransactionID(tid)
|
|
{
|
|
}
|
|
|
|
LLUUID mItemUUID;
|
|
LLUUID mObjectUUID;
|
|
std::string mDesc;
|
|
LLTransactionID mTransactionID;
|
|
};
|
|
|
|
|
|
void LLPreviewGesture::saveIfNeeded()
|
|
{
|
|
if (!gAssetStorage)
|
|
{
|
|
llwarns << "Can't save gesture, no asset storage system." << llendl;
|
|
return;
|
|
}
|
|
|
|
if (!mDirty)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Copy the UI into a gesture
|
|
LLMultiGesture* gesture = createGesture();
|
|
|
|
// Serialize the gesture
|
|
S32 max_size = gesture->getMaxSerialSize();
|
|
char* buffer = new char[max_size];
|
|
|
|
LLDataPackerAsciiBuffer dp(buffer, max_size);
|
|
|
|
BOOL ok = gesture->serialize(dp);
|
|
|
|
// <edit>
|
|
//if (dp.getCurrentSize() > 1000)
|
|
if(0)
|
|
// </edit>
|
|
{
|
|
LLNotifications::instance().add("GestureSaveFailedTooManySteps");
|
|
|
|
delete gesture;
|
|
gesture = NULL;
|
|
}
|
|
else if (!ok)
|
|
{
|
|
LLNotifications::instance().add("GestureSaveFailedTryAgain");
|
|
delete gesture;
|
|
gesture = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Every save gets a new UUID. Yup.
|
|
LLTransactionID tid;
|
|
LLAssetID asset_id;
|
|
tid.generate();
|
|
asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
|
|
|
|
LLVFile file(gVFS, asset_id, LLAssetType::AT_GESTURE, LLVFile::APPEND);
|
|
|
|
S32 size = dp.getCurrentSize();
|
|
file.setMaxSize(size);
|
|
file.write((U8*)buffer, size);
|
|
|
|
BOOL delayedUpload = FALSE;
|
|
|
|
// Upload that asset to the database
|
|
LLViewerInventoryItem* item = (LLViewerInventoryItem*) getItem();
|
|
if (item)
|
|
{
|
|
std::string agent_url = gAgent.getRegion()->getCapability("UpdateGestureAgentInventory");
|
|
std::string task_url = gAgent.getRegion()->getCapability("UpdateGestureTaskInventory");
|
|
if (mObjectUUID.isNull() && !agent_url.empty())
|
|
{
|
|
//need to disable the preview floater so item
|
|
//isn't re-saved before new asset arrives
|
|
//fake out refresh.
|
|
item->setComplete(FALSE);
|
|
refresh();
|
|
item->setComplete(TRUE);
|
|
|
|
// Saving into agent inventory
|
|
LLSD body;
|
|
body["item_id"] = mItemUUID;
|
|
LLHTTPClient::post(agent_url, body,
|
|
new LLUpdateAgentInventoryResponder(body, asset_id, LLAssetType::AT_GESTURE));
|
|
delayedUpload = TRUE;
|
|
}
|
|
else if (!mObjectUUID.isNull() && !task_url.empty())
|
|
{
|
|
// Saving into task inventory
|
|
LLSD body;
|
|
body["task_id"] = mObjectUUID;
|
|
body["item_id"] = mItemUUID;
|
|
LLHTTPClient::post(task_url, body,
|
|
new LLUpdateTaskInventoryResponder(body, asset_id, LLAssetType::AT_GESTURE));
|
|
}
|
|
else if (gAssetStorage)
|
|
{
|
|
LLLineEditor* descEditor = getChild<LLLineEditor>("desc");
|
|
LLSaveInfo* info = new LLSaveInfo(mItemUUID, mObjectUUID, descEditor->getText(), tid);
|
|
gAssetStorage->storeAssetData(tid, LLAssetType::AT_GESTURE, onSaveComplete, info, FALSE);
|
|
}
|
|
}
|
|
|
|
// If this gesture is active, then we need to update the in-memory
|
|
// active map with the new pointer.
|
|
if (!delayedUpload && gGestureManager.isGestureActive(mItemUUID))
|
|
{
|
|
// gesture manager now owns the pointer
|
|
gGestureManager.replaceGesture(mItemUUID, gesture, asset_id);
|
|
|
|
// replaceGesture may deactivate other gestures so let the
|
|
// inventory know.
|
|
gInventory.notifyObservers();
|
|
}
|
|
else
|
|
{
|
|
// we're done with this gesture
|
|
delete gesture;
|
|
gesture = NULL;
|
|
}
|
|
|
|
mDirty = FALSE;
|
|
// refresh will be called when callback
|
|
// if triggered when delayedUpload
|
|
if(!delayedUpload)
|
|
{
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
delete [] buffer;
|
|
buffer = NULL;
|
|
}
|
|
|
|
|
|
// TODO: This is very similar to LLPreviewNotecard::onSaveComplete.
|
|
// Could merge code.
|
|
// static
|
|
void LLPreviewGesture::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
|
|
{
|
|
LLSaveInfo* info = (LLSaveInfo*)user_data;
|
|
if (info && (status == 0))
|
|
{
|
|
if(info->mObjectUUID.isNull())
|
|
{
|
|
// Saving into user inventory
|
|
LLViewerInventoryItem* item;
|
|
item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
|
|
if(item)
|
|
{
|
|
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
|
|
new_item->setDescription(info->mDesc);
|
|
new_item->setTransactionID(info->mTransactionID);
|
|
new_item->setAssetUUID(asset_uuid);
|
|
new_item->updateServer(FALSE);
|
|
gInventory.updateItem(new_item);
|
|
gInventory.notifyObservers();
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Inventory item for gesture " << info->mItemUUID
|
|
<< " is no longer in agent inventory." << llendl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Saving into in-world object inventory
|
|
LLViewerObject* object = gObjectList.findObject(info->mObjectUUID);
|
|
LLViewerInventoryItem* item = NULL;
|
|
if(object)
|
|
{
|
|
item = (LLViewerInventoryItem*)object->getInventoryObject(info->mItemUUID);
|
|
}
|
|
if(object && item)
|
|
{
|
|
item->setDescription(info->mDesc);
|
|
item->setAssetUUID(asset_uuid);
|
|
item->setTransactionID(info->mTransactionID);
|
|
object->updateInventory(item, TASK_INVENTORY_ITEM_KEY, false);
|
|
dialog_refresh_all();
|
|
}
|
|
else
|
|
{
|
|
LLNotifications::instance().add("GestureSaveFailedObjectNotFound");
|
|
}
|
|
}
|
|
|
|
// Find our window and close it if requested.
|
|
LLPreviewGesture* previewp = (LLPreviewGesture*)LLPreview::find(info->mItemUUID);
|
|
if (previewp && previewp->mCloseAfterSave)
|
|
{
|
|
previewp->close();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
llwarns << "Problem saving gesture: " << status << llendl;
|
|
LLSD args;
|
|
args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
|
|
LLNotifications::instance().add("GestureSaveFailedReason", args);
|
|
}
|
|
delete info;
|
|
info = NULL;
|
|
}
|
|
|
|
|
|
LLMultiGesture* LLPreviewGesture::createGesture()
|
|
{
|
|
LLMultiGesture* gesture = new LLMultiGesture();
|
|
|
|
gesture->mTrigger = mTriggerEditor->getText();
|
|
gesture->mReplaceText = mReplaceEditor->getText();
|
|
|
|
const std::string& modifier = mModifierCombo->getSimple();
|
|
if (modifier == CTRL_LABEL)
|
|
{
|
|
gesture->mMask = MASK_CONTROL;
|
|
}
|
|
else if (modifier == SHIFT_LABEL)
|
|
{
|
|
gesture->mMask = MASK_SHIFT;
|
|
}
|
|
else
|
|
{
|
|
gesture->mMask = MASK_NONE;
|
|
}
|
|
|
|
if (mKeyCombo->getCurrentIndex() == 0)
|
|
{
|
|
gesture->mKey = KEY_NONE;
|
|
}
|
|
else
|
|
{
|
|
const std::string& key_string = mKeyCombo->getSimple();
|
|
LLKeyboard::keyFromString(key_string, &(gesture->mKey));
|
|
}
|
|
|
|
std::vector<LLScrollListItem*> data_list = mStepList->getAllData();
|
|
std::vector<LLScrollListItem*>::iterator data_itor;
|
|
for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
|
|
{
|
|
LLScrollListItem* item = *data_itor;
|
|
LLGestureStep* step = (LLGestureStep*)item->getUserdata();
|
|
|
|
switch(step->getType())
|
|
{
|
|
case STEP_ANIMATION:
|
|
{
|
|
// Copy UI-generated step into actual gesture step
|
|
LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
|
|
LLGestureStepAnimation* new_anim_step =
|
|
new LLGestureStepAnimation(*anim_step);
|
|
gesture->mSteps.push_back(new_anim_step);
|
|
break;
|
|
}
|
|
case STEP_SOUND:
|
|
{
|
|
// Copy UI-generated step into actual gesture step
|
|
LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
|
|
LLGestureStepSound* new_sound_step =
|
|
new LLGestureStepSound(*sound_step);
|
|
gesture->mSteps.push_back(new_sound_step);
|
|
break;
|
|
}
|
|
case STEP_CHAT:
|
|
{
|
|
// Copy UI-generated step into actual gesture step
|
|
LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
|
|
LLGestureStepChat* new_chat_step =
|
|
new LLGestureStepChat(*chat_step);
|
|
gesture->mSteps.push_back(new_chat_step);
|
|
break;
|
|
}
|
|
case STEP_WAIT:
|
|
{
|
|
// Copy UI-generated step into actual gesture step
|
|
LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
|
|
LLGestureStepWait* new_wait_step =
|
|
new LLGestureStepWait(*wait_step);
|
|
gesture->mSteps.push_back(new_wait_step);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return gesture;
|
|
}
|
|
|
|
|
|
// static
|
|
void LLPreviewGesture::updateLabel(LLScrollListItem* item)
|
|
{
|
|
LLGestureStep* step = (LLGestureStep*)item->getUserdata();
|
|
|
|
LLScrollListCell* cell = item->getColumn(0);
|
|
LLScrollListText* text_cell = (LLScrollListText*)cell;
|
|
std::string label = step->getLabel();
|
|
text_cell->setText(label);
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitSetDirty(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitLibrary(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* library_item = self->mLibraryList->getFirstSelected();
|
|
if (library_item)
|
|
{
|
|
self->mStepList->deselectAllItems();
|
|
self->refresh();
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitStep(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* step_item = self->mStepList->getFirstSelected();
|
|
if (!step_item) return;
|
|
|
|
self->mLibraryList->deselectAllItems();
|
|
self->refresh();
|
|
}
|
|
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitAnimation(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* step_item = self->mStepList->getFirstSelected();
|
|
if (step_item)
|
|
{
|
|
LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
|
|
if (step->getType() == STEP_ANIMATION)
|
|
{
|
|
// Assign the animation name
|
|
LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
|
|
if (self->mAnimationCombo->getCurrentIndex() == 0)
|
|
{
|
|
anim_step->mAnimName.clear();
|
|
anim_step->mAnimAssetID.setNull();
|
|
}
|
|
else
|
|
{
|
|
anim_step->mAnimName = self->mAnimationCombo->getSimple();
|
|
anim_step->mAnimAssetID = self->mAnimationCombo->getCurrentID();
|
|
}
|
|
//anim_step->mFlags = 0x0;
|
|
|
|
// Update the UI label in the list
|
|
updateLabel(step_item);
|
|
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitAnimationTrigger(LLUICtrl* ctrl, void *data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* step_item = self->mStepList->getFirstSelected();
|
|
if (step_item)
|
|
{
|
|
LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
|
|
if (step->getType() == STEP_ANIMATION)
|
|
{
|
|
LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
|
|
if (self->mAnimationRadio->getSelectedIndex() == 0)
|
|
{
|
|
// start
|
|
anim_step->mFlags &= ~ANIM_FLAG_STOP;
|
|
}
|
|
else
|
|
{
|
|
// stop
|
|
anim_step->mFlags |= ANIM_FLAG_STOP;
|
|
}
|
|
// Update the UI label in the list
|
|
updateLabel(step_item);
|
|
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitSound(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* step_item = self->mStepList->getFirstSelected();
|
|
if (step_item)
|
|
{
|
|
LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
|
|
if (step->getType() == STEP_SOUND)
|
|
{
|
|
// Assign the sound name
|
|
LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
|
|
sound_step->mSoundName = self->mSoundCombo->getSimple();
|
|
sound_step->mSoundAssetID = self->mSoundCombo->getCurrentID();
|
|
sound_step->mFlags = 0x0;
|
|
|
|
// Update the UI label in the list
|
|
updateLabel(step_item);
|
|
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitChat(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* step_item = self->mStepList->getFirstSelected();
|
|
if (!step_item) return;
|
|
|
|
LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
|
|
if (step->getType() != STEP_CHAT) return;
|
|
|
|
LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
|
|
chat_step->mChatText = self->mChatEditor->getText();
|
|
chat_step->mFlags = 0x0;
|
|
|
|
// Update the UI label in the list
|
|
updateLabel(step_item);
|
|
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitWait(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* step_item = self->mStepList->getFirstSelected();
|
|
if (!step_item) return;
|
|
|
|
LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
|
|
if (step->getType() != STEP_WAIT) return;
|
|
|
|
LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
|
|
U32 flags = 0x0;
|
|
if (self->mWaitAnimCheck->get()) flags |= WAIT_FLAG_ALL_ANIM;
|
|
if (self->mWaitTimeCheck->get()) flags |= WAIT_FLAG_TIME;
|
|
wait_step->mFlags = flags;
|
|
|
|
{
|
|
LLLocale locale(LLLocale::USER_LOCALE);
|
|
|
|
F32 wait_seconds = (F32)atof(self->mWaitTimeEditor->getText().c_str());
|
|
if (wait_seconds < 0.f) wait_seconds = 0.f;
|
|
if (wait_seconds > 3600.f) wait_seconds = 3600.f;
|
|
wait_step->mWaitSeconds = wait_seconds;
|
|
}
|
|
|
|
// Enable the input area if necessary
|
|
self->mWaitTimeEditor->setEnabled(self->mWaitTimeCheck->get());
|
|
|
|
// Update the UI label in the list
|
|
updateLabel(step_item);
|
|
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitWaitTime(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* step_item = self->mStepList->getFirstSelected();
|
|
if (!step_item) return;
|
|
|
|
LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
|
|
if (step->getType() != STEP_WAIT) return;
|
|
|
|
self->mWaitTimeCheck->set(TRUE);
|
|
onCommitWait(ctrl, data);
|
|
}
|
|
|
|
|
|
// static
|
|
void LLPreviewGesture::onKeystrokeCommit(LLLineEditor* caller,
|
|
void* data)
|
|
{
|
|
// Just commit every keystroke
|
|
onCommitSetDirty(caller, data);
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onClickAdd(void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* library_item = self->mLibraryList->getFirstSelected();
|
|
if (!library_item) return;
|
|
|
|
S32 library_item_index = self->mLibraryList->getFirstSelectedIndex();
|
|
|
|
const LLScrollListCell* library_cell = library_item->getColumn(0);
|
|
const std::string& library_text = library_cell->getValue().asString();
|
|
|
|
if( library_item_index >= STEP_EOF )
|
|
{
|
|
llerrs << "Unknown step type: " << library_text << llendl;
|
|
return;
|
|
}
|
|
|
|
self->addStep( (EStepType)library_item_index );
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
|
|
LLScrollListItem* LLPreviewGesture::addStep( const EStepType step_type )
|
|
{
|
|
// Order of enum EStepType MUST match the library_list element in floater_preview_gesture.xml
|
|
|
|
LLGestureStep* step = NULL;
|
|
switch( step_type)
|
|
{
|
|
case STEP_ANIMATION:
|
|
step = new LLGestureStepAnimation();
|
|
break;
|
|
case STEP_SOUND:
|
|
step = new LLGestureStepSound();
|
|
break;
|
|
case STEP_CHAT:
|
|
step = new LLGestureStepChat();
|
|
break;
|
|
case STEP_WAIT:
|
|
step = new LLGestureStepWait();
|
|
break;
|
|
default:
|
|
llerrs << "Unknown step type: " << (S32)step_type << llendl;
|
|
return NULL;
|
|
}
|
|
|
|
// Create an enabled item with this step
|
|
LLSD row;
|
|
row["columns"][0]["value"] = step->getLabel();
|
|
row["columns"][0]["font"] = "SANSSERIF_SMALL";
|
|
LLScrollListItem* step_item = mStepList->addElement(row);
|
|
step_item->setUserdata(step);
|
|
|
|
// And move selection to the list on the right
|
|
mLibraryList->deselectAllItems();
|
|
mStepList->deselectAllItems();
|
|
|
|
step_item->setSelected(TRUE);
|
|
|
|
return step_item;
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onClickUp(void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
S32 selected_index = self->mStepList->getFirstSelectedIndex();
|
|
if (selected_index > 0)
|
|
{
|
|
self->mStepList->swapWithPrevious(selected_index);
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onClickDown(void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
S32 selected_index = self->mStepList->getFirstSelectedIndex();
|
|
if (selected_index < 0) return;
|
|
|
|
S32 count = self->mStepList->getItemCount();
|
|
if (selected_index < count-1)
|
|
{
|
|
self->mStepList->swapWithNext(selected_index);
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onClickDelete(void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
LLScrollListItem* item = self->mStepList->getFirstSelected();
|
|
S32 selected_index = self->mStepList->getFirstSelectedIndex();
|
|
if (item && selected_index >= 0)
|
|
{
|
|
LLGestureStep* step = (LLGestureStep*)item->getUserdata();
|
|
delete step;
|
|
step = NULL;
|
|
|
|
self->mStepList->deleteSingleItem(selected_index);
|
|
|
|
self->mDirty = TRUE;
|
|
self->refresh();
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onCommitActive(LLUICtrl* ctrl, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
if (!gGestureManager.isGestureActive(self->mItemUUID))
|
|
{
|
|
gGestureManager.activateGesture(self->mItemUUID);
|
|
}
|
|
else
|
|
{
|
|
gGestureManager.deactivateGesture(self->mItemUUID);
|
|
}
|
|
|
|
// Make sure the (active) label in the inventory gets updated.
|
|
LLViewerInventoryItem* item = gInventory.getItem(self->mItemUUID);
|
|
if (item)
|
|
{
|
|
gInventory.updateItem(item);
|
|
gInventory.notifyObservers();
|
|
}
|
|
|
|
self->refresh();
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onClickSave(void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
self->saveIfNeeded();
|
|
}
|
|
|
|
// static
|
|
void LLPreviewGesture::onClickPreview(void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
if (!self->mPreviewGesture)
|
|
{
|
|
// make temporary gesture
|
|
self->mPreviewGesture = self->createGesture();
|
|
|
|
// add a callback
|
|
self->mPreviewGesture->mDoneCallback = onDonePreview;
|
|
self->mPreviewGesture->mCallbackData = self;
|
|
|
|
// set the button title
|
|
self->mPreviewBtn->setLabel(self->getString("stop_txt"));
|
|
|
|
// play it, and delete when done
|
|
gGestureManager.playGesture(self->mPreviewGesture);
|
|
|
|
self->refresh();
|
|
}
|
|
else
|
|
{
|
|
// Will call onDonePreview() below
|
|
gGestureManager.stopGesture(self->mPreviewGesture);
|
|
|
|
self->refresh();
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
void LLPreviewGesture::onDonePreview(LLMultiGesture* gesture, void* data)
|
|
{
|
|
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
|
|
|
self->mPreviewBtn->setLabel(self->getString("preview_txt"));
|
|
|
|
delete self->mPreviewGesture;
|
|
self->mPreviewGesture = NULL;
|
|
|
|
self->refresh();
|
|
}
|