Files
SingularityViewer/indra/newview/llviewermenufile.cpp
Lirusaito 0846f8a963 Fixeds and ToDones
-Color Fixies-
Fixed Gemini skin not looking as it originally did.
We now use DefaultListText to color all otherwise uncolored text in scroll list cells.
All of our dark skins have been updated to use white here, as dark skins are intended to have white text...
This includes the Dark we ship with.

LLFloaterActiveSpeakers no longer uses hard coded font colors, it uses SpeakersInactive, DefaultListText, and SpeakersGhost.

LLFloaterAvatarList no longer uses hard coded font colors, it uses DefaultListText, RadarTextChatRange, RadarTextShoutRange, and RadarTextDrawDist, in place of previously hard coded font colors
Since the tooltip defines color meaning, these new colors should only be skinned to change shade.

DefaultListText defaults to black; SpeaksInactive, gray(grey4); SpeakersGhost, Red; RadarTextChatRange, Red; RadarTextShoutRange, Yellow(yellow1); RadarTextDrawDist, Green(green2).


-Translation update and fixies-
Partial credit to viewer-development, thanks for hanging onto old strings!  Also, updated to look more like v-d in translated areas.
Punctuation strings can be used quite a lot.  Brought them all in, just in case.

AscentPrefs*:
Drag and Drop points now use CurrentlySetTo, CurrentlyNotSet, AnItemNotOnThisAccount, and NotLoggedIn. (applies, as well, to FloaterAO)
Power User message is now built from PowerUser1, PowerUser2, Unlocked:, PowerUser3, RightClick, PowerUser4 and PowerUser5; this should give translators enough space to explain any tough to translate, and make the message easier to alter in the future, if necessary.

LLCompileQueue:
Now uses translation strings from upstream
Starting, Done, Resetting, Running, and NotRunning in its xml.
CompileQueueTitle, CompileQueueStart, CompileQueueDownloadedCompiling, CompileQueueScriptNotFound, CompileQueueProblemDownloading, CompileQueueInsufficientPermDownload, CompileQueueInsufficientPermFor, CompileQueueUnknownFailure, ResetQueueTitle, ResetQueueStart, NotRunQueueTitle, and NotRunQueueStart from strings.

LLFloaterAvatarList(floater_radar) now uses has_entered, has_left, the_sim, draw_distance, shout_range, and chat_range in its xml for translatable alerts.

LLFloaterLand(floater_about_land) now uses minutes, 1_minute, 1_second, seconds, and remaining from its xml.
LLFloaterLand, LLFolderView, LLPanelDirBrowser now make use of InventoryNoMatchingItems, NoneFound, and Searching in their searches.
LLFolderView was brought closer to v-d in translation.

LLGroupNotify now uses GroupNotifyGroupNotice, GroupNotifySentBy, GroupNotifyAttached, next (also now used by LLNotify), ok, GroupNotifyGroupNotices, GroupNotifyViewPastNotices, GroupNotifyOpenAttachment, and GroupNotifySaveAttachment.

LLInventoryFilter synced with V-D for translation:
Now uses Animations, Calling Cards, Clothing, Gestures, Landmarks, Notecards, Objects, Scripts, Sounds, Textures, Snapshots, No Filters, Since Logoff, and Worn.

LLManipRotate now uses Direction_Forward, Direction_Left, Direction_Right, Direction_Back, Direction_North, Direction_South, Direction_West, Direction_East, Direction_Up, and Direction_Down, like upstream v-d.

LLPanelAvatar(panel_avatar) now uses None string in its xml for when there are no groups, also removed cruft. Though the None has not been showing up for quite some time, anyway...

LLPanelObjectInventory now uses Buy, LoadingContents and NoContents,
however the last two strings did not seem to show up anyway...
thanks to Latif Khalifa, Jean Horten, theGenius Indigo and Cubbi Bearcat for confirming this happens across many different versions and on both Windows and linux(32 and 64).

LLPreviewScript now uses CompileSuccessful, SaveComplete, ObjectOutOfRange, and CompileSuccessfulSaving.

LLScrollingPanelParam now translates Less and More.
Avatar Shape Information strings now used for customize appearance panels.

LLTextureCtrl now uses multiple_textures.

LLToolpie has been updated to use v-d include order and now uses UnmuteAvatar, MuteAvatar, UnmuteObject, and MuteObject2 strings.
LLVOAvatarSelf now uses BodyParts* strings for translation of toolpie entries pertaining to bodyparts.

LLViewerMenuFile now uses UnknownFileExtension, and UploadingCosts.

LLViewerMessage now uses Cancel(also used by LLViewerDisplay), AcquiredItems, Saved_message, IM_autoresponse_sent_item, IM_autoresponded_to, IM_announce_incoming, InvOfferDecline, InvOfferGaveYou, InvOfferOwnedByUnknownUser, InvOfferAnObjectNamed, InvOfferOwnedBy, InvOfferOwnedByUnknownGroup, and InvOfferOwnedByGroup.


-AvatarRadar Update-
AvatarRadar enhanced with code from both Cool VL Viewer and some avian corpse.
e_radar_alert_type has been reworked to allow bitwise usage in the future.

Cool stuff:
gSavedSettings for radar are now CachedControls,
entering boolean is now checked before going into the switchcase,
Style changes, yay C++ style.

Avian Flu:
Distance for shout range corrected to be 96.
handleKeyHere: If the user hits enter with an avatar on the radar selected, focus camera on selected avatar; ctrl-enter, teleport to selected avatar.

Otherwise:
Tiny spelling fixies, and a suggestive comment.
2012-08-05 20:40:02 -04:00

1363 lines
42 KiB
C++

/**
* @file llviewermenufile.cpp
* @brief "File" menu in the main menu bar.
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-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"
// system libraries
#include <boost/tokenizer.hpp>
#include "llviewermenufile.h"
// linden libraries
#include "lleconomy.h"
#include "llhttpclient.h"
#include "llimage.h"
#include "llmemberlistener.h"
#include "llnotificationsutil.h"
#include "llsdserialize.h"
#include "llsdutil.h"
#include "llstring.h"
#include "lltrans.h"
#include "lltransactiontypes.h"
#include "lluictrlfactory.h"
#include "lluuid.h"
#include "llvorbisencode.h"
#include "message.h"
// project includes
#include "llagent.h"
#include "llagentcamera.h"
#include "llappviewer.h"
#include "llassetuploadresponders.h"
#ifdef MESH_UPLOAD
#include "llfloatermodelpreview.h"
#endif
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagebmp.h"
#include "statemachine/aifilepicker.h"
#include "llfloateranimpreview.h"
#include "llfloaterbuycurrency.h"
#include "llfloaterimagepreview.h"
#include "llfloaternamedesc.h"
#include "llfloatersnapshot.h"
#include "llinventorymodel.h" // gInventory
#include "llresourcedata.h"
#include "llfloaterperms.h"
#include "llstatusbar.h"
#include "llviewercontrol.h" // gSavedSettings
#include "llviewertexturelist.h"
#include "lluictrlfactory.h"
#include "llviewermenu.h" // gMenuHolder
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewerwindow.h"
#include "lluploaddialog.h"
// <edit>
#include "llselectmgr.h"
#include "llassettype.h"
#include "llinventorytype.h"
#include "llbvhloader.h"
#include "lllocalinventory.h"
// </edit>
#include "hippogridmanager.h"
#include "importtracker.h"
using namespace LLOldEvents;
std::deque<std::string> gUploadQueue;
typedef LLMemberListener<LLView> view_listener_t;
class LLFileEnableSaveAs : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
bool new_value = gFloaterView->getFrontmost() &&
gFloaterView->getFrontmost()->canSaveAs();
gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
return true;
}
};
class LLFileEnableUpload : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
bool new_value = gStatusBar && LLGlobalEconomy::Singleton::getInstance() &&
gStatusBar->getBalance() >= LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
return true;
}
};
//============================================================================
#if LL_WINDOWS
static std::string SOUND_EXTENSIONS = "wav";
static std::string IMAGE_EXTENSIONS = "tga bmp jpg jpeg png";
static std::string ANIM_EXTENSIONS = "bvh anim animatn";
#ifdef _CORY_TESTING
static std::string GEOMETRY_EXTENSIONS = "slg";
#endif
static std::string XML_EXTENSIONS = "xml";
static std::string SLOBJECT_EXTENSIONS = "slobject";
#endif
static std::string ALL_FILE_EXTENSIONS = "*.*";
std::string build_extensions_string(ELoadFilter filter)
{
switch(filter)
{
#if LL_WINDOWS
case FFLOAD_IMAGE:
return IMAGE_EXTENSIONS;
case FFLOAD_WAV:
return SOUND_EXTENSIONS;
case FFLOAD_ANIM:
return ANIM_EXTENSIONS;
case FFLOAD_SLOBJECT:
return SLOBJECT_EXTENSIONS;
#ifdef _CORY_TESTING
case FFLOAD_GEOMETRY:
return GEOMETRY_EXTENSIONS;
#endif
case FFLOAD_XML:
return XML_EXTENSIONS;
case FFLOAD_ALL:
return ALL_FILE_EXTENSIONS;
#endif
default:
return ALL_FILE_EXTENSIONS;
}
}
class AIFileUpload {
public:
AIFileUpload(void) { }
virtual ~AIFileUpload() { }
public:
bool is_valid(std::string const& filename, ELoadFilter type);
void filepicker_callback(ELoadFilter type, AIFilePicker* picker);
void start_filepicker(ELoadFilter type, char const* context);
protected:
virtual void handle_event(std::string const& filename) = 0;
};
void AIFileUpload::start_filepicker(ELoadFilter filter, char const* context)
{
if( gAgentCamera.cameraMouselook() )
{
gAgentCamera.changeCameraToDefault();
// This doesn't seem necessary. JC
// display();
}
AIFilePicker* picker = AIFilePicker::create();
picker->open(filter, "", context);
// Note that when the call back is called then we're still in the main loop of
// the viewer and therefore the AIFileUpload still exists, since that is only
// destructed at the end of main when exiting the viewer.
picker->run(boost::bind(&AIFileUpload::filepicker_callback, this, filter, picker));
}
void AIFileUpload::filepicker_callback(ELoadFilter type, AIFilePicker* picker)
{
if (picker->hasFilename())
{
std::string filename = picker->getFilename();
if (is_valid(filename, type))
handle_event(filename);
}
}
bool AIFileUpload::is_valid(std::string const& filename, ELoadFilter type)
{
std::string ext = gDirUtilp->getExtension(filename);
//strincmp doesn't like NULL pointers
if (ext.empty())
{
std::string short_name = gDirUtilp->getBaseFileName(filename);
// No extension
LLSD args;
args["FILE"] = short_name;
LLNotifications::instance().add("NoFileExtension", args);
return false;
}
else
{
//so there is an extension
//loop over the valid extensions and compare to see
//if the extension is valid
//now grab the set of valid file extensions
std::string valid_extensions = build_extensions_string(type);
BOOL ext_valid = FALSE;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep(" ");
tokenizer tokens(valid_extensions, sep);
tokenizer::iterator token_iter;
//now loop over all valid file extensions
//and compare them to the extension of the file
//to be uploaded
for( token_iter = tokens.begin();
token_iter != tokens.end() && ext_valid != TRUE;
++token_iter)
{
const std::string& cur_token = *token_iter;
if (cur_token == ext || cur_token == "*.*")
{
//valid extension
//or the acceptable extension is any
ext_valid = TRUE;
}
}//end for (loop over all tokens)
if (ext_valid == FALSE)
{
//should only get here if the extension exists
//but is invalid
LLSD args;
args["EXTENSION"] = ext;
args["VALIDS"] = valid_extensions;
LLNotifications::instance().add("InvalidFileExtension", args);
return false;
}
}//end else (non-null extension)
//valid file extension
//now we check to see
//if the file is actually a valid image/sound/etc.
//Consider completely disabling this, see how SL handles it. Maybe we can get full song uploads again! -HgB
if (type == FFLOAD_WAV)
{
// pre-qualify wavs to make sure the format is acceptable
std::string error_msg;
if (check_for_invalid_wav_formats(filename,error_msg))
{
llinfos << error_msg << ": " << filename << llendl;
LLSD args;
args["FILE"] = filename;
LLNotifications::instance().add( error_msg, args );
return false;
}
}//end if a wave/sound file
return true;
}
class LLFileUploadImage : public view_listener_t, public AIFileUpload
{
public:
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
start_filepicker(FFLOAD_IMAGE, "image");
return true;
}
protected:
// Inherited from AIFileUpload.
/*virtual*/ void handle_event(std::string const& filename)
{
LLFloaterImagePreview* floaterp = new LLFloaterImagePreview(filename);
LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_image_preview.xml");
}
};
class LLFileUploadSound : public view_listener_t, public AIFileUpload
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
start_filepicker(FFLOAD_WAV, "sound");
return true;
}
protected:
// Inherited from AIFileUpload.
/*virtual*/ void handle_event(std::string const& filename)
{
LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename);
LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_sound_preview.xml");
floaterp->childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload() ));
}
};
class LLFileUploadAnim : public view_listener_t, public AIFileUpload
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
start_filepicker(FFLOAD_ANIM, "animations");
return true;
}
protected:
// Inherited from AIFileUpload.
/*virtual*/ void handle_event(std::string const& filename)
{
LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename);
LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_animation_preview.xml");
}
};
class LLFileUploadBulk : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
if( gAgentCamera.cameraMouselook() )
{
gAgentCamera.changeCameraToDefault();
}
// TODO:
// Iterate over all files
// Check extensions for uploadability, cost
// Check user balance for entire cost
// Charge user entire cost
// Loop, uploading
// If an upload fails, refund the user for that one
//
// Also fix single upload to charge first, then refund
// <edit>
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
LLSD args;
std::string msg = "Would you like to bulk upload the files as temporary files?\nOnly textures will upload as temporary on Agni and Aditi.";
if(expected_upload_cost)
msg.append(llformat("\nWARNING: Each upload costs L$%d if it's not temporary.",expected_upload_cost));
args["MESSAGE"] = msg;
LLNotifications::instance().add("GenericAlertYesNoCancel", args, LLSD(), onConfirmBulkUploadTemp);
return true;
}
static bool onConfirmBulkUploadTemp(const LLSD& notification, const LLSD& response )
{
S32 option = LLNotification::getSelectedOption(notification, response);
bool enabled;
if (option == 0) // yes
enabled = true;
else if(option == 1) // no
enabled = false;
else // cancel
return false;
AIFilePicker* filepicker = AIFilePicker::create();
filepicker->open(FFLOAD_ALL, "", "openfile", true);
filepicker->run(boost::bind(&LLFileUploadBulk::onConfirmBulkUploadTemp_continued, enabled, filepicker));
return true;
}
static void onConfirmBulkUploadTemp_continued(bool enabled, AIFilePicker* filepicker)
{
if (filepicker->hasFilename())
{
std::vector<std::string> const& file_names(filepicker->getFilenames());
for (std::vector<std::string>::const_iterator iter = file_names.begin(); iter != file_names.end(); ++iter)
{
std::string const& filename(*iter);
if (filename.empty())
continue;
std::string name = gDirUtilp->getBaseFileName(filename, true);
std::string asset_name = name;
LLStringUtil::replaceNonstandardASCII( asset_name, '?' );
LLStringUtil::replaceChar(asset_name, '|', '?');
LLStringUtil::stripNonprintable(asset_name);
LLStringUtil::trim(asset_name);
std::string display_name = LLStringUtil::null;
LLAssetStorage::LLStoreAssetCallback callback = NULL;
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
void *userdata = NULL;
gSavedSettings.setBOOL("TemporaryUpload", enabled);
upload_new_resource(filename, asset_name, asset_name, 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(),
display_name, callback, expected_upload_cost, userdata);
}
}
else
{
gSavedSettings.setBOOL("TemporaryUpload", FALSE);
}
}
};
void upload_error(const std::string& error_message, const std::string& label, const std::string& filename, const LLSD& args)
{
llwarns << error_message << llendl;
LLNotifications::instance().add(label, args);
if(LLFile::remove(filename) == -1)
{
lldebugs << "unable to remove temp file" << llendl;
}
//AIFIXME? LLFilePicker::instance().reset();
}
class LLFileEnableCloseWindow : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
bool new_value = NULL != LLFloater::getClosableFloaterFromFocus();
// horrendously opaque, this code
gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
return true;
}
};
class LLFileCloseWindow : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
LLFloater::closeFocusedFloater();
return true;
}
};
class LLFileEnableCloseAllWindows : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
bool open_children = gFloaterView->allChildrenClosed();
gMenuHolder->findControl(userdata["control"].asString())->setValue(!open_children);
return true;
}
};
class LLFileCloseAllWindows : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
bool app_quitting = false;
gFloaterView->closeAllChildren(app_quitting);
return true;
}
};
// <edit>
class LLFileMinimizeAllWindows : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
gFloaterView->minimizeAllChildren();
return true;
}
};
// </edit>
class LLFileSavePreview : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
LLFloater* top = gFloaterView->getFrontmost();
if (top)
{
top->saveAs();
}
return true;
}
};
class LLFileTakeSnapshot : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
LLFloaterSnapshot::show(NULL);
return true;
}
};
class LLFileTakeSnapshotToDisk : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
LLPointer<LLImageRaw> raw = new LLImageRaw;
S32 width = gViewerWindow->getWindowDisplayWidth();
S32 height = gViewerWindow->getWindowDisplayHeight();
F32 supersample = 1.f;
if (gSavedSettings.getBOOL("HighResSnapshot"))
{
#if 1//SHY_MOD // screenshot improvement
const F32 mult = gSavedSettings.getF32("SHHighResSnapshotScale");
width *= mult;
height *= mult;
static const LLCachedControl<F32> super_sample_scale("SHHighResSnapshotSuperSample",1.f);
supersample = super_sample_scale;
#else //shy_mod
width *= 2;
height *= 2;
#endif //ignore
}
if (gViewerWindow->rawSnapshot(raw,
width,
height,
TRUE,
FALSE,
gSavedSettings.getBOOL("RenderUIInSnapshot"),
FALSE,
LLViewerWindow::SNAPSHOT_TYPE_COLOR,
6144,
supersample))
{
LLPointer<LLImageFormatted> formatted;
switch(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat")))
{
case LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG:
formatted = new LLImageJPEG(gSavedSettings.getS32("SnapshotQuality"));
break;
case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG:
formatted = new LLImagePNG;
break;
case LLFloaterSnapshot::SNAPSHOT_FORMAT_BMP:
formatted = new LLImageBMP;
break;
default:
llwarns << "Unknown Local Snapshot format" << llendl;
return true;
}
formatted->enableOverSize() ;
formatted->encode(raw, 0);
formatted->disableOverSize();
gViewerWindow->saveImageNumbered(formatted);
}
return true;
}
};
class LLFileLogOut : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
std::string command(gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() + gDirUtilp->getExecutableFilename());
gSavedSettings.setBOOL("ShowConsoleWindow", FALSE);
gViewerWindow->getWindow()->ShellEx(command);
gSavedSettings.setBOOL("ShowConsoleWindow", FALSE);
LLAppViewer::instance()->userQuit();
gSavedSettings.setBOOL("ShowConsoleWindow", FALSE);
return true;
}
};
class LLFileQuit : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
LLAppViewer::instance()->userQuit();
return true;
}
};
static void handle_compress_image_continued(AIFilePicker* filepicker);
void handle_compress_image(void*)
{
AIFilePicker* filepicker = AIFilePicker::create();
filepicker->open(FFLOAD_IMAGE, "", "openfile", true);
filepicker->run(boost::bind(&handle_compress_image_continued, filepicker));
}
static void handle_compress_image_continued(AIFilePicker* filepicker)
{
if (!filepicker->hasFilename())
return;
std::vector<std::string> const& filenames(filepicker->getFilenames());
for(std::vector<std::string>::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename)
{
std::string const& infile(*filename);
std::string outfile = infile + ".j2c";
llinfos << "Input: " << infile << llendl;
llinfos << "Output: " << outfile << llendl;
BOOL success;
success = LLViewerTextureList::createUploadFile(infile, outfile, IMG_CODEC_TGA);
if (success)
{
llinfos << "Compression complete" << llendl;
}
else
{
llinfos << "Compression failed: " << LLImage::getLastError() << llendl;
}
}
}
void upload_new_resource(const std::string& src_filename, std::string name,
std::string desc, S32 compression_info,
LLFolderType::EType destination_folder_type,
LLInventoryType::EType inv_type,
U32 next_owner_perms,
U32 group_perms,
U32 everyone_perms,
const std::string& display_name,
LLAssetStorage::LLStoreAssetCallback callback,
S32 expected_upload_cost,
void *userdata)
{
// Generate the temporary UUID.
std::string filename = gDirUtilp->getTempFilename();
LLTransactionID tid;
LLAssetID uuid;
LLSD args;
std::string exten = gDirUtilp->getExtension(src_filename);
U32 codec = LLImageBase::getCodecFromExtension(exten);
LLAssetType::EType asset_type = LLAssetType::AT_NONE;
std::string error_message;
BOOL error = FALSE;
if (exten.empty())
{
std::string short_name = gDirUtilp->getBaseFileName(filename);
// No extension
error_message = llformat(
"No file extension for the file: '%s'\nPlease make sure the file has a correct file extension",
short_name.c_str());
args["FILE"] = short_name;
upload_error(error_message, "NofileExtension", filename, args);
return;
}
else if (codec != IMG_CODEC_INVALID)
{
// It's an image file, the upload procedure is the same for all
asset_type = LLAssetType::AT_TEXTURE;
if (!LLViewerTextureList::createUploadFile(src_filename, filename, codec))
{
error_message = llformat( "Problem with file %s:\n\n%s\n",
src_filename.c_str(), LLImage::getLastError().c_str());
args["FILE"] = src_filename;
args["ERROR"] = LLImage::getLastError();
upload_error(error_message, "ProblemWithFile", filename, args);
return;
}
}
else if(exten == "wav")
{
asset_type = LLAssetType::AT_SOUND; // tag it as audio
S32 encode_result = 0;
llinfos << "Attempting to encode wav as an ogg file" << llendl;
encode_result = encode_vorbis_file(src_filename, filename);
if (LLVORBISENC_NOERR != encode_result)
{
switch(encode_result)
{
case LLVORBISENC_DEST_OPEN_ERR:
error_message = llformat( "Couldn't open temporary compressed sound file for writing: %s\n", filename.c_str());
args["FILE"] = filename;
upload_error(error_message, "CannotOpenTemporarySoundFile", filename, args);
break;
default:
error_message = llformat("Unknown vorbis encode failure on: %s\n", src_filename.c_str());
args["FILE"] = src_filename;
upload_error(error_message, "UnknownVorbisEncodeFailure", filename, args);
break;
}
return;
}
}
else if(exten == "ogg")
{
asset_type = LLAssetType::AT_SOUND; // tag it as audio
filename = src_filename;
}
else if(exten == "tmp")
{
// This is a generic .lin resource file
asset_type = LLAssetType::AT_OBJECT;
LLFILE* in = LLFile::fopen(src_filename, "rb"); /* Flawfinder: ignore */
if (in)
{
// read in the file header
char buf[16384]; /* Flawfinder: ignore */
size_t readbytes;
S32 version;
if (fscanf(in, "LindenResource\nversion %d\n", &version))
{
if (2 == version)
{
// *NOTE: This buffer size is hard coded into scanf() below.
char label[MAX_STRING]; /* Flawfinder: ignore */
char value[MAX_STRING]; /* Flawfinder: ignore */
S32 tokens_read;
while (fgets(buf, 1024, in))
{
label[0] = '\0';
value[0] = '\0';
tokens_read = sscanf( /* Flawfinder: ignore */
buf,
"%254s %254s\n",
label, value);
llinfos << "got: " << label << " = " << value
<< llendl;
if (EOF == tokens_read)
{
fclose(in);
error_message = llformat("corrupt resource file: %s", src_filename.c_str());
args["FILE"] = src_filename;
upload_error(error_message, "CorruptResourceFile", filename, args);
return;
}
if (2 == tokens_read)
{
if (! strcmp("type", label))
{
asset_type = (LLAssetType::EType)(atoi(value));
}
}
else
{
if (! strcmp("_DATA_", label))
{
// below is the data section
break;
}
}
// other values are currently discarded
}
}
else
{
fclose(in);
error_message = llformat("unknown linden resource file version in file: %s", src_filename.c_str());
args["FILE"] = src_filename;
upload_error(error_message, "UnknownResourceFileVersion", filename, args);
return;
}
}
else
{
// this is an original binary formatted .lin file
// start over at the beginning of the file
fseek(in, 0, SEEK_SET);
const S32 MAX_ASSET_DESCRIPTION_LENGTH = 256;
const S32 MAX_ASSET_NAME_LENGTH = 64;
S32 header_size = 34 + MAX_ASSET_DESCRIPTION_LENGTH + MAX_ASSET_NAME_LENGTH;
S16 type_num;
// read in and throw out most of the header except for the type
if (fread(buf, header_size, 1, in) != 1)
{
llwarns << "Short read" << llendl;
}
memcpy(&type_num, buf + 16, sizeof(S16)); /* Flawfinder: ignore */
asset_type = (LLAssetType::EType)type_num;
}
// copy the file's data segment into another file for uploading
LLFILE* out = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
if (out)
{
while((readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
{
if (fwrite(buf, 1, readbytes, out) != readbytes)
{
llwarns << "Short write" << llendl;
}
}
fclose(out);
}
else
{
fclose(in);
error_message = llformat( "Unable to create output file: %s", filename.c_str());
args["FILE"] = filename;
upload_error(error_message, "UnableToCreateOutputFile", filename, args);
return;
}
fclose(in);
}
else
{
llinfos << "Couldn't open .lin file " << src_filename << llendl;
}
}
else if (exten == "bvh")
{
error_message = llformat("We do not currently support bulk upload of animation files\n");
upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args);
return;
}
// <edit>
else if (exten == "anim" || exten == "animatn")
{
asset_type = LLAssetType::AT_ANIMATION;
filename = src_filename;
}
else if(exten == "j2k" || exten == "jp2" || exten == "j2c")
{
asset_type = LLAssetType::AT_TEXTURE;
filename = src_filename;
}
// </edit>
else
{
// Unknown extension
error_message = llformat(LLTrans::getString("UnknownFileExtension").c_str(), exten.c_str());
error = TRUE;;
}
// gen a new transaction ID for this asset
tid.generate();
if (!error)
{
uuid = tid.makeAssetID(gAgent.getSecureSessionID());
// copy this file into the vfs for upload
S32 file_size;
LLAPRFile infile(filename, LL_APR_RB, &file_size);
if (infile.getFileHandle())
{
LLVFile file(gVFS, uuid, asset_type, LLVFile::WRITE);
file.setMaxSize(file_size);
const S32 buf_size = 65536;
U8 copy_buf[buf_size];
while ((file_size = infile.read(copy_buf, buf_size)))
{
file.write(copy_buf, file_size);
}
}
else
{
error_message = llformat( "Unable to access output file: %s", filename.c_str());
error = TRUE;
}
}
if (!error)
{
std::string t_disp_name = display_name;
if (t_disp_name.empty())
{
t_disp_name = src_filename;
}
// <edit> hack to create scripts and gestures
if(exten == "lsl" || exten == "gesture" || exten == "notecard") // added notecard Oct 15 2009
{
LLInventoryType::EType inv_type = LLInventoryType::IT_GESTURE;
if(exten == "lsl") inv_type = LLInventoryType::IT_LSL;
else if(exten == "gesture") inv_type = LLInventoryType::IT_GESTURE;
else if(exten == "notecard") inv_type = LLInventoryType::IT_NOTECARD;
create_inventory_item( gAgent.getID(),
gAgent.getSessionID(),
gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(asset_type)),
LLTransactionID::tnull,
name,
uuid.asString(), // fake asset id, but in vfs
asset_type,
inv_type,
NOT_WEARABLE,
PERM_ITEM_UNRESTRICTED,
new NewResourceItemCallback);
}
else
// </edit>
upload_new_resource(tid, asset_type, name, desc, compression_info, // tid
destination_folder_type, inv_type, next_owner_perms, group_perms, everyone_perms,
display_name, callback, expected_upload_cost, userdata);
}
else
{
llwarns << error_message << llendl;
LLSD args;
args["ERROR_MESSAGE"] = error_message;
LLNotificationsUtil::add("ErrorMessage", args);
if(LLFile::remove(filename) == -1)
{
lldebugs << "unable to remove temp file" << llendl;
}
//AIFIXME? LLFilePicker::instance().reset();
}
}
// <edit>
void temp_upload_callback(const LLUUID& uuid, void* user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed)
{
LLResourceData* data = (LLResourceData*)user_data;
if(result >= 0)
{
LLUUID item_id;
item_id.generate();
LLPermissions* perms = new LLPermissions();
perms->set(LLPermissions::DEFAULT);
perms->setOwnerAndGroup(gAgentID, gAgentID, gAgentID, false);
perms->setMaskBase(PERM_ALL);
perms->setMaskOwner(PERM_ALL);
perms->setMaskEveryone(PERM_ALL);
perms->setMaskGroup(PERM_ALL);
perms->setMaskNext(PERM_ALL);
LLUUID destination = gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE);
BOOL bUseSystemInventory = (gSavedSettings.getBOOL("AscentUseSystemFolder") && gSavedSettings.getBOOL("AscentSystemTemporary"));
if (bUseSystemInventory)
{
destination = gSystemFolderAssets;
}
LLViewerInventoryItem* item = new LLViewerInventoryItem(
item_id,
destination,
*perms,
uuid,
(LLAssetType::EType)data->mAssetInfo.mType,
(LLInventoryType::EType)data->mInventoryType,
data->mAssetInfo.getName(),
data->mAssetInfo.getDescription(),
LLSaleInfo::DEFAULT,
0,
time_corrected());
if (bUseSystemInventory)
{
LLLocalInventory::addItem(item);
}
else
{
item->updateServer(TRUE);
gInventory.updateItem(item);
gInventory.notifyObservers();
}
}
else
{
LLSD args;
args["FILE"] = LLInventoryType::lookupHumanReadable(data->mInventoryType);
args["REASON"] = std::string(LLAssetStorage::getErrorString(result));
LLNotificationsUtil::add("CannotUploadReason", args);
}
LLUploadDialog::modalUploadFinished();
delete data;
}
// <edit>
void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed)
{
LLResourceData* data = (LLResourceData*)user_data;
S32 expected_upload_cost = data ? data->mExpectedUploadCost : 0;
//LLAssetType::EType pref_loc = data->mPreferredLocation;
BOOL is_balance_sufficient = TRUE;
if(!data)
{
LLUploadDialog::modalUploadFinished();
return;
}
if(result >= 0)
{
LLFolderType::EType dest_loc = (data->mPreferredLocation == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(data->mAssetInfo.mType) : data->mPreferredLocation;
if (LLAssetType::AT_SOUND == data->mAssetInfo.mType ||
LLAssetType::AT_TEXTURE == data->mAssetInfo.mType ||
LLAssetType::AT_ANIMATION == data->mAssetInfo.mType)
{
// Charge the user for the upload.
LLViewerRegion* region = gAgent.getRegion();
if(!(can_afford_transaction(expected_upload_cost)))
{
LLStringUtil::format_map_t args;
args["[NAME]"] = data->mAssetInfo.getName();
args["[CURRENCY]"] = gHippoGridManager->getConnectedGrid()->getCurrencySymbol();
args["[AMOUNT]"] = llformat("%d", expected_upload_cost);
LLFloaterBuyCurrency::buyCurrency( LLTrans::getString("UploadingCosts", args), expected_upload_cost );
is_balance_sufficient = FALSE;
}
else if(region)
{
// Charge user for upload
gStatusBar->debitBalance(expected_upload_cost);
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_MoneyTransferRequest);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_MoneyData);
msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID());
msg->addUUIDFast(_PREHASH_DestID, LLUUID::null);
msg->addU8("Flags", 0);
// we tell the sim how much we were expecting to pay so it
// can respond to any discrepancy
msg->addS32Fast(_PREHASH_Amount, expected_upload_cost);
msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY);
msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY);
msg->addS32Fast(_PREHASH_TransactionType, TRANS_UPLOAD_CHARGE);
msg->addStringFast(_PREHASH_Description, NULL);
msg->sendReliable(region->getHost());
}
}
if(is_balance_sufficient)
{
// Actually add the upload to inventory
llinfos << "Adding " << uuid << " to inventory." << llendl;
const LLUUID folder_id = gInventory.findCategoryUUIDForType(dest_loc);
if(folder_id.notNull())
{
U32 next_owner_perms = data->mNextOwnerPerm;
if(PERM_NONE == next_owner_perms)
{
next_owner_perms = PERM_MOVE | PERM_TRANSFER;
}
create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
folder_id, data->mAssetInfo.mTransactionID, data->mAssetInfo.getName(),
data->mAssetInfo.getDescription(), data->mAssetInfo.mType,
data->mInventoryType, NOT_WEARABLE, next_owner_perms,
LLPointer<LLInventoryCallback>(NULL));
}
else
{
llwarns << "Can't find a folder to put it in" << llendl;
}
}
}
else // if(result >= 0)
{
LLSD args;
args["FILE"] = LLInventoryType::lookupHumanReadable(data->mInventoryType);
args["REASON"] = std::string(LLAssetStorage::getErrorString(result));
LLNotificationsUtil::add("CannotUploadReason", args);
}
LLUploadDialog::modalUploadFinished();
delete data;
data = NULL;
}
static LLAssetID upload_new_resource_prep(const LLTransactionID& tid,
LLAssetType::EType asset_type,
LLInventoryType::EType& inventory_type,
std::string& name,
const std::string& display_name,
std::string& description)
{
LLAssetID uuid = generate_asset_id_for_new_upload(tid);
increase_new_upload_stats(asset_type);
assign_defaults_and_show_upload_message(asset_type,
inventory_type,
name,
display_name,
description);
return uuid;
}
LLSD generate_new_resource_upload_capability_body(LLAssetType::EType asset_type,
const std::string& name,
const std::string& desc,
LLFolderType::EType destination_folder_type,
LLInventoryType::EType inv_type,
U32 next_owner_perms,
U32 group_perms,
U32 everyone_perms)
{
LLSD body;
body["folder_id"] = gInventory.findCategoryUUIDForType(destination_folder_type == LLFolderType::FT_NONE ?
LLFolderType::assetTypeToFolderType(asset_type) :
destination_folder_type);
body["asset_type"] = LLAssetType::lookup(asset_type);
body["inventory_type"] = LLInventoryType::lookup(inv_type);
body["name"] = name;
body["description"] = desc;
body["next_owner_mask"] = LLSD::Integer(next_owner_perms);
body["group_mask"] = LLSD::Integer(group_perms);
body["everyone_mask"] = LLSD::Integer(everyone_perms);
return body;
}
void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_type,
std::string name,
std::string desc, S32 compression_info,
LLFolderType::EType destination_folder_type,
LLInventoryType::EType inv_type,
U32 next_owner_perms,
U32 group_perms,
U32 everyone_perms,
const std::string& display_name,
LLAssetStorage::LLStoreAssetCallback callback,
S32 expected_upload_cost,
void *userdata)
{
if(gDisconnected)
{
return ;
}
LLAssetID uuid = upload_new_resource_prep(tid, asset_type, inv_type,
name, display_name, desc);
llinfos << "*** Uploading: "
<< "\nType: " << LLAssetType::lookup(asset_type)
<< "\nUUID: " << uuid
<< "\nName: " << name
<< "\nDesc: " << desc
<< "\nFolder: "
<< gInventory.findCategoryUUIDForType(destination_folder_type == LLFolderType::FT_NONE ?
LLFolderType::assetTypeToFolderType(asset_type) :
destination_folder_type)
<< "\nExpected Upload Cost: " << expected_upload_cost << llendl;
std::string url = gAgent.getRegion()->getCapability("NewFileAgentInventory");
// <edit>
BOOL temporary = gSavedSettings.getBOOL("TemporaryUpload");
gSavedSettings.setBOOL("TemporaryUpload",FALSE);
if (!url.empty() && !temporary)
// </edit>
{
llinfos << "New Agent Inventory via capability" << llendl;
LLSD body;
body["folder_id"] = gInventory.findCategoryUUIDForType((destination_folder_type == LLFolderType::FT_NONE) ? LLFolderType::assetTypeToFolderType(asset_type) : destination_folder_type);
body["asset_type"] = LLAssetType::lookup(asset_type);
body["inventory_type"] = LLInventoryType::lookup(inv_type);
body["name"] = name;
body["description"] = desc;
body["next_owner_mask"] = LLSD::Integer(next_owner_perms);
body["group_mask"] = LLSD::Integer(group_perms);
body["everyone_mask"] = LLSD::Integer(everyone_perms);
body["expected_upload_cost"] = LLSD::Integer(expected_upload_cost);
LLHTTPClient::post(url, body,
new LLNewAgentInventoryResponder(body, uuid, asset_type));
}
else
{
// <edit>
if(!temporary)
{
llinfos << "NewAgentInventory capability not found, new agent inventory via asset system." << llendl;
// check for adequate funds
// TODO: do this check on the sim
if (LLAssetType::AT_SOUND == asset_type ||
LLAssetType::AT_TEXTURE == asset_type ||
LLAssetType::AT_ANIMATION == asset_type)
{
S32 balance = gStatusBar->getBalance();
if (balance < expected_upload_cost)
{
// insufficient funds, bail on this upload
LLFloaterBuyCurrency::buyCurrency("Uploading costs", expected_upload_cost);
return;
}
}
}
LLResourceData* data = new LLResourceData;
data->mAssetInfo.mTransactionID = tid;
data->mAssetInfo.mUuid = uuid;
data->mAssetInfo.mType = asset_type;
data->mAssetInfo.mCreatorID = gAgentID;
data->mInventoryType = inv_type;
data->mNextOwnerPerm = next_owner_perms;
data->mExpectedUploadCost = expected_upload_cost;
data->mUserData = userdata;
data->mAssetInfo.setName(name);
data->mAssetInfo.setDescription(desc);
data->mPreferredLocation = destination_folder_type;
LLAssetStorage::LLStoreAssetCallback asset_callback = temporary ? &temp_upload_callback : &upload_done_callback;
if (callback)
{
asset_callback = callback;
}
gAssetStorage->storeAssetData(data->mAssetInfo.mTransactionID, data->mAssetInfo.mType,
asset_callback,
(void*)data,
temporary,
TRUE,
temporary);
}
}
LLAssetID generate_asset_id_for_new_upload(const LLTransactionID& tid)
{
LLAssetID uuid;
if (gDisconnected)
{
uuid.setNull();
}
else
{
uuid = tid.makeAssetID(gAgent.getSecureSessionID());
}
return uuid;
}
void increase_new_upload_stats(LLAssetType::EType asset_type)
{
if (LLAssetType::AT_SOUND == asset_type)
{
LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_SOUND_COUNT);
}
else if (LLAssetType::AT_TEXTURE == asset_type)
{
LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_TEXTURE_COUNT);
}
else if (LLAssetType::AT_ANIMATION == asset_type)
{
LLViewerStats::getInstance()->incStat(LLViewerStats::ST_UPLOAD_ANIM_COUNT);
}
}
void assign_defaults_and_show_upload_message(LLAssetType::EType asset_type,
LLInventoryType::EType& inventory_type,
std::string& name,
const std::string& display_name,
std::string& description)
{
if (LLInventoryType::IT_NONE == inventory_type)
{
inventory_type = LLInventoryType::defaultForAssetType(asset_type);
}
LLStringUtil::stripNonprintable(name);
LLStringUtil::stripNonprintable(description);
if (name.empty())
{
name = "(No Name)";
}
if (description.empty())
{
description = "(No Description)";
}
// At this point, we're ready for the upload.
std::string upload_message = "Uploading...\n\n";
upload_message.append(display_name);
LLUploadDialog::modalUploadDialog(upload_message);
}
void init_menu_file()
{
(new LLFileUploadImage())->registerListener(gMenuHolder, "File.UploadImage");
(new LLFileUploadSound())->registerListener(gMenuHolder, "File.UploadSound");
(new LLFileUploadAnim())->registerListener(gMenuHolder, "File.UploadAnim");
(new LLFileUploadBulk())->registerListener(gMenuHolder, "File.UploadBulk");
(new LLFileCloseWindow())->registerListener(gMenuHolder, "File.CloseWindow");
(new LLFileCloseAllWindows())->registerListener(gMenuHolder, "File.CloseAllWindows");
(new LLFileEnableCloseWindow())->registerListener(gMenuHolder, "File.EnableCloseWindow");
(new LLFileEnableCloseAllWindows())->registerListener(gMenuHolder, "File.EnableCloseAllWindows");
// <edit>
(new LLFileMinimizeAllWindows())->registerListener(gMenuHolder, "File.MinimizeAllWindows");
// </edit>
(new LLFileSavePreview())->registerListener(gMenuHolder, "File.SavePreview");
(new LLFileTakeSnapshot())->registerListener(gMenuHolder, "File.TakeSnapshot");
(new LLFileTakeSnapshotToDisk())->registerListener(gMenuHolder, "File.TakeSnapshotToDisk");
(new LLFileQuit())->registerListener(gMenuHolder, "File.Quit");
(new LLFileLogOut())->registerListener(gMenuHolder, "File.LogOut");
(new LLFileEnableUpload())->registerListener(gMenuHolder, "File.EnableUpload");
(new LLFileEnableSaveAs())->registerListener(gMenuHolder, "File.EnableSaveAs");
}
// <edit>
void NewResourceItemCallback::fire(const LLUUID& new_item_id)
{
LLViewerInventoryItem* new_item = (LLViewerInventoryItem*)gInventory.getItem(new_item_id);
if(!new_item) return;
LLUUID vfile_id = LLUUID(new_item->getDescription());
if(vfile_id.isNull()) return;
new_item->setDescription("(No Description)");
new_item->updateServer(FALSE);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
std::string agent_url;
LLSD body;
body["item_id"] = new_item_id;
if(new_item->getType() == LLAssetType::AT_GESTURE)
{
agent_url = gAgent.getRegion()->getCapability("UpdateGestureAgentInventory");
}
else if(new_item->getType() == LLAssetType::AT_LSL_TEXT)
{
agent_url = gAgent.getRegion()->getCapability("UpdateScriptAgent");
body["target"] = "lsl2";
}
else if(new_item->getType() == LLAssetType::AT_NOTECARD)
{
agent_url = gAgent.getRegion()->getCapability("UpdateNotecardAgentInventory");
}
else
{
return;
}
if(agent_url.empty()) return;
LLHTTPClient::post(agent_url, body,
new LLUpdateAgentInventoryResponder(body, vfile_id, new_item->getType()));
}
// </edit>