diff --git a/indra/llvfs/llvfile.h b/indra/llvfs/llvfile.h
index 5f69a4104..8db45d30b 100644
--- a/indra/llvfs/llvfile.h
+++ b/indra/llvfs/llvfile.h
@@ -80,10 +80,11 @@ public:
static const S32 READ_WRITE;
static const S32 APPEND;
-protected:
LLAssetType::EType mFileType;
LLUUID mFileID;
+
+protected:
S32 mPosition;
S32 mMode;
LLVFS *mVFS;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index a38a746cc..8c89e75f6 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -76,6 +76,7 @@ set(viewer_SOURCE_FILES
llanimstatelabels.cpp
llao.cpp
llappviewer.cpp
+ llassetconverter.cpp
llassetuploadresponders.cpp
llassetuploadqueue.cpp
llaudiosourcevo.cpp
@@ -213,6 +214,7 @@ set(viewer_SOURCE_FILES
llfloatertos.cpp
llfloaterurldisplay.cpp
llfloaterurlentry.cpp
+ llfloatervfs.cpp
llfloatervoicedevicesettings.cpp
llfloaterwater.cpp
llfloaterwindlight.cpp
@@ -511,6 +513,7 @@ set(viewer_HEADER_FILES
llao.h
llappearance.h
llappviewer.h
+ llassetconverter.h
llassetuploadresponders.h
llassetuploadqueue.h
llaudiosourcevo.h
@@ -649,6 +652,7 @@ set(viewer_HEADER_FILES
llfloatertos.h
llfloaterurldisplay.h
llfloaterurlentry.h
+ llfloatervfs.h
llfloatervoicedevicesettings.h
llfloaterwater.h
llfloaterwindlight.h
diff --git a/indra/newview/llassetconverter.cpp b/indra/newview/llassetconverter.cpp
new file mode 100644
index 000000000..b0a14dbc7
--- /dev/null
+++ b/indra/newview/llassetconverter.cpp
@@ -0,0 +1,178 @@
+//
+#include "llviewerprecompiledheaders.h"
+#include "llvfs.h"
+#include "llapr.h"
+#include "llvfile.h"
+#include "llassetconverter.h"
+#include "llviewerimagelist.h"
+#include "llvorbisencode.h"
+#include "llbvhloader.h"
+// static
+LLAssetType::EType LLAssetConverter::convert(std::string src_filename, std::string filename)
+{
+ std::string exten = gDirUtilp->getExtension(src_filename);
+ LLAssetType::EType asset_type = LLAssetType::AT_NONE;
+ if (exten.empty())
+ return LLAssetType::AT_NONE;
+ else if(exten == "bmp")
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_BMP ))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if( exten == "tga")
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_TGA ))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if( exten == "jpg" || exten == "jpeg")
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_JPEG ))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if( exten == "png")
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_PNG ))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if(exten == "wav")
+ {
+ asset_type = LLAssetType::AT_SOUND;
+ if(encode_vorbis_file(src_filename, filename) != LLVORBISENC_NOERR)
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if(exten == "ogg")
+ {
+ asset_type = LLAssetType::AT_SOUND;
+ if(!copyFile(src_filename, filename))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ //else if(exten == "tmp") FIXME
+ else if (exten == "bvh")
+ {
+ asset_type = LLAssetType::AT_ANIMATION;
+ S32 file_size;
+ LLAPRFile fp;
+ fp.open(src_filename, LL_APR_RB, LLAPRFile::global, &file_size);
+ if(!fp.getFileHandle()) return LLAssetType::AT_NONE;
+ char* file_buffer = new char[file_size + 1];
+ if(fp.read(file_buffer, file_size) == 0) //not sure if this is right, gotta check this one
+ {
+ fp.close();
+ delete[] file_buffer;
+ return LLAssetType::AT_NONE;
+ }
+ LLBVHLoader* loaderp = new LLBVHLoader(file_buffer);
+ if(!loaderp->isInitialized())
+ {
+ fp.close();
+ delete[] file_buffer;
+ return LLAssetType::AT_NONE;
+ }
+ S32 buffer_size = loaderp->getOutputSize();
+ U8* buffer = new U8[buffer_size];
+ LLDataPackerBinaryBuffer dp(buffer, buffer_size);
+ loaderp->serialize(dp);
+ LLAPRFile apr_file(filename, LL_APR_WB, LLAPRFile::global);
+ apr_file.write(buffer, buffer_size);
+ delete[] file_buffer;
+ delete[] buffer;
+ fp.close();
+ apr_file.close();
+ }
+ else if (exten == "animatn")
+ {
+ asset_type = LLAssetType::AT_ANIMATION;
+ if(!copyFile(src_filename, filename))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if(exten == "jp2" || exten == "j2k" || exten == "j2c")
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if(!copyFile(src_filename, filename))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if(exten == "gesture")
+ {
+ asset_type = LLAssetType::AT_GESTURE;
+ if(!copyFile(src_filename, filename))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if(exten == "notecard")
+ {
+ asset_type = LLAssetType::AT_NOTECARD;
+ if(!copyFile(src_filename, filename))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if(exten == "lsl")
+ {
+ asset_type = LLAssetType::AT_LSL_TEXT;
+ if(!copyFile(src_filename, filename))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else if(exten == "eyes" || exten == "gloves" || exten == "hair" || exten == "jacket" || exten == "pants" || exten == "shape" || exten == "shirt" || exten == "shoes" || exten == "skin" || exten == "skirt" || exten == "socks" || exten == "underpants" || exten == "undershirt" || exten == "bodypart" || exten == "clothing")
+ {
+ asset_type = LLAssetType::AT_CLOTHING;
+ if(!copyFile(src_filename, filename))
+ {
+ return LLAssetType::AT_NONE;
+ }
+ }
+ else
+ {
+ llwarns << "Unhandled extension" << llendl;
+ return LLAssetType::AT_NONE;
+ }
+ return asset_type;
+}
+BOOL LLAssetConverter::copyFile(std::string src_filename, std::string dst_filename)
+{
+ S32 src_size;
+ LLAPRFile src_fp;
+ src_fp.open(src_filename, LL_APR_RB, LLAPRFile::global, &src_size);
+ if(!src_fp.getFileHandle()) return FALSE;
+ LLAPRFile dst_fp;
+ dst_fp.open(dst_filename, LL_APR_WB, LLAPRFile::global);
+ if(!dst_fp.getFileHandle()) return FALSE;
+ char* buffer = new char[src_size + 1];
+ src_fp.read(buffer, src_size);
+ dst_fp.write(buffer, src_size);
+ src_fp.close();
+ dst_fp.close();
+ delete[] buffer;
+ return TRUE;
+}
+//
diff --git a/indra/newview/llassetconverter.h b/indra/newview/llassetconverter.h
new file mode 100644
index 000000000..87f264b1d
--- /dev/null
+++ b/indra/newview/llassetconverter.h
@@ -0,0 +1,15 @@
+//
+#ifndef LL_LLASSETCONVERTER_H
+#define LL_LLASSETCONVERTER_H
+
+#include "llcommon.h"
+#include "llassettype.h"
+
+class LLAssetConverter
+{
+public:
+ static LLAssetType::EType convert(std::string src_filename, std::string filename);
+ static BOOL copyFile(std::string src_filename, std::string dest_filename);
+};
+#endif
+//
diff --git a/indra/newview/llfloatervfs.cpp b/indra/newview/llfloatervfs.cpp
new file mode 100644
index 000000000..b7e75e89c
--- /dev/null
+++ b/indra/newview/llfloatervfs.cpp
@@ -0,0 +1,332 @@
+//
+#include "llviewerprecompiledheaders.h"
+#include "llfloatervfs.h"
+#include "lluictrlfactory.h"
+#include "llscrolllistctrl.h"
+#include "llfilepicker.h"
+#include "lllocalinventory.h"
+#include "llviewerwindow.h"
+#include "llassetconverter.h"
+LLFloaterVFS* LLFloaterVFS::sInstance;
+std::list LLFloaterVFS::mFiles;
+LLFloaterVFS::LLFloaterVFS()
+: LLFloater(),
+ mEditID(LLUUID::null)
+{
+ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_vfs.xml");
+}
+LLFloaterVFS::~LLFloaterVFS()
+{
+ sInstance = NULL;
+}
+// static
+void LLFloaterVFS::show()
+{
+ if(sInstance)
+ sInstance->open();
+ else
+ {
+ sInstance = new LLFloaterVFS();
+ sInstance->open();
+ }
+}
+BOOL LLFloaterVFS::postBuild()
+{
+ childSetAction("add_btn", onClickAdd, this);
+ childSetAction("clear_btn", onClickClear, this);
+ childSetAction("reload_all_btn", onClickReloadAll, this);
+ childSetCommitCallback("file_list", onCommitFileList, this);
+ childSetCommitCallback("name_edit", onCommitEdit, this);
+ childSetCommitCallback("id_edit", onCommitEdit, this);
+ childSetCommitCallback("type_combo", onCommitEdit, this);
+ childSetAction("copy_uuid_btn", onClickCopyUUID, this);
+ childSetAction("item_btn", onClickItem, this);
+ childSetAction("reload_btn", onClickReload, this);
+ childSetAction("remove_btn", onClickRemove, this);
+ refresh();
+ return TRUE;
+}
+void LLFloaterVFS::refresh()
+{
+ LLScrollListCtrl* list = getChild("file_list");
+ list->clearRows();
+ std::list::iterator end = mFiles.end();
+ for(std::list::iterator iter = mFiles.begin(); iter != end; ++iter)
+ {
+ entry file = (*iter);
+ LLSD element;
+ element["id"] = file.mID;
+ LLSD& name_column = element["columns"][0];
+ name_column["column"] = "name";
+ name_column["value"] = file.mName.empty() ? file.mID.asString() : file.mName;
+ LLSD& type_column = element["columns"][1];
+ type_column["column"] = "type";
+ type_column["value"] = std::string(LLAssetType::lookup(file.mType));
+ list->addElement(element, ADD_BOTTOM);
+ }
+ setMassEnabled(!mFiles.empty());
+ setEditID(mEditID);
+}
+void LLFloaterVFS::add(entry file)
+{
+ mFiles.push_back(file);
+ refresh();
+}
+void LLFloaterVFS::clear()
+{
+ std::list::iterator end = mFiles.end();
+ for(std::list::iterator iter = mFiles.begin(); iter != end; ++iter)
+ {
+ gVFS->removeFile((*iter).mID, (*iter).mType);
+ }
+ mFiles.clear();
+ refresh();
+}
+void LLFloaterVFS::reloadAll()
+{
+ std::list::iterator end = mFiles.end();
+ for(std::list::iterator iter = mFiles.begin(); iter != end; ++iter)
+ {
+ entry file = (*iter);
+ reloadEntry(file);
+ }
+ refresh();
+}
+void LLFloaterVFS::reloadEntry(entry file)
+{
+ gVFS->removeFile(file.mID, file.mType);
+ std::string file_name = file.mFilename;
+ S32 file_size;
+ LLAPRFile fp;
+ fp.open(file_name, LL_APR_RB, LLAPRFile::global, &file_size);
+ if(fp.getFileHandle())
+ {
+ LLVFile file(gVFS, file.mFileID, file.mFileType, LLVFile::WRITE);
+ file.setMaxSize(file_size);
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((file_size = fp.read(copy_buf, buf_size)))
+ file.write(copy_buf, file_size);
+ fp.close();
+ }
+ else
+ {
+ // todo: show a warning, couldn't open the original file
+ return;
+ }
+ refresh();
+}
+void LLFloaterVFS::setEditID(LLUUID edit_id)
+{
+ LLScrollListCtrl* list = getChild("file_list");
+ bool found_in_list = (list->getItemIndex(edit_id) != -1);
+ bool found_in_files = false;
+ entry file;
+ std::list::iterator end = mFiles.end();
+ for(std::list::iterator iter = mFiles.begin(); iter != end; ++iter)
+ {
+ if((*iter).mID == edit_id)
+ {
+ found_in_files = true;
+ file = (*iter);
+ break;
+ }
+ }
+ if(found_in_files && found_in_list)
+ {
+ mEditID = edit_id;
+ list->selectByID(edit_id);
+ setEditEnabled(true);
+ childSetText("name_edit", file.mName);
+ childSetText("id_edit", file.mID.asString());
+ childSetValue("type_combo", std::string(LLAssetType::lookup(file.mType)));
+ }
+ else
+ {
+ mEditID = LLUUID::null;
+ list->deselectAllItems(TRUE);
+ setEditEnabled(false);
+ childSetText("name_edit", std::string(""));
+ childSetText("id_edit", std::string(""));
+ childSetValue("type_combo", std::string("animatn"));
+ }
+}
+LLFloaterVFS::entry LLFloaterVFS::getEditEntry()
+{
+ std::list::iterator end = mFiles.end();
+ for(std::list::iterator iter = mFiles.begin(); iter != end; ++iter)
+ {
+ if((*iter).mID == mEditID)
+ return (*iter);
+ }
+ entry file;
+ file.mID = LLUUID::null;
+ return file;
+}
+void LLFloaterVFS::commitEdit()
+{
+ bool found = false;
+ entry file;
+ std::list::iterator iter;
+ std::list::iterator end = mFiles.end();
+ for(iter = mFiles.begin(); iter != end; ++iter)
+ {
+ if((*iter).mID == mEditID)
+ {
+ found = true;
+ file = (*iter);
+ break;
+ }
+ }
+ if(!found) return;
+ entry edited_file;
+ edited_file.mName = childGetValue("name_edit").asString();
+ edited_file.mID = LLUUID(childGetValue("id_edit").asString());
+ edited_file.mType = LLAssetType::lookup(getChild("type_combo")->getValue().asString());
+ if((edited_file.mID != file.mID) || (edited_file.mType != file.mType))
+ {
+ gVFS->renameFile(file.mID, file.mType, edited_file.mID, edited_file.mType);
+ mEditID = edited_file.mID;
+ }
+ (*iter) = edited_file;
+ refresh();
+}
+void LLFloaterVFS::removeEntry()
+{
+ for(std::list::iterator iter = mFiles.begin(); iter != mFiles.end(); )
+ {
+ if((*iter).mID == mEditID)
+ {
+ gVFS->removeFile((*iter).mID, (*iter).mType);
+ iter = mFiles.erase(iter);
+ }
+ else ++iter;
+ }
+ refresh();
+}
+void LLFloaterVFS::setMassEnabled(bool enabled)
+{
+ childSetEnabled("clear_btn", enabled);
+ childSetEnabled("reload_all_btn", false); // DOESN'T WORK
+}
+void LLFloaterVFS::setEditEnabled(bool enabled)
+{
+ childSetEnabled("name_edit", enabled);
+ childSetEnabled("id_edit", false); // DOESN'T WORK
+ childSetEnabled("type_combo", false); // DOESN'T WORK
+ childSetEnabled("copy_uuid_btn", enabled);
+ childSetEnabled("item_btn", enabled);
+ childSetEnabled("reload_btn", false); // DOESN'T WORK
+ childSetEnabled("remove_btn", enabled);
+}
+// static
+void LLFloaterVFS::onClickAdd(void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ if(!floaterp) return;
+ LLUUID asset_id;
+ LLAssetType::EType asset_type = LLAssetType::AT_NONE;
+ asset_id.generate();
+ LLFilePicker& file_picker = LLFilePicker::instance();
+ if(file_picker.getOpenFile(LLFilePicker::FFLOAD_ALL))
+ {
+ std::string file_name = file_picker.getFirstFile();
+ std::string temp_filename = file_name + ".tmp";
+ asset_type = LLAssetConverter::convert(file_name, temp_filename);
+ if(asset_type == LLAssetType::AT_NONE)
+ {
+ // todo: show a warning
+ return;
+ }
+ S32 file_size;
+ LLAPRFile fp;
+ fp.open(temp_filename, LL_APR_RB, LLAPRFile::global, &file_size);
+ if(fp.getFileHandle())
+ {
+ LLVFile file(gVFS, asset_id, asset_type, LLVFile::WRITE);
+ file.setMaxSize(file_size);
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((file_size = fp.read(copy_buf, buf_size)))
+ file.write(copy_buf, file_size);
+ fp.close();
+ }
+ else
+ {
+ if(temp_filename != file_name)
+ {
+ LLFile::remove(temp_filename);
+ }
+ // todo: show a warning, couldn't open the selected file
+ return;
+ }
+ if(temp_filename != file_name)
+ {
+ LLFile::remove(temp_filename);
+ }
+ entry file;
+ file.mFilename = file_name;
+ file.mID = asset_id;
+ file.mType = asset_type;
+ file.mName = gDirUtilp->getBaseFileName(file_name, true);
+ floaterp->add(file);
+ }
+}
+// static
+void LLFloaterVFS::onClickClear(void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ if(!floaterp) return;
+ floaterp->clear();
+}
+// static
+void LLFloaterVFS::onClickReloadAll(void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ if(!floaterp) return;
+ floaterp->reloadAll();
+}
+// static
+void LLFloaterVFS::onCommitFileList(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ LLScrollListCtrl* list = floaterp->getChild("file_list");
+ LLUUID selected_id(LLUUID::null);
+ if(list->getFirstSelected())
+ selected_id = list->getFirstSelected()->getUUID();
+ floaterp->setEditID(selected_id);
+}
+// static
+void LLFloaterVFS::onCommitEdit(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ floaterp->commitEdit();
+}
+// static
+void LLFloaterVFS::onClickCopyUUID(void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ entry file = floaterp->getEditEntry();
+ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(file.mID.asString()));
+}
+// static
+void LLFloaterVFS::onClickItem(void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ entry file = floaterp->getEditEntry();
+ LLLocalInventory::addItem(file.mName, (int)file.mType, file.mID, true);
+}
+// static
+void LLFloaterVFS::onClickReload(void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ entry file = floaterp->getEditEntry();
+ floaterp->reloadEntry(file);
+}
+// static
+void LLFloaterVFS::onClickRemove(void* user_data)
+{
+ LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data;
+ floaterp->removeEntry();
+}
+//
diff --git a/indra/newview/llfloatervfs.h b/indra/newview/llfloatervfs.h
new file mode 100644
index 000000000..7218b1090
--- /dev/null
+++ b/indra/newview/llfloatervfs.h
@@ -0,0 +1,46 @@
+//
+#ifndef LL_LLFLOATERVFS_H
+#define LL_LLFLOATERVFS_H
+#include "llfloater.h"
+#include "llassettype.h"
+class LLFloaterVFS : LLFloater
+{
+typedef struct
+{
+ std::string mFilename;
+ std::string mName;
+ LLUUID mID;
+ LLAssetType::EType mType;
+} entry;
+public:
+ LLFloaterVFS();
+ ~LLFloaterVFS();
+ static void show();
+ BOOL postBuild();
+ void refresh();
+ void add(entry file);
+ void clear();
+ void reloadAll();
+ void setEditID(LLUUID edit_id);
+ entry getEditEntry();
+ void commitEdit();
+ void reloadEntry(entry file);
+ void removeEntry();
+ static void onClickAdd(void* user_data);
+ static void onClickClear(void* user_data);
+ static void onClickReloadAll(void* user_data);
+ static void onCommitFileList(LLUICtrl* ctrl, void* user_data);
+ static void onCommitEdit(LLUICtrl* ctrl, void* user_data);
+ static void onClickCopyUUID(void* user_data);
+ static void onClickItem(void* user_data);
+ static void onClickReload(void* user_data);
+ static void onClickRemove(void* user_data);
+private:
+ static LLFloaterVFS* sInstance;
+ static std::list mFiles;
+ LLUUID mEditID;
+ void setMassEnabled(bool enabled);
+ void setEditEnabled(bool enabled);
+};
+#endif
+//
diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp
index 58b5e289f..619b0cf1b 100644
--- a/indra/newview/llinventoryactions.cpp
+++ b/indra/newview/llinventoryactions.cpp
@@ -550,6 +550,18 @@ class LLLoadInvCacheFloater : public inventory_listener_t
return true;
}
};
+
+class LLRefreshInvModel : public inventory_listener_t
+{
+ bool handleEvent(LLPointer event, const LLSD& userdata)
+ {
+ LLInventoryModel* model = mPtr->getPanel()->getModel();
+ if(!model) return false;
+ model->empty();
+ model->startBackgroundFetch();
+ return true;
+ }
+};
//
class LLSetSortBy : public inventory_listener_t
{
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index 291a230aa..59f000dd0 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -391,7 +391,7 @@ public:
void addCategory(LLViewerInventoryCategory* category);
void addItem(LLViewerInventoryItem* item);
//
-protected:
+//protected:
//
// Internal method which looks for a category with the specified
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 29017b3b4..79298762f 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -234,6 +234,7 @@
#include "llfloatermessagelog.h"
#include "llfloatermessagebuilder.h"
#include "llao.h"
+#include "llfloatervfs.h"
#include "llfloaterexportregion.h"
//
@@ -402,6 +403,7 @@ void handle_reopen_with_hex_editor(void*);
void handle_open_message_log(void*);
void handle_open_message_builder(void*);
void handle_edit_ao(void*);
+void handle_local_assets(void*);
//
BOOL is_inventory_visible( void* user_data );
@@ -752,6 +754,9 @@ void init_client_menu(LLMenuGL* menu)
sub->append(new LLMenuItemCallGL( "Message Log", &handle_open_message_log, NULL));
sub->append(new LLMenuItemCallGL( "Message Builder", &handle_open_message_builder, NULL));
+
+ sub->append(new LLMenuItemCallGL( "Local Assets...",
+ &handle_local_assets, NULL));
sub->append(new LLMenuItemCheckGL( "Enable AO",
&menu_toggle_control,
@@ -3084,6 +3089,11 @@ void handle_edit_ao(void*)
LLFloaterAO::show();
}
+void handle_local_assets(void*)
+{
+ LLFloaterVFS::show();
+}
+
void handle_close_all_notifications(void*)
{
LLView::child_list_t child_list(*(gNotifyBoxView->getChildList()));
diff --git a/indra/newview/skins/default/xui/en-us/floater_inventory.xml b/indra/newview/skins/default/xui/en-us/floater_inventory.xml
index 70cd4f2a5..8a69f2f53 100644
--- a/indra/newview/skins/default/xui/en-us/floater_inventory.xml
+++ b/indra/newview/skins/default/xui/en-us/floater_inventory.xml
@@ -60,6 +60,10 @@
name="Load InvCache..." width="128">
+
+
+