diff --git a/indra/llui/lluixmltags.h b/indra/llui/lluixmltags.h
index 4166131fe..ba5c26490 100644
--- a/indra/llui/lluixmltags.h
+++ b/indra/llui/lluixmltags.h
@@ -127,5 +127,6 @@ const std::string
LL_FLYOUT_BUTTON_ITEM_TAG("flyout_button_item"),
LL_SIMPLE_TEXT_EDITOR_TAG("simple_text_editor"),
LL_RADIO_ITEM_TAG("radio_item"),
- LL_PROGRESS_BAR_TAG("progress_bar");
+ LL_PROGRESS_BAR_TAG("progress_bar"),
+ DO_HEX_EDITOR_TAG("hex_editor");
#endif
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index b5dbb3124..102a74c10 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -64,6 +64,9 @@ include_directories(
)
set(viewer_SOURCE_FILES
+ dofloaterhex.cpp
+ dohexeditor.cpp
+ doinventorybackup.cpp
jcfloaterareasearch.cpp
llagent.cpp
llagentaccess.cpp
@@ -484,6 +487,9 @@ set(viewer_HEADER_FILES
CMakeLists.txt
ViewerInstall.cmake
+ dofloaterhex.h
+ dohexeditor.h
+ doinventorybackup.h
jcfloaterareasearch.h
llagent.h
llagentaccess.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 1631c573e..99d959e95 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -12637,6 +12637,22 @@
250
200
-
+
+ FloaterHexRect
+
diff --git a/indra/newview/dofloaterhex.cpp b/indra/newview/dofloaterhex.cpp
new file mode 100644
index 000000000..2619e7dfb
--- /dev/null
+++ b/indra/newview/dofloaterhex.cpp
@@ -0,0 +1,412 @@
+/**
+ * @file dofloaterhex.h
+ * @brief Hex Editor Floater made by Day
+ * @author Day Oh
+ *
+ * $LicenseInfo:firstyear=2009&license=WTFPLV2$
+ *
+ */
+
+//
+
+#include "llviewerprecompiledheaders.h"
+
+#include "dofloaterhex.h"
+#include "lluictrlfactory.h"
+#include "doinventorybackup.h" // for downloading
+#include "llviewercontrol.h" // gSavedSettings
+#include "llviewerwindow.h" // alertXML
+#include "llagent.h" // gAgent getID
+#include "llviewermenufile.h"
+#include "llviewerregion.h" // getCapability
+#include "llassetuploadresponders.h" // LLUpdateAgentInventoryResponder
+#include "llinventorymodel.h" // gInventory.updateItem
+#include "llappviewer.h" // gLocalInventoryRoot
+#include "llfloaterperms.h" //get default perms
+
+std::list DOFloaterHex::sInstances;
+S32 DOFloaterHex::sUploadAmount = 10;
+
+DOFloaterHex::DOFloaterHex(LLInventoryItem* item)
+: LLFloater()
+{
+ sInstances.push_back(this);
+ mItem = item;
+ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_hex.xml");
+}
+
+// static
+void DOFloaterHex::show(LLUUID item_id)
+{
+ LLInventoryItem* item = (LLInventoryItem*)gInventory.getItem(item_id);
+ if(item)
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("FloaterHexRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ DOFloaterHex* floaterp = new DOFloaterHex(item);
+ floaterp->setRect(rect);
+ gFloaterView->adjustToFitScreen(floaterp, FALSE);
+ }
+}
+
+DOFloaterHex::~DOFloaterHex()
+{
+ sInstances.remove(this);
+}
+
+void DOFloaterHex::close(bool app_quitting)
+{
+ LLFloater::close(app_quitting);
+}
+
+BOOL DOFloaterHex::postBuild(void)
+{
+ DOHexEditor* editor = getChild("hex");
+ mEditor = editor;
+
+ // Set number of columns
+ U8 columns = U8(gSavedSettings.getU32("HexEditorColumns"));
+ editor->setColumns(columns);
+ // Reflect clamped U8ness in settings
+ gSavedSettings.setU32("HexEditorColumns", U32(columns));
+
+ // Reshape a little based on columns
+ S32 min_width = S32(editor->getSuggestedWidth()) + 20;
+ setResizeLimits(min_width, getMinHeight());
+ if(getRect().getWidth() < min_width)
+ {
+ //LLRect rect = getRect();
+ //rect.setOriginAndSize(rect.mLeft, rect.mBottom, min_width, rect.getHeight());
+ //setRect(rect);
+
+ reshape(min_width, getRect().getHeight(), FALSE);
+ editor->reshape(editor->getRect().getWidth(), editor->getRect().getHeight(), TRUE);
+ }
+
+ childSetEnabled("upload_btn", false);
+ childSetLabelArg("upload_btn", "[UPLOAD]", std::string("Upload"));
+ childSetAction("upload_btn", onClickUpload, this);
+ childSetEnabled("save_btn", false);
+ childSetAction("save_btn", onClickSave, this);
+
+ if(mItem)
+ {
+ std::string title = "Hex editor: " + mItem->getName();
+ const char* asset_type_name = LLAssetType::lookup(mItem->getType());
+ if(asset_type_name)
+ {
+ title.append(" (" + std::string(asset_type_name) + ")");
+ }
+ setTitle(title);
+ }
+
+ if(mItem->getCreatorUUID() == gAgentID)
+ {
+ // Load the asset
+ editor->setVisible(FALSE);
+ childSetText("status_text", std::string("Loading..."));
+ DOInventoryBackup::download(mItem, this, imageCallback, assetCallback);
+ } else {
+ this->close(false);
+ }
+
+ return TRUE;
+}
+
+// static
+void DOFloaterHex::imageCallback(BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata)
+{
+ if(final)
+ {
+ DOInventoryBackup::callbackdata* data = static_cast(userdata);
+ DOFloaterHex* floater = (DOFloaterHex*)(data->floater);
+ if(!floater) return;
+ if(std::find(sInstances.begin(), sInstances.end(), floater) == sInstances.end()) return; // no more crash
+ //LLInventoryItem* item = data->item;
+
+ if(!success)
+ {
+ floater->childSetText("status_text", std::string("Unable to download asset."));
+ return;
+ }
+
+ U8* src_data = src->getData();
+ S32 size = src->getDataSize();
+ std::vector new_data;
+ for(S32 i = 0; i < size; i++)
+ new_data.push_back(src_data[i]);
+
+ floater->mEditor->setValue(new_data);
+ floater->mEditor->setVisible(TRUE);
+ floater->childSetText("status_text", std::string("Note: Image data shown isn't the actual asset data, yet"));
+
+ floater->childSetEnabled("save_btn", false);
+ floater->childSetEnabled("upload_btn", true);
+ floater->childSetLabelArg("upload_btn", "[UPLOAD]", std::string("Upload (L$10)"));
+ }
+ else
+ {
+ src_vi->setBoostLevel(LLViewerImageBoostLevel::BOOST_UI);
+ }
+}
+
+// static
+void DOFloaterHex::assetCallback(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status, LLExtStat ext_status)
+{
+ DOInventoryBackup::callbackdata* data = static_cast(user_data);
+ DOFloaterHex* floater = (DOFloaterHex*)(data->floater);
+ if(!floater) return;
+ if(std::find(sInstances.begin(), sInstances.end(), floater) == sInstances.end()) return; // no more crash
+ LLInventoryItem* item = data->item;
+
+ if(status != 0 && item->getType() != LLAssetType::AT_NOTECARD)
+ {
+ floater->childSetText("status_text", std::string("Unable to download asset."));
+ return;
+ }
+
+ // Todo: this doesn't work for static vfs shit
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ char* buffer = new char[size];
+ if (buffer == NULL)
+ {
+ llerrs << "Memory Allocation Failed" << llendl;
+ return;
+ }
+
+ file.read((U8*)buffer, size);
+
+ std::vector new_data;
+ for(S32 i = 0; i < size; i++)
+ new_data.push_back(buffer[i]);
+
+ delete[] buffer;
+
+ floater->mEditor->setValue(new_data);
+ floater->mEditor->setVisible(TRUE);
+ floater->childSetText("status_text", std::string(""));
+
+ floater->childSetEnabled("upload_btn", true);
+ floater->childSetEnabled("save_btn", false);
+ if(item->getPermissions().allowModifyBy(gAgent.getID()))
+ {
+ switch(item->getType())
+ {
+ case LLAssetType::AT_TEXTURE:
+ case LLAssetType::AT_ANIMATION:
+ case LLAssetType::AT_SOUND:
+ floater->childSetLabelArg("upload_btn", "[UPLOAD]", std::string("Upload (L$10)"));
+ break;
+ case LLAssetType::AT_LANDMARK:
+ case LLAssetType::AT_CALLINGCARD:
+ floater->childSetEnabled("upload_btn", false);
+ floater->childSetEnabled("save_btn", false);
+ break;
+ default:
+ floater->childSetEnabled("save_btn", true);
+ break;
+ }
+ }
+ else
+ {
+ switch(item->getType())
+ {
+ case LLAssetType::AT_TEXTURE:
+ case LLAssetType::AT_ANIMATION:
+ case LLAssetType::AT_SOUND:
+ floater->childSetLabelArg("upload_btn", "[UPLOAD]", std::string("Upload (L$10)"));
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Never enable save if it's a pretend item
+ /* if(gInventory.isObjectDescendentOf(item->getUUID(), gLocalInventoryRoot))
+ {
+ floater->childSetEnabled("save_btn", false);
+ } */
+}
+
+// static
+void DOFloaterHex::onClickUpload(void* user_data)
+{
+ DOFloaterHex* floater = (DOFloaterHex*)user_data;
+ LLInventoryItem* item = floater->mItem;
+
+ LLTransactionID transaction_id;
+ transaction_id.generate();
+ LLUUID fake_asset_id = transaction_id.makeAssetID(gAgent.getSecureSessionID());
+
+ std::vector value = floater->mEditor->getValue();
+ int size = value.size();
+ U8* buffer = new U8[size];
+ for(int i = 0; i < size; i++)
+ buffer[i] = value[i];
+ value.clear();
+
+ LLVFile file(gVFS, fake_asset_id, item->getType(), LLVFile::APPEND);
+ file.setMaxSize(size);
+ if (!file.write(buffer, size))
+ {
+ LLSD args;
+ args["ERROR_MESSAGE"] = "Couldn't write data to file";
+ LLNotifications::instance().add("ErrorMessage", args);
+ return;
+ }
+ delete[] buffer;
+
+ LLAssetStorage::LLStoreAssetCallback callback = NULL;
+ void *fake_user_data = NULL;
+
+ if(item->getType() != LLAssetType::AT_GESTURE && item->getType() != LLAssetType::AT_LSL_TEXT
+ && item->getType() != LLAssetType::AT_NOTECARD)
+ {
+ //U32 const std::string &display_name, LLAssetStorage::LLStoreAssetCallback callback, S32 expected_upload_cost, void *userdata)
+ upload_new_resource(transaction_id,
+ item->getType(),
+ item->getName(),
+ item->getDescription(),
+ 0,
+ item->getType(),
+ item->getInventoryType(),
+ LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(),
+ item->getName(),
+ callback,
+ sUploadAmount,
+ fake_user_data);
+ }
+ else // gestures and scripts, create an item first
+ { // AND notecards
+ //if(item->getType() == LLAssetType::AT_NOTECARD) gDontOpenNextNotecard = true;
+ create_inventory_item( gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getParentUUID(), //gInventory.findCategoryUUIDForType(item->getType()),
+ LLTransactionID::tnull,
+ item->getName(),
+ fake_asset_id.asString(),
+ item->getType(),
+ item->getInventoryType(),
+ (EWearableType)item->getFlags(),
+ PERM_ITEM_UNRESTRICTED,
+ new NewResourceItemCallback);
+ }
+}
+
+struct LLSaveInfo
+{
+ LLSaveInfo(DOFloaterHex* floater, LLTransactionID transaction_id)
+ : mFloater(floater), mTransactionID(transaction_id)
+ {
+ }
+
+ DOFloaterHex* mFloater;
+ LLTransactionID mTransactionID;
+};
+
+// static
+void DOFloaterHex::onClickSave(void* user_data)
+{
+ DOFloaterHex* floater = (DOFloaterHex*)user_data;
+ LLInventoryItem* item = floater->mItem;
+
+ LLTransactionID transaction_id;
+ transaction_id.generate();
+ LLUUID fake_asset_id = transaction_id.makeAssetID(gAgent.getSecureSessionID());
+
+ std::vector value = floater->mEditor->getValue();
+ int size = value.size();
+ U8* buffer = new U8[size];
+ for(int i = 0; i < size; i++)
+ buffer[i] = value[i];
+ value.clear();
+
+ LLVFile file(gVFS, fake_asset_id, item->getType(), LLVFile::APPEND);
+ file.setMaxSize(size);
+ if (!file.write(buffer, size))
+ {
+ LLSD args;
+ args["ERROR_MESSAGE"] = "Couldn't write data to file";
+ LLNotifications::instance().add("ErrorMessage", args);
+ return;
+ }
+ delete[] buffer;
+
+
+ bool caps = false;
+ std::string url;
+ LLSD body;
+ body["item_id"] = item->getUUID();
+
+ switch(item->getType())
+ {
+ case LLAssetType::AT_GESTURE:
+ url = gAgent.getRegion()->getCapability("UpdateGestureAgentInventory");
+ caps = true;
+ break;
+ case LLAssetType::AT_LSL_TEXT:
+ url = gAgent.getRegion()->getCapability("UpdateScriptAgent");
+ body["target"] = "mono";
+ caps = true;
+ break;
+ case LLAssetType::AT_NOTECARD:
+ url = gAgent.getRegion()->getCapability("UpdateNotecardAgentInventory");
+ caps = true;
+ break;
+ default: // wearables & notecards, Oct 12 2009
+ // ONLY WEARABLES, Oct 15 2009
+ floater->childSetText("status_text", std::string("Saving..."));
+ LLSaveInfo* info = new LLSaveInfo(floater, transaction_id);
+ gAssetStorage->storeAssetData(transaction_id, item->getType(), onSaveComplete, info);
+ caps = false;
+ break;
+ }
+
+ if(caps)
+ {
+ LLHTTPClient::post(url, body,
+ new LLUpdateAgentInventoryResponder(body, fake_asset_id, item->getType()));
+ }
+}
+
+void DOFloaterHex::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status)
+{
+ LLSaveInfo* info = (LLSaveInfo*)user_data;
+ DOFloaterHex* floater = info->mFloater;
+ if(std::find(sInstances.begin(), sInstances.end(), floater) == sInstances.end()) return; // no more crash
+ LLInventoryItem* item = floater->mItem;
+
+ floater->childSetText("status_text", std::string(""));
+
+ if(item && (status == 0))
+ {
+ LLPointer new_item = new LLViewerInventoryItem(item);
+ new_item->setDescription(item->getDescription());
+ new_item->setTransactionID(info->mTransactionID);
+ new_item->setAssetUUID(asset_uuid);
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLSD args;
+ args["ERROR_MESSAGE"] = llformat("Upload failed with status %d, also %d", status, ext_status);
+ LLNotifications::instance().add("ErrorMessage", args);
+ }
+}
+
+//
diff --git a/indra/newview/dofloaterhex.h b/indra/newview/dofloaterhex.h
new file mode 100644
index 000000000..003507445
--- /dev/null
+++ b/indra/newview/dofloaterhex.h
@@ -0,0 +1,53 @@
+/**
+ * @file dofloaterhex.h
+ * @brief Hex Editor Floater made by Day
+ * @author Day Oh
+ *
+ * $LicenseInfo:firstyear=2009&license=WTFPLV2$
+ *
+ */
+
+//
+
+
+#ifndef DO_DOFLOATERHEX_H
+#define DO_DOFLOATERHEX_H
+
+#include "llfloater.h"
+#include "dohexeditor.h"
+#include "llinventory.h"
+#include "llviewerimage.h"
+
+class DOFloaterHex
+: public LLFloater
+{
+public:
+ DOFloaterHex(LLInventoryItem* item);
+ static void show(LLUUID item_id);
+ BOOL postBuild(void);
+ void close(bool app_quitting);
+ static void imageCallback(BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata);
+ static void assetCallback(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status, LLExtStat ext_status);
+ static void onClickSave(void* user_data);
+ static void onClickUpload(void* user_data);
+ static void onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status);
+ LLInventoryItem* mItem;
+ DOHexEditor* mEditor;
+ static std::list sInstances;
+private:
+ virtual ~DOFloaterHex();
+protected:
+ static S32 sUploadAmount;
+};
+
+#endif
+//
diff --git a/indra/newview/dohexeditor.cpp b/indra/newview/dohexeditor.cpp
new file mode 100644
index 000000000..b2dde3242
--- /dev/null
+++ b/indra/newview/dohexeditor.cpp
@@ -0,0 +1,1238 @@
+/**
+ * @file dohexeditor.cpp
+ * @brief DOHexEditor Widget
+ * @author Day Oh
+ *
+ * $LicenseInfo:firstyear=2009&license=WTFPLV2$
+ *
+ */
+
+//
+#include "linden_common.h"
+#include "dohexeditor.h"
+#include "llfocusmgr.h"
+#include "llkeyboard.h"
+#include "llclipboard.h"
+#include "llwindow.h" // setCursor
+
+static LLRegisterWidget r("hex_editor");
+
+DOHexEditor::DOHexEditor(const std::string& name, const LLRect& rect)
+: LLUICtrl(name, rect, TRUE, NULL, NULL),
+ LLEditMenuHandler(),
+ mColumns(16),
+ mCursorPos(0),
+ mSecondNibble(FALSE),
+ mSelecting(FALSE),
+ mHasSelection(FALSE),
+ mInData(FALSE),
+ mSelectionStart(0),
+ mSelectionEnd(0)
+{
+ mGLFont = LLFontGL::getFontMonospace();
+
+ mTextRect.setOriginAndSize(
+ 5, // border + padding
+ 1, // border
+ getRect().getWidth() - SCROLLBAR_SIZE - 6,
+ getRect().getHeight() - 5);
+
+ S32 line_height = llround( mGLFont->getLineHeight() );
+ S32 page_size = mTextRect.getHeight() / line_height;
+
+ // Init the scrollbar
+ LLRect scroll_rect;
+ scroll_rect.setOriginAndSize(
+ getRect().getWidth() - SCROLLBAR_SIZE,
+ 1,
+ SCROLLBAR_SIZE,
+ getRect().getHeight() - 1);
+ S32 lines_in_doc = getLineCount();
+ mScrollbar = new LLScrollbar( std::string("Scrollbar"), scroll_rect,
+ LLScrollbar::VERTICAL,
+ lines_in_doc,
+ 0,
+ page_size,
+ NULL, this );
+ mScrollbar->setFollowsRight();
+ mScrollbar->setFollowsTop();
+ mScrollbar->setFollowsBottom();
+ mScrollbar->setEnabled( TRUE );
+ mScrollbar->setVisible( TRUE );
+ mScrollbar->setOnScrollEndCallback(NULL, NULL);
+ addChild(mScrollbar);
+
+ mBorder = new LLViewBorder( std::string("text ed border"),
+ LLRect(0, getRect().getHeight(), getRect().getWidth(), 0),
+ LLViewBorder::BEVEL_IN,
+ LLViewBorder::STYLE_LINE,
+ 1 ); // border width
+ addChild( mBorder );
+
+ changedLength();
+
+ mUndoBuffer = new LLUndoBuffer(DOUndoHex::create, 128);
+}
+
+DOHexEditor::~DOHexEditor()
+{
+ gFocusMgr.releaseFocusIfNeeded(this);
+ if(gEditMenuHandler == this)
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+LLView* DOHexEditor::fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFactory *factory)
+{
+ std::string name("text_editor");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ DOHexEditor* editor = new DOHexEditor(name, rect);
+
+ editor->initFromXML(node, parent);
+
+ return editor;
+}
+
+void DOHexEditor::setValue(const LLSD& value)
+{
+ mValue = value.asBinary();
+ changedLength();
+}
+
+LLSD DOHexEditor::getValue() const
+{
+ return LLSD(mValue);
+}
+
+void DOHexEditor::setColumns(U8 columns)
+{
+ mColumns = columns;
+ changedLength();
+}
+
+U32 DOHexEditor::getLineCount()
+{
+ U32 lines = mValue.size();
+ lines /= mColumns;
+ lines++; // incomplete or extra line at bottom
+ return lines;
+}
+
+void DOHexEditor::getPosAndContext(S32 x, S32 y, BOOL force_context, U32& pos, BOOL& in_data, BOOL& second_nibble)
+{
+ pos = 0;
+
+ F32 line_height = mGLFont->getLineHeight();
+ F32 char_width = mGLFont->getWidthF32(".");
+ F32 data_column_width = char_width * 3; // " 00";
+ F32 text_x = mTextRect.mLeft;
+ F32 text_x_data = text_x + (char_width * 10.1f); // "00000000 ", dunno why it's a fraction off
+ F32 text_x_ascii = text_x_data + (data_column_width * mColumns) + (char_width * 2);
+ F32 text_y = (F32)(mTextRect.mTop - line_height);
+ U32 first_line = mScrollbar->getDocPos();
+ //U32 last_line = first_line + mScrollbar->getPageSize(); // don't -2 from scrollbar sizes
+ S32 first_line_y = text_y - line_height;
+
+ S32 ly = -(y - first_line_y); // negative vector from first line to y
+ ly -= 5; // slight skew
+ S32 line = ly / line_height;
+ if(line < 0) line = 0;
+ line += first_line;
+
+ if(!force_context)
+ {
+ in_data = (x < (text_x_ascii - char_width)); // char width for less annoying
+ }
+ S32 lx = x;
+ S32 offset;
+ if(in_data)
+ {
+ lx -= char_width; // subtracting char width because idk
+ lx -= text_x_data;
+ offset = lx / data_column_width;
+
+ // Now, which character
+ S32 rem = S32( (F32)lx - (data_column_width * offset) - (char_width * 0.25) );
+ if(rem > 0)
+ {
+ if(rem > char_width)
+ {
+ offset++; // next byte
+ second_nibble = FALSE;
+ }
+ else second_nibble = TRUE;
+ }
+ else second_nibble = FALSE;
+ }
+ else
+ {
+ second_nibble = FALSE;
+ lx += char_width; // adding char width because idk
+ lx -= text_x_ascii;
+ offset = lx / char_width;
+ }
+ if(offset < 0) offset = 0;
+ if(offset >= mColumns)//offset = mColumns - 1;
+ {
+ offset = 0;
+ line++;
+ second_nibble = FALSE;
+ }
+
+ pos = (line * mColumns) + offset;
+ if(pos < 0) pos = 0;
+ if(pos > mValue.size()) pos = mValue.size();
+ if(pos == mValue.size())
+ {
+ second_nibble = FALSE;
+ }
+}
+
+void DOHexEditor::changedLength()
+{
+ S32 line_height = llround( mGLFont->getLineHeight() );
+ S32 page_size = mTextRect.getHeight() / line_height;
+ page_size -= 2; // don't count the spacer and header
+
+ mScrollbar->setDocSize(getLineCount());
+ mScrollbar->setPageSize(page_size);
+
+ moveCursor(mCursorPos, mSecondNibble); // cursor was off end after undo of paste
+}
+
+void DOHexEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLView::reshape( width, height, called_from_parent );
+ mTextRect.setOriginAndSize(
+ 5, // border + padding
+ 1, // border
+ getRect().getWidth() - SCROLLBAR_SIZE - 6,
+ getRect().getHeight() - 5);
+ LLRect scrollrect;
+ scrollrect.setOriginAndSize(
+ getRect().getWidth() - SCROLLBAR_SIZE,
+ 1,
+ SCROLLBAR_SIZE,
+ getRect().getHeight() - 1);
+ mScrollbar->setRect(scrollrect);
+ mBorder->setRect(LLRect(0, getRect().getHeight(), getRect().getWidth(), 0));
+ changedLength();
+}
+
+void DOHexEditor::setFocus(BOOL b)
+{
+ if(b)
+ {
+ LLEditMenuHandler::gEditMenuHandler = this;
+ }
+ else
+ {
+ mSelecting = FALSE;
+ gFocusMgr.releaseFocusIfNeeded(this);
+ if(gEditMenuHandler == this)
+ {
+ gEditMenuHandler = NULL;
+ }
+ }
+ LLUICtrl::setFocus(b);
+}
+
+F32 DOHexEditor::getSuggestedWidth()
+{
+ F32 char_width = mGLFont->getWidthF32(".");
+ F32 data_column_width = char_width * 3; // " 00";
+ F32 text_x = mTextRect.mLeft;
+ F32 text_x_data = text_x + (char_width * 10.1f); // "00000000 ", dunno why it's a fraction off
+ F32 text_x_ascii = text_x_data + (data_column_width * mColumns) + (char_width * 2);
+ F32 suggested_width = text_x_ascii + (char_width * mColumns);
+ suggested_width += mScrollbar->getRect().getWidth();
+ suggested_width += 10.0f;
+ return suggested_width;
+}
+
+U32 DOHexEditor::getProperSelectionStart()
+{
+ return (mSelectionStart < mSelectionEnd) ? mSelectionStart : mSelectionEnd;
+}
+
+U32 DOHexEditor::getProperSelectionEnd()
+{
+ return (mSelectionStart < mSelectionEnd) ? mSelectionEnd : mSelectionStart;
+}
+
+BOOL DOHexEditor::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ return mScrollbar->handleScrollWheel( 0, 0, clicks );
+}
+
+BOOL DOHexEditor::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+ if(!handled)
+ {
+ setFocus(TRUE);
+ gFocusMgr.setMouseCapture(this);
+ handled = TRUE;
+ if(!mSelecting)
+ {
+ if(mask & MASK_SHIFT)
+ {
+ // extend a selection
+ getPosAndContext(x, y, FALSE, mCursorPos, mInData, mSecondNibble);
+ mSelectionEnd = mCursorPos;
+ mHasSelection = (mSelectionStart != mSelectionEnd);
+ mSelecting = TRUE;
+ }
+ else
+ {
+ // start selecting
+ getPosAndContext(x, y, FALSE, mCursorPos, mInData, mSecondNibble);
+ mSelectionStart = mCursorPos;
+ mSelectionEnd = mCursorPos;
+ mHasSelection = FALSE;
+ mSelecting = TRUE;
+ }
+ }
+ }
+ return handled;
+}
+
+BOOL DOHexEditor::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ if(!hasMouseCapture())
+ {
+ handled = childrenHandleHover(x, y, mask) != NULL;
+ }
+ if(!handled && mSelecting && hasMouseCapture())
+ {
+ // continuation of selecting
+ getPosAndContext(x, y, TRUE, mCursorPos, mInData, mSecondNibble);
+ mSelectionEnd = mCursorPos;
+ mHasSelection = (mSelectionStart != mSelectionEnd);
+ handled = TRUE;
+ }
+ return handled;
+}
+
+BOOL DOHexEditor::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
+ if(!handled && mSelecting && hasMouseCapture())
+ {
+ gFocusMgr.setMouseCapture(NULL);
+ mSelecting = FALSE;
+ }
+ return handled;
+}
+
+BOOL DOHexEditor::handleKeyHere(KEY key, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL DOHexEditor::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+
+ BOOL moved_cursor = FALSE;
+ U32 old_cursor = mCursorPos;
+ U32 cursor_line = mCursorPos / mColumns;
+ U32 doc_first_line = 0;
+ U32 doc_last_line = mValue.size() / mColumns;
+ //U32 first_line = mScrollbar->getDocPos();
+ //U32 last_line = first_line + mScrollbar->getPageSize(); // don't -2 from scrollbar sizes
+ U32 beginning_of_line = mCursorPos - (mCursorPos % mColumns);
+ U32 end_of_line = beginning_of_line + mColumns - 1;
+
+ handled = TRUE;
+ switch( key )
+ {
+
+ // Movement keys
+
+ case KEY_UP:
+ if(cursor_line > doc_first_line)
+ {
+ moveCursor(mCursorPos - mColumns, mSecondNibble);
+ moved_cursor = TRUE;
+ }
+ break;
+ case KEY_DOWN:
+ if(cursor_line < doc_last_line)
+ {
+ moveCursor(mCursorPos + mColumns, mSecondNibble);
+ moved_cursor = TRUE;
+ }
+ break;
+ case KEY_LEFT:
+ if(mCursorPos)
+ {
+ if(!mSecondNibble) moveCursor(mCursorPos - 1, FALSE);
+ else moveCursor(mCursorPos, FALSE);
+ moved_cursor = TRUE;
+ }
+ break;
+ case KEY_RIGHT:
+ moveCursor(mCursorPos + 1, FALSE);
+ moved_cursor = TRUE;
+ break;
+ case KEY_PAGE_UP:
+ mScrollbar->pageUp(1);
+ break;
+ case KEY_PAGE_DOWN:
+ mScrollbar->pageDown(1);
+ break;
+ case KEY_HOME:
+ if(mask & MASK_CONTROL)
+ moveCursor(0, FALSE);
+ else
+ moveCursor(beginning_of_line, FALSE);
+ moved_cursor = TRUE;
+ break;
+ case KEY_END:
+ if(mask & MASK_CONTROL)
+ moveCursor(mValue.size(), FALSE);
+ else
+ moveCursor(end_of_line, FALSE);
+ moved_cursor = TRUE;
+ break;
+
+ // Special
+
+ case KEY_INSERT:
+ gKeyboard->toggleInsertMode();
+ break;
+
+ case KEY_ESCAPE:
+ gFocusMgr.releaseFocusIfNeeded(this);
+ break;
+
+ // Editing
+ case KEY_BACKSPACE:
+ if(mHasSelection)
+ {
+ U32 start = getProperSelectionStart();
+ U32 end = getProperSelectionEnd();
+ del(start, end - 1, TRUE);
+ moveCursor(start, FALSE);
+ }
+ else if(mCursorPos && (!mSecondNibble))
+ {
+ del(mCursorPos - 1, mCursorPos - 1, TRUE);
+ moveCursor(mCursorPos - 1, FALSE);
+ }
+ break;
+
+ case KEY_DELETE:
+ if(mHasSelection)
+ {
+ U32 start = getProperSelectionStart();
+ U32 end = getProperSelectionEnd();
+ del(start, end - 1, TRUE);
+ moveCursor(start, FALSE);
+ }
+ else if((mCursorPos != mValue.size()) && (!mSecondNibble))
+ {
+ del(mCursorPos, mCursorPos, TRUE);
+ }
+ break;
+
+ default:
+ handled = FALSE;
+ break;
+ }
+
+ if(moved_cursor)
+ {
+ // Selecting and deselecting
+ if(mask & MASK_SHIFT)
+ {
+ if(!mHasSelection) mSelectionStart = old_cursor;
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ mSelectionStart = mCursorPos;
+ mSelectionEnd = mCursorPos;
+ }
+ mHasSelection = mSelectionStart != mSelectionEnd;
+ }
+
+ return handled;
+}
+
+BOOL DOHexEditor::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
+{
+ U8 c = uni_char & 0xff;
+ if(mInData)
+ {
+ if(c > 0x39)
+ {
+ if(c > 0x46) c -= 0x20;
+ if(c >= 0x41 && c <= 0x46) c = (c & 0x0f) + 0x09;
+ else return TRUE;
+ }
+ else if(c < 0x30) return TRUE;
+ else c &= 0x0f;
+ }
+
+ if(uni_char < 0x20) return FALSE;
+
+ if( (LL_KIM_INSERT == gKeyboard->getInsertMode() && (!mHasSelection))
+ || (!mHasSelection && (mCursorPos == mValue.size())) )// last byte? always insert
+ {
+ // Todo: this should overwrite if there's a selection
+ if(!mInData)
+ {
+ std::vector new_data;
+ new_data.push_back(c);
+ insert(mCursorPos, new_data, TRUE);
+ moveCursor(mCursorPos + 1, FALSE);
+ }
+ else if(!mSecondNibble)
+ {
+ c <<= 4;
+ std::vector new_data;
+ new_data.push_back(c);
+ insert(mCursorPos, new_data, TRUE);
+ moveCursor(mCursorPos, TRUE);
+ }
+ else
+ {
+ c |= (mValue[mCursorPos] & 0xF0);
+ std::vector new_data;
+ new_data.push_back(c);
+ overwrite(mCursorPos, mCursorPos, new_data, TRUE);
+ moveCursor(mCursorPos + 1, FALSE);
+ }
+ }
+ else // overwrite mode
+ {
+ if(mHasSelection)
+ {
+ if(mInData) c <<= 4;
+ std::vector new_data;
+ new_data.push_back(c);
+ U8 start = getProperSelectionStart();
+ overwrite(start, getProperSelectionEnd() - 1, new_data, TRUE);
+ if(mInData) moveCursor(start, TRUE); // we only entered a nibble
+ else moveCursor(start + 1, FALSE); // we only entered a byte
+ }
+ else if(!mInData)
+ {
+ std::vector new_data;
+ new_data.push_back(c);
+ overwrite(mCursorPos, mCursorPos, new_data, TRUE);
+ moveCursor(mCursorPos + 1, FALSE);
+ }
+ else if(!mSecondNibble)
+ {
+ c <<= 4;
+ c |= (mValue[mCursorPos] & 0x0F);
+ std::vector new_data;
+ new_data.push_back(c);
+ overwrite(mCursorPos, mCursorPos, new_data, TRUE);
+ moveCursor(mCursorPos, TRUE);
+ }
+ else
+ {
+ c |= (mValue[mCursorPos] & 0xF0);
+ std::vector new_data;
+ new_data.push_back(c);
+ overwrite(mCursorPos, mCursorPos, new_data, TRUE);
+ moveCursor(mCursorPos + 1, FALSE);
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL DOHexEditor::handleUnicodeCharHere(llwchar uni_char)
+{
+ return FALSE;
+}
+
+void DOHexEditor::draw()
+{
+ S32 left = 0;
+ S32 top = getRect().getHeight();
+ S32 right = getRect().getWidth();
+ S32 bottom = 0;
+
+ BOOL has_focus = gFocusMgr.getKeyboardFocus() == this;
+
+
+ F32 line_height = mGLFont->getLineHeight();
+ F32 char_width = mGLFont->getWidthF32(".");
+ F32 data_column_width = char_width * 3; // " 00";
+ F32 text_x = mTextRect.mLeft;
+ F32 text_x_data = text_x + (char_width * 10.1f); // "00000000 ", dunno why it's a fraction off
+ F32 text_x_ascii = text_x_data + (data_column_width * mColumns) + (char_width * 2);
+ F32 text_y = (F32)(mTextRect.mTop - line_height);
+
+ U32 data_length = mValue.size();
+ U32 line_count = getLineCount();
+ U32 first_line = mScrollbar->getDocPos();
+ U32 last_line = first_line + mScrollbar->getPageSize(); // don't -2 from scrollbar sizes
+
+ LLRect clip(getRect());
+ clip.mRight = mScrollbar->getRect().mRight;
+ clip.mLeft -= 10;
+ clip.mBottom -= 10;
+ LLLocalClipRect bgclip(clip);
+
+ // Background
+ gl_rect_2d(left, top, right, bottom, LLColor4::white);
+
+ // Let's try drawing some helpful guides
+ LLColor4 guide_color_light = LLColor4(1.0f, 1.0f, 0.9f);
+ LLColor4 guide_color_dark = LLColor4(1.0f, 1.0f, 0.8f);
+ for(U32 col = 0; col < mColumns; col += 2)
+ {
+ // Behind hex
+ F32 box_left = text_x_data + (col * data_column_width) + 2; // skew 2
+ F32 box_right = box_left + data_column_width;
+ gl_rect_2d(box_left, top, box_right, bottom, (col & 3) ? guide_color_light : guide_color_dark);
+ // Behind ASCII
+ //box_left = text_x_ascii + (col * char_width) - 1; // skew 1
+ //box_right = box_left + char_width;
+ //gl_rect_2d(box_left, top, box_right, bottom, guide_color);
+ }
+
+
+ // Scrollbar & border (drawn twice?)
+ mBorder->setKeyboardFocusHighlight(has_focus);
+ LLView::draw();
+
+
+ LLLocalClipRect textrect_clip(mTextRect);
+
+
+ // Selection stuff is reused
+ U32 selection_start = getProperSelectionStart();
+ U32 selection_end = getProperSelectionEnd();
+ U32 selection_first_line = selection_start / mColumns;
+ U32 selection_last_line = selection_end / mColumns;
+ U32 selection_start_column = selection_start % mColumns;
+ U32 selection_end_column = selection_end % mColumns;
+
+ // Don't pretend a selection there is visible
+ if(!selection_end_column)
+ {
+ selection_last_line--;
+ selection_end_column = mColumns;
+ }
+
+ if(mHasSelection)
+ {
+ LLColor4 selection_color_context(LLColor4::black);
+ LLColor4 selection_color_not_context(LLColor4::grey3);
+ LLColor4 selection_color_data(selection_color_not_context);
+ LLColor4 selection_color_ascii(selection_color_not_context);
+ if(mInData) selection_color_data = selection_color_context;
+ else selection_color_ascii = selection_color_context;
+
+
+ // Setup for selection in data
+ F32 selection_pixel_x_base = text_x_data + char_width - 3; // skew 3
+ F32 selection_pixel_x_right_base = selection_pixel_x_base + (data_column_width * mColumns) - char_width + 4;
+ F32 selection_pixel_x;
+ F32 selection_pixel_x_right;
+ F32 selection_pixel_y = (F32)(mTextRect.mTop - line_height) - 3; // skew 3;
+ selection_pixel_y -= line_height * 2;
+ selection_pixel_y -= line_height * (S32(selection_first_line) - S32(first_line));
+
+ // Selection in data, First line
+ if(selection_first_line >= first_line && selection_first_line <= last_line)
+ {
+ selection_pixel_x = selection_pixel_x_base;
+ selection_pixel_x += (data_column_width * selection_start_column);
+ if(selection_first_line == selection_last_line)
+ {
+ // Select to last character
+ selection_pixel_x_right = selection_pixel_x_base + (data_column_width * selection_end_column);
+ selection_pixel_x_right -= (char_width - 4);
+ }
+ else
+ {
+ // Select to end of line
+ selection_pixel_x_right = selection_pixel_x_right_base;
+ }
+ gl_rect_2d(selection_pixel_x, selection_pixel_y + line_height, selection_pixel_x_right, selection_pixel_y, selection_color_data);
+ }
+
+ // Selection in data, Middle lines
+ for(U32 line = selection_first_line + 1; line < selection_last_line; line++)
+ {
+ selection_pixel_y -= line_height;
+ if(line >= first_line && line <= last_line)
+ {
+ gl_rect_2d(selection_pixel_x_base, selection_pixel_y + line_height, selection_pixel_x_right_base, selection_pixel_y, selection_color_data);
+ }
+ }
+
+ // Selection in data, Last line
+ if(selection_first_line != selection_last_line
+ && selection_last_line >= first_line && selection_last_line <= last_line)
+ {
+ selection_pixel_x_right = selection_pixel_x_base + (data_column_width * selection_end_column);
+ selection_pixel_x_right -= (char_width - 4);
+ selection_pixel_y -= line_height;
+ gl_rect_2d(selection_pixel_x_base, selection_pixel_y + line_height, selection_pixel_x_right, selection_pixel_y, selection_color_data);
+ }
+
+ selection_pixel_y = (F32)(mTextRect.mTop - line_height) - 3; // skew 3;
+ selection_pixel_y -= line_height * 2;
+ selection_pixel_y -= line_height * (S32(selection_first_line) - S32(first_line));
+
+ // Setup for selection in ASCII
+ selection_pixel_x_base = text_x_ascii - 1;
+ selection_pixel_x_right_base = selection_pixel_x_base + (char_width * mColumns);
+
+ // Selection in ASCII, First line
+ if(selection_first_line >= first_line && selection_first_line <= last_line)
+ {
+ selection_pixel_x = selection_pixel_x_base;
+ selection_pixel_x += (char_width * selection_start_column);
+ if(selection_first_line == selection_last_line)
+ {
+ // Select to last character
+ selection_pixel_x_right = selection_pixel_x_base + (char_width * selection_end_column);
+ }
+ else
+ {
+ // Select to end of line
+ selection_pixel_x_right = selection_pixel_x_right_base;
+ }
+ gl_rect_2d(selection_pixel_x, selection_pixel_y + line_height, selection_pixel_x_right, selection_pixel_y, selection_color_ascii);
+ }
+
+ // Selection in ASCII, Middle lines
+ for(U32 line = selection_first_line + 1; line < selection_last_line; line++)
+ {
+ selection_pixel_y -= line_height;
+ if(line >= first_line && line <= last_line)
+ {
+ gl_rect_2d(selection_pixel_x_base, selection_pixel_y + line_height, selection_pixel_x_right_base, selection_pixel_y, selection_color_ascii);
+ }
+ }
+
+ // Selection in ASCII, Last line
+ if(selection_first_line != selection_last_line
+ && selection_last_line >= first_line && selection_last_line <= last_line)
+ {
+ selection_pixel_x_right = selection_pixel_x_base + (char_width * selection_end_column);
+ selection_pixel_y -= line_height;
+ gl_rect_2d(selection_pixel_x_base, selection_pixel_y + line_height, selection_pixel_x_right, selection_pixel_y, selection_color_ascii);
+ }
+ }
+
+
+ // Insert/Overwrite
+ std::string text = (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) ? "OVERWRITE" : "INSERT";
+ mGLFont->renderUTF8(text, 0, text_x, text_y, LLColor4::purple);
+ // Offset on top
+ text = "";
+ for(U32 i = 0; i < mColumns; i++)
+ {
+ text.append(llformat(" %02X", i));
+ }
+ mGLFont->renderUTF8(text, 0, text_x_data, text_y, LLColor4::blue);
+ // Size
+ {
+ S32 size = mValue.size();
+ std::string size_desc;
+ if(size < 1000) size_desc = llformat("%d bytes", size);
+ else
+ {
+ if(size < 1000000)
+ {
+ size_desc = llformat("%f", F32(size) / 1000.0f);
+ int i = size_desc.length() - 1;
+ for(; i && size_desc.substr(i, 1) == "0"; i--);
+ if(size_desc.substr(i, 1) == ".") i--;
+ size_desc = size_desc.substr(0, i + 1);
+ size_desc.append(" KB");
+ }
+ else
+ {
+ size_desc = llformat("%f", F32(size) / 1000000.0f);
+ int i = size_desc.length() - 1;
+ for(; i && size_desc.substr(i, 1) == "0"; i--);
+ if(size_desc.substr(i, 1) == ".") i--;
+ size_desc = size_desc.substr(0, i + 1);
+ size_desc.append(" MB");
+ }
+ }
+ F32 x = text_x_ascii;
+ x += (char_width * (mColumns - size_desc.length()));
+ mGLFont->renderUTF8(size_desc, 0, x, text_y, LLColor4::purple);
+ }
+ // Leave a blank line
+ text_y -= (line_height * 2);
+
+ // Everything below "header"
+ for(U32 line = first_line; line <= last_line; line++)
+ {
+ if(line >= line_count) break;
+
+ // Offset on left
+ text = llformat("%08X", line * mColumns); // offset on left
+ mGLFont->renderUTF8(text, 0, text_x, text_y, LLColor4::blue);
+
+ // Setup for rendering hex and ascii
+ U32 line_char_offset = mColumns * line;
+ U32 colstart0 = 0;
+ U32 colend0 = mColumns;
+ U32 colstart1 = mColumns;
+ U32 colend1 = mColumns;
+ U32 colstart2 = mColumns;
+ U32 colend2 = mColumns;
+ if(mHasSelection)
+ {
+ if(line == selection_first_line)
+ {
+ colend0 = selection_start_column;
+ colstart1 = selection_start_column;
+ if(selection_first_line == selection_last_line)
+ {
+ colend1 = selection_end_column;
+ colstart2 = selection_end_column;
+ colend2 = mColumns;
+ }
+ }
+ else if(line > selection_first_line && line < selection_last_line)
+ {
+ colend0 = 0;
+ colstart1 = 0;
+ colend1 = mColumns;
+ }
+ else if(line == selection_last_line)
+ {
+ colend0 = 0;
+ colstart1 = 0;
+ colend1 = selection_end_column;
+ colstart2 = selection_end_column;
+ colend2 = mColumns;
+ }
+ }
+
+ // Data in hex
+ text = "";
+ for(U32 c = colstart0; c < colend0; c++)
+ {
+ U32 o = line_char_offset + c;
+ if(o >= data_length) text.append(" ");
+ else text.append(llformat(" %02X", mValue[o]));
+ }
+ mGLFont->renderUTF8(text, 0, text_x_data + (colstart0 * data_column_width), text_y, LLColor4::black);
+ text = "";
+ for(U32 c = colstart1; c < colend1; c++)
+ {
+ U32 o = line_char_offset + c;
+ if(o >= data_length) text.append(" ");
+ else text.append(llformat(" %02X", mValue[o]));
+ }
+ mGLFont->renderUTF8(text, 0, text_x_data + (colstart1 * data_column_width), text_y, LLColor4::white);
+ text = "";
+ for(U32 c = colstart2; c < colend2; c++)
+ {
+ U32 o = line_char_offset + c;
+ if(o >= data_length) text.append(" ");
+ else text.append(llformat(" %02X", mValue[o]));
+ }
+ mGLFont->renderUTF8(text, 0, text_x_data + (colstart2 * data_column_width), text_y, LLColor4::black);
+
+ // ASCII
+ text = "";
+ for(U32 c = colstart0; c < colend0; c++)
+ {
+ U32 o = line_char_offset + c;
+ if(o >= data_length) break;
+ if((mValue[o] < 0x20) || (mValue[o] >= 0x7F)) text.append(".");
+ else text.append(llformat("%c", mValue[o]));
+ }
+ mGLFont->renderUTF8(text, 0, text_x_ascii + (colstart0 * char_width), text_y, LLColor4::black);
+ text = "";
+ for(U32 c = colstart1; c < colend1; c++)
+ {
+ U32 o = line_char_offset + c;
+ if(o >= data_length) break;
+ if((mValue[o] < 0x20) || (mValue[o] >= 0x7F)) text.append(".");
+ else text.append(llformat("%c", mValue[o]));
+ }
+ mGLFont->renderUTF8(text, 0, text_x_ascii + (colstart1 * char_width), text_y, LLColor4::white);
+ text = "";
+ for(U32 c = colstart2; c < colend2; c++)
+ {
+ U32 o = line_char_offset + c;
+ if(o >= data_length) break;
+ if((mValue[o] < 0x20) || (mValue[o] >= 0x7F)) text.append(".");
+ else text.append(llformat("%c", mValue[o]));
+ }
+ mGLFont->renderUTF8(text, 0, text_x_ascii + (colstart2 * char_width), text_y, LLColor4::black);
+
+ text_y -= line_height;
+ }
+
+
+
+ // Cursor
+ if(has_focus && !mHasSelection && (U32(LLTimer::getElapsedSeconds() * 2.0f) & 0x1))
+ {
+ U32 cursor_line = mCursorPos / mColumns;
+ if((cursor_line >= first_line) && (cursor_line <= last_line))
+ {
+ F32 pixel_y = (F32)(mTextRect.mTop - line_height);
+ pixel_y -= line_height * (2 + (cursor_line - first_line));
+
+ U32 cursor_offset = mCursorPos % mColumns; // bytes
+ F32 pixel_x = mInData ? text_x_data : text_x_ascii;
+ if(mInData)
+ {
+ pixel_x += data_column_width * cursor_offset;
+ pixel_x += char_width;
+ if(mSecondNibble) pixel_x += char_width;
+ }
+ else
+ {
+ pixel_x += char_width * cursor_offset;
+ }
+ pixel_x -= 2.0f;
+ pixel_y -= 2.0f;
+ gl_rect_2d(pixel_x, pixel_y + line_height, pixel_x + 2, pixel_y, LLColor4::black);
+ }
+ }
+}
+
+void DOHexEditor::deselect()
+{
+ mSelectionStart = mCursorPos;
+ mSelectionEnd = mCursorPos;
+ mHasSelection = FALSE;
+ mSelecting = FALSE;
+}
+
+BOOL DOHexEditor::canUndo() const
+{
+ return mUndoBuffer->canUndo();
+}
+
+void DOHexEditor::undo()
+{
+ mUndoBuffer->undoAction();
+}
+
+BOOL DOHexEditor::canRedo() const
+{
+ return mUndoBuffer->canRedo();
+}
+
+void DOHexEditor::redo()
+{
+ mUndoBuffer->redoAction();
+}
+
+
+
+
+void DOHexEditor::moveCursor(U32 pos, BOOL second_nibble)
+{
+ mCursorPos = pos;
+
+ // Clamp and handle second nibble
+ if(mCursorPos >= mValue.size())
+ {
+ mCursorPos = mValue.size();
+ mSecondNibble = FALSE;
+ }
+ else
+ {
+ mSecondNibble = mInData ? second_nibble : FALSE;
+ }
+
+ // Change selection
+ mSelectionEnd = mCursorPos;
+ if(!mHasSelection) mSelectionStart = mCursorPos;
+
+ // Scroll
+ U32 line = mCursorPos / mColumns;
+ U32 first_line = mScrollbar->getDocPos();
+ U32 last_line = first_line + mScrollbar->getPageSize(); // don't -2 from scrollbar sizes
+ if(line < first_line) mScrollbar->setDocPos(line);
+ if(line > (last_line - 2)) mScrollbar->setDocPos(line - mScrollbar->getPageSize() + 1);
+}
+
+BOOL DOHexEditor::canCut() const
+{
+ return mHasSelection;
+}
+
+void DOHexEditor::cut()
+{
+ if(!canCut()) return;
+
+ copy();
+
+ U32 start = getProperSelectionStart();
+ del(start, getProperSelectionEnd() - 1, TRUE);
+
+ moveCursor(start, FALSE);
+}
+
+BOOL DOHexEditor::canCopy() const
+{
+ return mHasSelection;
+}
+
+void DOHexEditor::copy()
+{
+ if(!canCopy()) return;
+
+ std::string text;
+ if(mInData)
+ {
+ U32 start = getProperSelectionStart();
+ U32 end = getProperSelectionEnd();
+ for(U32 i = start; i < end; i++)
+ text.append(llformat("%02X", mValue[i]));
+ }
+ else
+ {
+ U32 start = getProperSelectionStart();
+ U32 end = getProperSelectionEnd();
+ for(U32 i = start; i < end; i++)
+ text.append(llformat("%c", mValue[i]));
+ }
+ LLWString wtext = utf8str_to_wstring(text);
+ gClipboard.copyFromSubstring(wtext, 0, wtext.length());
+}
+
+BOOL DOHexEditor::canPaste() const
+{
+ return TRUE;
+}
+
+void DOHexEditor::paste()
+{
+ if(!canPaste()) return;
+
+ std::string clipstr = wstring_to_utf8str(gClipboard.getPasteWString());
+ const char* clip = clipstr.c_str();
+
+ std::vector new_data;
+ if(mInData)
+ {
+ int len = strlen(clip);
+ for(int i = 0; (i + 1) < len; i += 2)
+ {
+ int c = 0;
+ if(sscanf(&(clip[i]), "%02X", &c) != 1) break;
+ new_data.push_back(U8(c));
+ }
+ }
+ else
+ {
+ int len = strlen(clip);
+ for(int i = 0; i < len; i++)
+ {
+ U8 c = 0;
+ if(sscanf(&(clip[i]), "%c", &c) != 1) break;
+ new_data.push_back(c);
+ }
+ }
+
+ U32 start = mCursorPos;
+ if(!mHasSelection)
+ insert(start, new_data, TRUE);
+ else
+ {
+ start = getProperSelectionStart();
+ overwrite(start, getProperSelectionEnd() - 1, new_data, TRUE);
+ }
+
+ moveCursor(start + new_data.size(), FALSE);
+}
+
+BOOL DOHexEditor::canDoDelete() const
+{
+ return mValue.size() > 0;
+}
+
+void DOHexEditor::doDelete()
+{
+ if(!canDoDelete()) return;
+
+ U32 start = getProperSelectionStart();
+ del(start, getProperSelectionEnd(), TRUE);
+
+ moveCursor(start, FALSE);
+}
+
+BOOL DOHexEditor::canSelectAll() const
+{
+ return mValue.size() > 0;
+}
+
+void DOHexEditor::selectAll()
+{
+ if(!canSelectAll()) return;
+
+ mSelectionStart = 0;
+ mSelectionEnd = mValue.size();
+ mHasSelection = mSelectionStart != mSelectionEnd;
+}
+
+BOOL DOHexEditor::canDeselect() const
+{
+ return mHasSelection;
+}
+
+void DOHexEditor::insert(U32 pos, std::vector new_data, BOOL undoable)
+{
+ if(pos > mValue.size())
+ {
+ llwarns << "pos outside data!" << llendl;
+ return;
+ }
+
+ deselect();
+
+ if(undoable)
+ {
+ DOUndoHex* action = (DOUndoHex*)(mUndoBuffer->getNextAction());
+ action->set(this, &(DOUndoHex::undoInsert), &(DOUndoHex::redoInsert), pos, pos, std::vector(), new_data);
+ }
+
+ std::vector::iterator wheres = mValue.begin() + pos;
+
+ mValue.insert(wheres, new_data.begin(), new_data.end());
+
+ changedLength();
+}
+
+void DOHexEditor::overwrite(U32 first_pos, U32 last_pos, std::vector new_data, BOOL undoable)
+{
+ if(first_pos > mValue.size() || last_pos > mValue.size())
+ {
+ llwarns << "pos outside data!" << llendl;
+ return;
+ }
+
+ deselect();
+
+ std::vector::iterator first = mValue.begin() + first_pos;
+ std::vector::iterator last = mValue.begin() + last_pos;
+
+ std::vector old_data;
+ if(last_pos > 0) old_data = std::vector(first, last + 1);
+
+ if(undoable)
+ {
+ DOUndoHex* action = (DOUndoHex*)(mUndoBuffer->getNextAction());
+ action->set(this, &(DOUndoHex::undoOverwrite), &(DOUndoHex::redoOverwrite), first_pos, last_pos, old_data, new_data);
+ }
+
+ mValue.erase(first, last + 1);
+ first = mValue.begin() + first_pos;
+ mValue.insert(first, new_data.begin(), new_data.end());
+
+ changedLength();
+}
+
+void DOHexEditor::del(U32 first_pos, U32 last_pos, BOOL undoable)
+{
+ if(first_pos > mValue.size() || last_pos > mValue.size())
+ {
+ llwarns << "pos outside data!" << llendl;
+ return;
+ }
+
+ deselect();
+
+ std::vector::iterator first = mValue.begin() + first_pos;
+ std::vector::iterator last = mValue.begin() + last_pos;
+
+ std::vector old_data;
+ if(last_pos > 0) old_data = std::vector(first, last + 1);
+
+ if(undoable)
+ {
+ DOUndoHex* action = (DOUndoHex*)(mUndoBuffer->getNextAction());
+ action->set(this, &(DOUndoHex::undoDel), &(DOUndoHex::redoDel), first_pos, last_pos, old_data, std::vector());
+ }
+
+ mValue.erase(first, last + 1);
+
+ changedLength();
+}
+
+void DOUndoHex::set(DOHexEditor* hex_editor,
+ void (*undo_action)(DOUndoHex*),
+ void (*redo_action)(DOUndoHex*),
+ U32 first_pos,
+ U32 last_pos,
+ std::vector old_data,
+ std::vector new_data)
+{
+ mHexEditor = hex_editor;
+ mUndoAction = undo_action;
+ mRedoAction = redo_action;
+ mFirstPos = first_pos;
+ mLastPos = last_pos;
+ mOldData = old_data;
+ mNewData = new_data;
+}
+
+void DOUndoHex::undo()
+{
+ mUndoAction(this);
+}
+
+void DOUndoHex::redo()
+{
+ mRedoAction(this);
+}
+
+void DOUndoHex::undoInsert(DOUndoHex* action)
+{
+ //action->mHexEditor->del(action->mFirstPos, action->mLastPos, FALSE);
+ action->mHexEditor->del(action->mFirstPos, action->mFirstPos + action->mNewData.size() - 1, FALSE);
+}
+
+void DOUndoHex::redoInsert(DOUndoHex* action)
+{
+ action->mHexEditor->insert(action->mFirstPos, action->mNewData, FALSE);
+}
+
+void DOUndoHex::undoOverwrite(DOUndoHex* action)
+{
+ //action->mHexEditor->overwrite(action->mFirstPos, action->mLastPos, action->mOldData, FALSE);
+ action->mHexEditor->overwrite(action->mFirstPos, action->mFirstPos + action->mNewData.size() - 1, action->mOldData, FALSE);
+}
+
+void DOUndoHex::redoOverwrite(DOUndoHex* action)
+{
+ action->mHexEditor->overwrite(action->mFirstPos, action->mLastPos, action->mNewData, FALSE);
+}
+
+void DOUndoHex::undoDel(DOUndoHex* action)
+{
+ action->mHexEditor->insert(action->mFirstPos, action->mOldData, FALSE);
+}
+
+void DOUndoHex::redoDel(DOUndoHex* action)
+{
+ action->mHexEditor->del(action->mFirstPos, action->mLastPos, FALSE);
+}
+
+
+//
diff --git a/indra/newview/dohexeditor.h b/indra/newview/dohexeditor.h
new file mode 100644
index 000000000..37f8ed35d
--- /dev/null
+++ b/indra/newview/dohexeditor.h
@@ -0,0 +1,152 @@
+/**
+ * @file dohexeditor.h
+ * @brief DOHexEditor Widget
+ * @author Day Oh
+ *
+ * $LicenseInfo:firstyear=2009&license=WTFPLV2$
+ *
+ */
+
+//
+#ifndef DO_DOHEXEDITOR_H
+#define DO_DOHEXEDITOR_H
+
+#include "lluictrl.h"
+#include "llscrollbar.h"
+#include "llviewborder.h"
+#include "llundo.h"
+#include "lleditmenuhandler.h"
+
+class DOHexEditor : public LLUICtrl, public LLEditMenuHandler
+{
+public:
+ DOHexEditor(const std::string& name, const LLRect& rect);
+ ~DOHexEditor();
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFactory *factory);
+ void setValue(const LLSD& value);
+ LLSD getValue() const;
+ void setColumns(U8 columns);
+ U8 getColumns(U8 columns) { return mColumns; };
+ U32 getLineCount();
+ F32 getSuggestedWidth();
+ U32 getProperSelectionStart();
+ U32 getProperSelectionEnd();
+ void reshape(S32 width, S32 height, BOOL called_from_parent);
+ void setFocus(BOOL b);
+
+ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+ BOOL handleKeyHere(KEY key, MASK mask);
+ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);
+ BOOL handleUnicodeCharHere(llwchar uni_char);
+
+ void draw();
+
+ void moveCursor(U32 pos, BOOL second_nibble);
+
+ void insert(U32 pos, std::vector new_data, BOOL undoable);
+ void overwrite(U32 first_pos, U32 last_pos, std::vector new_data, BOOL undoable);
+ void del(U32 first_pos, U32 last_pos, BOOL undoable);
+
+ virtual void cut();
+ virtual BOOL canCut() const;
+
+ virtual void copy();
+ virtual BOOL canCopy() const;
+
+ virtual void paste();
+ virtual BOOL canPaste() const;
+
+ virtual void doDelete();
+ virtual BOOL canDoDelete() const;
+
+ virtual void selectAll();
+ virtual BOOL canSelectAll() const;
+
+ virtual void deselect();
+ virtual BOOL canDeselect() const;
+
+ virtual void undo();
+ virtual BOOL canUndo() const;
+
+ virtual void redo();
+ virtual BOOL canRedo() const;
+
+private:
+ std::vector mValue;
+ U8 mColumns;
+
+ U32 mCursorPos;
+ BOOL mSecondNibble;
+ BOOL mInData;
+ BOOL mSelecting;
+ BOOL mHasSelection;
+ U32 mSelectionStart;
+ U32 mSelectionEnd;
+
+ LLFontGL* mGLFont;
+ LLRect mTextRect;
+ LLScrollbar* mScrollbar;
+ LLViewBorder* mBorder;
+
+ LLUndoBuffer* mUndoBuffer;
+
+ void changedLength();
+ void getPosAndContext(S32 x, S32 y, BOOL force_context, U32& pos, BOOL& in_data, BOOL& second_nibble);
+};
+
+class DOUndoHex : public LLUndoBuffer::LLUndoAction
+{
+protected:
+ DOUndoHex() { }
+ DOHexEditor* mHexEditor;
+ U32 mFirstPos;
+ U32 mLastPos;
+ std::vector mOldData;
+ std::vector mNewData;
+public:
+ static LLUndoAction* create() { return new DOUndoHex(); }
+ virtual void set(DOHexEditor* hex_editor,
+ void (*undo_action)(DOUndoHex*),
+ void (*redo_action)(DOUndoHex*),
+ U32 first_pos,
+ U32 last_pos,
+ std::vector old_data,
+ std::vector new_data);
+ void (*mUndoAction)(DOUndoHex*);
+ void (*mRedoAction)(DOUndoHex*);
+ virtual void undo();
+ virtual void redo();
+
+ static void undoInsert(DOUndoHex* action);
+ static void redoInsert(DOUndoHex* action);
+ static void undoOverwrite(DOUndoHex* action);
+ static void redoOverwrite(DOUndoHex* action);
+ static void undoDel(DOUndoHex* action);
+ static void redoDel(DOUndoHex* action);
+};
+
+class DOHexInsert : public DOUndoHex
+{
+ virtual void undo();
+ virtual void redo();
+};
+
+class DOHexOverwrite : public DOUndoHex
+{
+ virtual void undo();
+ virtual void redo();
+};
+
+class DOHexDel : public DOUndoHex
+{
+ virtual void undo();
+ virtual void redo();
+};
+
+#endif
+//
diff --git a/indra/newview/doinventorybackup.cpp b/indra/newview/doinventorybackup.cpp
new file mode 100644
index 000000000..0385a7690
--- /dev/null
+++ b/indra/newview/doinventorybackup.cpp
@@ -0,0 +1,786 @@
+/**
+ * @file doinventorybackup.cpp
+ * @brief DOInventoryBackup Floaters and Inventory Backup System
+ * @author Day Oh
+ *
+ * $LicenseInfo:firstyear=2009&license=WTFPLV2$
+ *
+ */
+
+//
+#include "llviewerprecompiledheaders.h"
+
+#include "doinventorybackup.h"
+#include "llinventorymodel.h"
+#include "llviewerinventory.h"
+#include "llfilepicker.h"
+#include "lldirpicker.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h" // gImageList
+#include "llagent.h" // gAgent
+#include "llviewerwindow.h" // gViewerWindow
+#include "llfloater.h"
+#include "lluictrlfactory.h"
+#include "llscrolllistctrl.h"
+
+
+std::list DOFloaterInventoryBackup::sInstances;
+
+DOInventoryBackupOrder::DOInventoryBackupOrder()
+{
+ // My personal defaults based on what is assumed to not work
+ mDownloadTextures = true;
+ mDownloadSounds = true;
+ mDownloadCallingCards = false;
+ mDownloadLandmarks = true;
+ mDownloadScripts = true;
+ mDownloadWearables = true;
+ mDownloadObjects = false;
+ mDownloadNotecards = true;
+ mDownloadAnimations = true;
+ mDownloadGestures = true;
+ //mDownloadOthers = true;
+}
+
+DOFloaterInventoryBackupSettings::DOFloaterInventoryBackupSettings(DOInventoryBackupOrder* order)
+: LLFloater(),
+ mOrder(order)
+{
+ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_backup_settings.xml");
+}
+
+DOFloaterInventoryBackupSettings::~DOFloaterInventoryBackupSettings()
+{
+}
+
+BOOL DOFloaterInventoryBackupSettings::postBuild(void)
+{
+ childSetValue("chk_textures", mOrder->mDownloadTextures);
+ childSetValue("chk_sounds", mOrder->mDownloadSounds);
+ childSetValue("chk_callingcards", mOrder->mDownloadCallingCards);
+ childSetValue("chk_landmarks", mOrder->mDownloadLandmarks);
+ childSetValue("chk_scripts", mOrder->mDownloadScripts);
+ childSetValue("chk_wearables", mOrder->mDownloadWearables);
+ childSetValue("chk_objects", mOrder->mDownloadObjects);
+ childSetValue("chk_notecards", mOrder->mDownloadNotecards);
+ childSetValue("chk_animations", mOrder->mDownloadAnimations);
+ childSetValue("chk_gestures", mOrder->mDownloadGestures);
+ //childSetValue("chk_others", mOrder->mDownloadOthers);
+
+ childSetAction("next_btn", DOFloaterInventoryBackupSettings::onClickNext, this);
+
+ return TRUE;
+}
+
+// static
+void DOFloaterInventoryBackupSettings::onClickNext(void* userdata)
+{
+ DOFloaterInventoryBackupSettings* floater = (DOFloaterInventoryBackupSettings*)userdata;
+ DOInventoryBackupOrder* order = floater->mOrder;
+
+ // Apply changes to filters
+ order->mDownloadAnimations = floater->childGetValue("chk_animations");
+ order->mDownloadCallingCards = floater->childGetValue("chk_callingcards");
+ order->mDownloadGestures = floater->childGetValue("chk_gestures");
+ order->mDownloadLandmarks = floater->childGetValue("chk_landmarks");
+ order->mDownloadNotecards = floater->childGetValue("chk_notecards");
+ order->mDownloadObjects = floater->childGetValue("chk_objects");
+ //order->mDownloadOthers = floater->childGetValue("chk_others");
+ order->mDownloadScripts = floater->childGetValue("chk_scripts");
+ order->mDownloadSounds = floater->childGetValue("chk_sounds");
+ order->mDownloadTextures = floater->childGetValue("chk_textures");
+ order->mDownloadWearables = floater->childGetValue("chk_wearables");
+
+ // Make filters
+ std::map type_remove;
+ type_remove[LLAssetType::AT_ANIMATION] = !order->mDownloadAnimations;
+ type_remove[LLAssetType::AT_BODYPART] = !order->mDownloadWearables;
+ type_remove[LLAssetType::AT_CALLINGCARD] = !order->mDownloadCallingCards;
+ type_remove[LLAssetType::AT_CLOTHING] = !order->mDownloadWearables;
+ type_remove[LLAssetType::AT_GESTURE] = !order->mDownloadGestures;
+ type_remove[LLAssetType::AT_IMAGE_JPEG] = !order->mDownloadTextures;
+ type_remove[LLAssetType::AT_IMAGE_TGA] = !order->mDownloadTextures;
+ type_remove[LLAssetType::AT_LANDMARK] = !order->mDownloadLandmarks;
+ type_remove[LLAssetType::AT_LSL_TEXT] = !order->mDownloadScripts;
+ type_remove[LLAssetType::AT_NOTECARD] = !order->mDownloadNotecards;
+ type_remove[LLAssetType::AT_OBJECT] = !order->mDownloadObjects;
+ type_remove[LLAssetType::AT_SCRIPT] = !order->mDownloadScripts;
+ type_remove[LLAssetType::AT_SOUND] = !order->mDownloadSounds;
+ type_remove[LLAssetType::AT_SOUND_WAV] = !order->mDownloadSounds;
+ type_remove[LLAssetType::AT_TEXTURE] = !order->mDownloadTextures;
+ type_remove[LLAssetType::AT_TEXTURE_TGA] = !order->mDownloadTextures;
+
+ // Apply filters
+ std::vector::iterator item_iter = order->mItems.begin();
+ for( ; item_iter != order->mItems.end(); )
+ {
+ if(type_remove[(*item_iter)->getType()])
+ order->mItems.erase(item_iter);
+ else
+ ++item_iter;
+ }
+
+ if(order->mItems.size() < 1)
+ {
+ LLSD args;
+ args["ERROR_MESSAGE"] = "No items passed the filter \\o/";
+ LLNotifications::instance().add("ErrorMessage", args);
+ return;
+ }
+
+ // Get dir name
+ LLDirPicker& picker = LLDirPicker::instance();
+ std::string filename = "New Folder";
+ if (!picker.getDir(&filename))
+ {
+ floater->close();
+ return;
+ }
+ filename = picker.getDirName();
+
+ // Make local directory tree
+ LLFile::mkdir(filename);
+ std::vector::iterator _cat_iter = order->mCats.begin();
+ std::vector::iterator _cat_end = order->mCats.end();
+ for( ; _cat_iter != _cat_end; ++_cat_iter)
+ {
+ std::string path = filename + "\\" + DOInventoryBackup::getPath(*_cat_iter, order->mCats);
+ LLFile::mkdir(path);
+ }
+
+ // Go go backup floater
+ DOFloaterInventoryBackup* backup_floater = new DOFloaterInventoryBackup(filename, order->mCats, order->mItems);
+ backup_floater->center();
+
+ // Close myself
+ floater->close();
+}
+
+
+
+
+// static
+bool DOInventoryBackup::itemIsFolder(LLInventoryItem* item)
+{
+ return ((item->getInventoryType() == LLInventoryType::IT_CATEGORY)
+ || (item->getInventoryType() == LLInventoryType::IT_ROOT_CATEGORY));
+}
+
+// static
+LLFilePicker::ESaveFilter DOInventoryBackup::getSaveFilter(LLInventoryItem* item)
+{
+ LLAssetType::EType type = item->getType();
+ EWearableType wear = (EWearableType)(item->getFlags() & 0xFF);
+ switch(type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ return LLFilePicker::FFSAVE_TGA;
+ case LLAssetType::AT_SOUND:
+ return LLFilePicker::FFSAVE_OGG;
+ case LLAssetType::AT_SCRIPT:
+ case LLAssetType::AT_LSL_TEXT:
+ return LLFilePicker::FFSAVE_LSL;
+ case LLAssetType::AT_ANIMATION:
+ return LLFilePicker::FFSAVE_ANIMATN;
+ case LLAssetType::AT_GESTURE:
+ return LLFilePicker::FFSAVE_GESTURE;
+ case LLAssetType::AT_NOTECARD:
+ return LLFilePicker::FFSAVE_NOTECARD;
+ case LLAssetType::AT_LANDMARK:
+ return LLFilePicker::FFSAVE_LANDMARK;
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ switch(wear)
+ {
+ case WT_EYES:
+ return LLFilePicker::FFSAVE_EYES;
+ case WT_GLOVES:
+ return LLFilePicker::FFSAVE_GLOVES;
+ case WT_HAIR:
+ return LLFilePicker::FFSAVE_HAIR;
+ case WT_JACKET:
+ return LLFilePicker::FFSAVE_JACKET;
+ case WT_PANTS:
+ return LLFilePicker::FFSAVE_PANTS;
+ case WT_SHAPE:
+ return LLFilePicker::FFSAVE_SHAPE;
+ case WT_SHIRT:
+ return LLFilePicker::FFSAVE_SHIRT;
+ case WT_SHOES:
+ return LLFilePicker::FFSAVE_SHOES;
+ case WT_SKIN:
+ return LLFilePicker::FFSAVE_SKIN;
+ case WT_SKIRT:
+ return LLFilePicker::FFSAVE_SKIRT;
+ case WT_SOCKS:
+ return LLFilePicker::FFSAVE_SOCKS;
+ case WT_UNDERPANTS:
+ return LLFilePicker::FFSAVE_UNDERPANTS;
+ case WT_UNDERSHIRT:
+ return LLFilePicker::FFSAVE_UNDERSHIRT;
+ default:
+ return LLFilePicker::FFSAVE_ALL;
+ }
+ default:
+ return LLFilePicker::FFSAVE_ALL;
+ }
+}
+
+// static
+std::string DOInventoryBackup::getExtension(LLInventoryItem* item)
+{
+ LLAssetType::EType type = item->getType();
+ EWearableType wear = (EWearableType)(item->getFlags() & 0xFF);
+ std::string scratch;
+ switch(type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ return ".tga";
+ case LLAssetType::AT_SOUND:
+ return ".ogg";
+ case LLAssetType::AT_SCRIPT:
+ case LLAssetType::AT_LSL_TEXT:
+ return ".lsl";
+ case LLAssetType::AT_ANIMATION:
+ return ".animatn";
+ case LLAssetType::AT_GESTURE:
+ return ".gesture";
+ case LLAssetType::AT_NOTECARD:
+ return ".notecard";
+ case LLAssetType::AT_LANDMARK:
+ return ".landmark";
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ scratch = LLWearable::typeToTypeName(wear);
+ if(scratch == "invalid")
+ {
+ if(type == LLAssetType::AT_BODYPART)
+ scratch = "bodypart";
+ else
+ scratch = "clothing";
+ }
+ return "." + scratch;
+ default:
+ return "";
+ }
+}
+
+// static
+std::string DOInventoryBackup::getUniqueFilename(std::string filename, std::string extension)
+{
+ if(LLFile::isfile( (filename + extension).c_str() ))
+ {
+ int i = 1;
+ while(LLFile::isfile( (filename + llformat(" %d", i) + extension).c_str() ))
+ {
+ i++;
+ }
+ return filename + llformat(" %d", i) + extension;
+ }
+ return filename + extension;
+}
+
+// static
+std::string DOInventoryBackup::getUniqueDirname(std::string dirname)
+{
+ if(LLFile::isdir(dirname.c_str()))
+ {
+ int i = 1;
+ while(LLFile::isdir( (dirname + llformat(" %d", i)).c_str() ))
+ {
+ i++;
+ }
+ return dirname + llformat(" %d", i);
+ }
+ return dirname;
+}
+
+
+// static
+void DOInventoryBackup::download(LLInventoryItem* item, LLFloater* floater, loaded_callback_func onImage, LLGetAssetCallback onAsset)
+{
+ DOInventoryBackup::callbackdata* userdata = new DOInventoryBackup::callbackdata();
+ userdata->floater = floater;
+ userdata->item = item;
+ LLViewerImage* imagep;
+
+ //don't be a jerk. (this check probably breaks stuff)
+ if(item->getCreatorUUID() == gAgentID)
+ {
+ switch(item->getType())
+ {
+ case LLAssetType::AT_TEXTURE:
+ imagep = gImageList.getImage(item->getAssetUUID(), MIPMAP_TRUE, TRUE);
+ imagep->setLoadedCallbackNoAux( onImage, 0, TRUE, FALSE, userdata );
+ break;
+ case LLAssetType::AT_NOTECARD:
+ case LLAssetType::AT_SCRIPT:
+ case LLAssetType::AT_LSL_TEXT: // normal script download
+ case LLAssetType::AT_LSL_BYTECODE:
+ gAssetStorage->getInvItemAsset(LLHost::invalid,
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getPermissions().getOwner(),
+ LLUUID::null,
+ item->getUUID(),
+ item->getAssetUUID(),
+ item->getType(),
+ onAsset,
+ userdata, // user_data
+ TRUE);
+ break;
+ case LLAssetType::AT_SOUND:
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_ANIMATION:
+ case LLAssetType::AT_GESTURE:
+ default:
+ gAssetStorage->getAssetData(item->getAssetUUID(), item->getType(), onAsset, userdata, TRUE);
+ break;
+ }
+ }
+}
+
+// static
+void DOInventoryBackup::imageCallback(BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata)
+{
+ if(final)
+ {
+ DOInventoryBackup::callbackdata* data = static_cast(userdata);
+ LLInventoryItem* item = data->item;
+
+ if(!success)
+ {
+ LLSD args;
+ args["ERROR_MESSAGE"] = "Download didn't work on " + item->getName() + ".";
+ LLNotifications::instance().add("ErrorMessage", args);
+ return;
+ }
+
+ LLFilePicker& file_picker = LLFilePicker::instance();
+ if( !file_picker.getSaveFile( getSaveFilter(item), LLDir::getScrubbedFileName(item->getName())) )
+ {
+ // User canceled or we failed to acquire save file.
+ return;
+ }
+ // remember the user-approved/edited file name.
+ std::string filename = file_picker.getFirstFile();
+
+ LLPointer image_tga = new LLImageTGA;
+ if( !image_tga->encode( src ) )
+ {
+ LLSD args;
+ args["ERROR_MESSAGE"] = "Couldn't encode file.";
+ LLNotifications::instance().add("ErrorMessage", args);
+ }
+ else if( !image_tga->save( filename ) )
+ {
+ LLSD args;
+ args["ERROR_MESSAGE"] = "Couldn't write file.";
+ LLNotifications::instance().add("ErrorMessage", args);
+ }
+ }
+ else
+ {
+ src_vi->setBoostLevel(LLViewerImageBoostLevel::BOOST_UI);
+ }
+}
+
+// static
+void DOInventoryBackup::assetCallback(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status, LLExtStat ext_status)
+{
+ DOInventoryBackup::callbackdata* data = static_cast(user_data);
+ LLInventoryItem* item = data->item;
+
+ if(status != 0)
+ {
+ LLSD args;
+ args["ERROR_MESSAGE"] = "Download didn't work on " + item->getName() + ".";
+ LLNotifications::instance().add("ErrorMessage", args);
+ return;
+ }
+
+ // Todo: this doesn't work for static vfs shit
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ char* buffer = new char[size];
+ if (buffer == NULL)
+ {
+ llerrs << "Memory Allocation Failed" << llendl;
+ return;
+ }
+
+ file.read((U8*)buffer, size);
+
+ // Write it back out...
+
+ LLFilePicker& file_picker = LLFilePicker::instance();
+ if( !file_picker.getSaveFile( getSaveFilter(item), LLDir::getScrubbedFileName(item->getName())) )
+ {
+ // User canceled or we failed to acquire save file.
+ return;
+ }
+ // remember the user-approved/edited file name.
+ std::string filename = file_picker.getFirstFile();
+
+ std::ofstream export_file(filename.c_str(), std::ofstream::binary);
+ export_file.write(buffer, size);
+ export_file.close();
+}
+
+// static
+void DOInventoryBackup::climb(LLInventoryCategory* cat,
+ std::vector& cats,
+ std::vector& items)
+{
+ LLInventoryModel* model = &gInventory;
+
+ // Add this category
+ cats.push_back(cat);
+
+ LLInventoryModel::cat_array_t *direct_cats;
+ LLInventoryModel::item_array_t *direct_items;
+ model->getDirectDescendentsOf(cat->getUUID(), direct_cats, direct_items);
+
+ // Add items
+ LLInventoryModel::item_array_t::iterator item_iter = direct_items->begin();
+ LLInventoryModel::item_array_t::iterator item_end = direct_items->end();
+ for( ; item_iter != item_end; ++item_iter)
+ {
+ items.push_back(*item_iter);
+ }
+
+ // Do subcategories
+ LLInventoryModel::cat_array_t::iterator cat_iter = direct_cats->begin();
+ LLInventoryModel::cat_array_t::iterator cat_end = direct_cats->end();
+ for( ; cat_iter != cat_end; ++cat_iter)
+ {
+ climb(*cat_iter, cats, items);
+ }
+}
+
+// static
+std::string DOInventoryBackup::getPath(LLInventoryCategory* cat, std::vector cats)
+{
+ LLInventoryModel* model = &gInventory;
+ std::string path = LLDir::getScrubbedFileName(cat->getName());
+ LLInventoryCategory* parent = model->getCategory(cat->getParentUUID());
+ while(parent && (std::find(cats.begin(), cats.end(), parent) != cats.end()))
+ {
+ path = LLDir::getScrubbedFileName(parent->getName()) + "\\" + path;
+ parent = model->getCategory(parent->getParentUUID());
+ }
+ return path;
+}
+
+// static
+void DOInventoryBackup::save(LLFolderView* folder)
+{
+ LLInventoryModel* model = &gInventory;
+
+ std::set selected_items;
+ folder->getSelectionList(selected_items);
+
+ if(selected_items.size() < 1)
+ {
+ // No items selected? Omg
+ return;
+ }
+ else if(selected_items.size() == 1)
+ {
+ // One item. See if it's a folder
+ LLUUID id = *(selected_items.begin());
+ LLInventoryItem* item = model->getItem(id);
+ if(item)
+ {
+ if(!itemIsFolder(item))
+ {
+ // Single item, save it now
+ DOInventoryBackup::download((LLViewerInventoryItem*)item, NULL, imageCallback, assetCallback);
+ return;
+ }
+ }
+ }
+
+ // We got here? We need to save multiple items or at least make a folder
+
+ std::vector cats;
+ std::vector items;
+
+ // Make complete lists of child categories and items
+ std::set::iterator sel_iter = selected_items.begin();
+ std::set::iterator sel_end = selected_items.end();
+ for( ; sel_iter != sel_end; ++sel_iter)
+ {
+ LLInventoryCategory* cat = model->getCategory(*sel_iter);
+ if(cat)
+ {
+ climb(cat, cats, items);
+ }
+ }
+
+ // And what about items inside a folder that wasn't selected?
+ // I guess I will just add selected items, so long as they aren't already added
+ for(sel_iter = selected_items.begin(); sel_iter != sel_end; ++sel_iter)
+ {
+ LLInventoryItem* item = model->getItem(*sel_iter);
+ if(item)
+ {
+ if(std::find(items.begin(), items.end(), item) == items.end())
+ {
+ items.push_back(item);
+ LLInventoryCategory* parent = model->getCategory(item->getParentUUID());
+ if(std::find(cats.begin(), cats.end(), parent) == cats.end())
+ {
+ cats.push_back(parent);
+ }
+ }
+ }
+ }
+
+ DOInventoryBackupOrder* order = new DOInventoryBackupOrder();
+ order->mCats = cats;
+ order->mItems = items;
+ DOFloaterInventoryBackupSettings* floater = new DOFloaterInventoryBackupSettings(order);
+ floater->center();
+}
+
+
+
+DOFloaterInventoryBackup::DOFloaterInventoryBackup(std::string path, std::vector cats, std::vector items)
+: LLFloater(),
+ mPath(path),
+ mCats(cats),
+ mItems(items),
+ mBusy(0)
+{
+ mItemsTotal = mItems.size();
+ mItemsCompleted = 0;
+
+ DOFloaterInventoryBackup::sInstances.push_back(this);
+ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_backup.xml");
+}
+
+
+DOFloaterInventoryBackup::~DOFloaterInventoryBackup()
+{
+ DOFloaterInventoryBackup::sInstances.remove(this);
+}
+
+BOOL DOFloaterInventoryBackup::postBuild(void)
+{
+ // Make progress bar
+
+ /*
+ LLLineEditor* line = new LLLineEditor(
+ std::string("progress_line"),
+ LLRect(4, 80, 396, 60),
+ std::string("Progress"));
+ line->setEnabled(FALSE);
+ addChild(line);
+
+ LLViewBorder* border = new LLViewBorder(
+ "progress_border",
+ LLRect(4, 79, 395, 60));
+ addChild(border);
+ */
+
+ // Add all items to the list
+
+ LLScrollListCtrl* list = getChild("item_list");
+
+ std::vector::iterator item_iter = mItems.begin();
+ std::vector::iterator item_end = mItems.end();
+ for( ; item_iter != item_end; ++item_iter)
+ {
+ LLSD element;
+ element["id"] = (*item_iter)->getUUID();
+
+ LLSD& type_column = element["columns"][LIST_TYPE];
+ type_column["column"] = "type";
+ type_column["type"] = "icon";
+ type_column["value"] = "move_down_in.tga"; // FIXME
+
+ LLSD& name_column = element["columns"][LIST_NAME];
+ name_column["column"] = "name";
+ name_column["value"] = (*item_iter)->getName();
+
+ LLSD& status_column = element["columns"][LIST_STATUS];
+ status_column["column"] = "status";
+ status_column["value"] = "Pending";
+
+ list->addElement(element, ADD_BOTTOM);
+ }
+
+ // Setup and go!
+ mBusy = 1;
+ mItemIter = mItems.begin();
+ setStatus((*mItemIter)->getUUID(), "Downloading");
+ DOInventoryBackup::download(*mItemIter, this, DOFloaterInventoryBackup::imageCallback, DOFloaterInventoryBackup::assetCallback);
+ advance();
+
+ return TRUE;
+}
+
+void DOFloaterInventoryBackup::advance()
+{
+ while((mItemIter != mItems.end()) && (mBusy < 4))
+ {
+ mBusy++;
+ mItemIter++;
+ if(mItemIter >= mItems.end()) break;
+ setStatus((*mItemIter)->getUUID(), "Downloading");
+ DOInventoryBackup::download(*mItemIter, this, DOFloaterInventoryBackup::imageCallback, DOFloaterInventoryBackup::assetCallback);
+ }
+}
+
+void DOFloaterInventoryBackup::setStatus(LLUUID itemid, std::string status)
+{
+ LLScrollListCtrl* list = getChild("item_list");
+ std::vector items = list->getAllData();
+ std::vector::iterator iter = items.begin();
+ std::vector::iterator end = items.end();
+ for( ; iter != end; ++iter)
+ {
+ if((*iter)->getUUID() == itemid)
+ {
+ (*iter)->getColumn(LIST_STATUS)->setValue(status);
+ break;
+ }
+ }
+}
+
+void DOFloaterInventoryBackup::finishItem(LLUUID itemid, std::string status)
+{
+ // Update big happy progress bar
+ mItemsCompleted++;
+ LLView* progress_background = getChildView("progress_background", TRUE, TRUE);
+ LLRect rect = progress_background->getRect();
+ float item_count = (float)mItemsTotal;
+ float item_pos = (float)mItemsCompleted;
+ float rect_width = (float)rect.getWidth();
+ float incr = rect_width / item_count;
+ incr *= item_pos;
+ rect.mRight = rect.mLeft + (S32)incr;
+ LLView* progress_foreground = getChildView("progress_foreground", TRUE, TRUE);
+ progress_foreground->setRect(rect);
+
+ if(mItemsCompleted >= mItemsTotal)
+ {
+ childSetText("progress_background", llformat("Completed %d items.", mItemsTotal));
+ childSetVisible("progress_foreground", false);
+ }
+
+ // Update item status
+ setStatus(itemid, status);
+
+ // And advance
+ mBusy--;
+ advance();
+}
+
+// static
+void DOFloaterInventoryBackup::imageCallback(BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata)
+{
+ if(final)
+ {
+ DOInventoryBackup::callbackdata* data = static_cast(userdata);
+ DOFloaterInventoryBackup* floater = (DOFloaterInventoryBackup*)(data->floater);
+ LLInventoryItem* item = data->item;
+
+ if(std::find(DOFloaterInventoryBackup::sInstances.begin(), DOFloaterInventoryBackup::sInstances.end(), floater) == DOFloaterInventoryBackup::sInstances.end())
+ {
+ return;
+ }
+
+ if(!success)
+ {
+ floater->finishItem(item->getUUID(), "Failed download");
+ return;
+ }
+
+ std::string filename = floater->mPath + "\\" + DOInventoryBackup::getPath(gInventory.getCategory(item->getParentUUID()), floater->mCats) + "\\" + LLDir::getScrubbedFileName(item->getName());
+ filename = DOInventoryBackup::getUniqueFilename(filename, DOInventoryBackup::getExtension(item));
+
+ LLPointer image_tga = new LLImageTGA;
+ if( !image_tga->encode( src ) )
+ {
+ floater->finishItem(item->getUUID(), "Failed tga encode");
+ }
+ else if( !image_tga->save( filename ) )
+ {
+ floater->finishItem(item->getUUID(), "Failed save");
+ }
+ else
+ {
+ floater->finishItem(item->getUUID(), "Done");
+ }
+ }
+ else
+ {
+ src_vi->setBoostLevel(LLViewerImageBoostLevel::BOOST_UI);
+ }
+}
+
+// static
+void DOFloaterInventoryBackup::assetCallback(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status, LLExtStat ext_status)
+{
+ DOInventoryBackup::callbackdata* data = static_cast(user_data);
+ DOFloaterInventoryBackup* floater = (DOFloaterInventoryBackup*)(data->floater);
+ LLInventoryItem* item = data->item;
+
+ if(std::find(DOFloaterInventoryBackup::sInstances.begin(), DOFloaterInventoryBackup::sInstances.end(), floater) == DOFloaterInventoryBackup::sInstances.end())
+ {
+ return;
+ }
+
+ if(status != 0)
+ {
+ floater->finishItem(item->getUUID(), "Failed download");
+ return;
+ }
+
+ // Todo: this doesn't work for static vfs shit
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ char* buffer = new char[size];
+ if (buffer == NULL)
+ {
+ //llerrs << "Memory Allocation Failed" << llendl;
+ floater->finishItem(item->getUUID(), "Failed memory allocation");
+ return;
+ }
+
+ file.read((U8*)buffer, size);
+
+ // Write it back out...
+ std::string filename = floater->mPath + "\\" + DOInventoryBackup::getPath(gInventory.getCategory(item->getParentUUID()), floater->mCats) + "\\" + LLDir::getScrubbedFileName(item->getName());
+ filename = DOInventoryBackup::getUniqueFilename(filename, DOInventoryBackup::getExtension(item));
+
+ std::ofstream export_file(filename.c_str(), std::ofstream::binary);
+ export_file.write(buffer, size);
+ export_file.close();
+
+ floater->finishItem(item->getUUID(), "Done");
+}
+//
diff --git a/indra/newview/doinventorybackup.h b/indra/newview/doinventorybackup.h
new file mode 100644
index 000000000..fbd9df894
--- /dev/null
+++ b/indra/newview/doinventorybackup.h
@@ -0,0 +1,137 @@
+/**
+ * @file doinventorybackup.h
+ * @brief DOInventoryBackup Floaters and Inventory Backup System
+ * @author Day Oh
+ *
+ * $LicenseInfo:firstyear=2009&license=WTFPLV2$
+ *
+ */
+
+//
+#ifndef DO_DOINVENTORYBACKUP_H
+#define DO_DOINVENTORYBACKUP_H
+
+#include "llviewerinventory.h"
+#include "llfolderview.h"
+#include "llfilepicker.h"
+#include "llviewerimage.h"
+#include "llfloater.h"
+
+
+class DOInventoryBackupOrder
+{
+public:
+ DOInventoryBackupOrder();
+
+ std::string mPath;
+ std::vector mCats;
+ std::vector mItems;
+
+ bool mDownloadTextures;
+ bool mDownloadSounds;
+ bool mDownloadCallingCards;
+ bool mDownloadLandmarks;
+ bool mDownloadScripts;
+ bool mDownloadWearables;
+ bool mDownloadObjects;
+ bool mDownloadNotecards;
+ bool mDownloadAnimations;
+ bool mDownloadGestures;
+ //bool mDownloadOthers;
+};
+
+class DOFloaterInventoryBackupSettings
+: public LLFloater
+{
+public:
+ DOFloaterInventoryBackupSettings(DOInventoryBackupOrder* order);
+ BOOL postBuild(void);
+ static void onClickNext(void* userdata);
+
+ DOInventoryBackupOrder* mOrder;
+ virtual ~DOFloaterInventoryBackupSettings();
+};
+
+class DOFloaterInventoryBackup
+: public LLFloater
+{
+public:
+ DOFloaterInventoryBackup(std::string path, std::vector cats, std::vector items);
+ BOOL postBuild(void);
+
+ std::string mPath;
+ std::vector mCats;
+ std::vector mItems;
+ std::vector::iterator mItemIter;
+ int mBusy;
+
+ static std::list sInstances;
+
+private:
+ virtual ~DOFloaterInventoryBackup();
+ void setStatus(LLUUID itemid, std::string status);
+ void finishItem(LLUUID itemid, std::string status);
+ void advance();
+ static void imageCallback(BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata);
+ static void assetCallback(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status, LLExtStat ext_status);
+
+ int mItemsTotal;
+ int mItemsCompleted;
+
+ enum LIST_COLUMN_ORDER
+ {
+ LIST_TYPE,
+ LIST_NAME,
+ LIST_STATUS
+ };
+
+};
+
+class DOInventoryBackup
+{
+public:
+ static LLFilePicker::ESaveFilter getSaveFilter(LLInventoryItem* item);
+ static std::string getExtension(LLInventoryItem* item);
+ static std::string getUniqueFilename(std::string filename, std::string extension);
+ static std::string getUniqueDirname(std::string dirname);
+ static bool itemIsFolder(LLInventoryItem* item);
+ static void save(LLFolderView* folder);
+ static void download(LLInventoryItem* item, LLFloater* floater, loaded_callback_func onImage, LLGetAssetCallback onAsset);
+ static std::string getPath(LLInventoryCategory* cat, std::vector cats);
+
+ struct callbackdata
+ {
+ LLFloater* floater;
+ LLInventoryItem* item;
+ };
+
+private:
+ static void imageCallback(BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata);
+ static void assetCallback(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status, LLExtStat ext_status);
+ static void climb(LLInventoryCategory* cat,
+ std::vector& cats,
+ std::vector& items);
+};
+
+
+
+#endif
+//
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index 7a9fc3ee6..a43a6d8f5 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -7,14 +7,14 @@
## - Avoids using any OpenAL audio driver.
#export LL_BAD_OPENAL_DRIVER=x
## - Avoids using any FMOD audio driver.
-#export LL_BAD_FMOD_DRIVER=x
+export LL_BAD_FMOD_DRIVER=x
## - Avoids using the FMOD ESD audio driver.
#export LL_BAD_FMOD_ESD=x
## - Avoids using the FMOD OSS audio driver.
#export LL_BAD_FMOD_OSS=x
## - Avoids using the FMOD ALSA audio driver.
-export LL_BAD_FMOD_ALSA=x
+#export LL_BAD_FMOD_ALSA=x
## - Avoids the optional OpenGL extensions which have proven most problematic
## on some hardware. Disabling this option may cause BETTER PERFORMANCE but
@@ -114,7 +114,7 @@ if [ -n "$LL_TCMALLOC" ]; then
fi
fi
-export VIEWER_BINARY='inertia-do-not-run-directly'
+export VIEWER_BINARY='snowglobe-do-not-run-directly'
export SL_ENV='LD_LIBRARY_PATH="`pwd`"/lib:"`pwd`"/app_settings/mozilla-runtime-linux-i686:"${LD_LIBRARY_PATH}"'
export SL_CMD='$LL_WRAPPER bin/$VIEWER_BINARY'
export SL_OPT="`cat gridargs.dat` $@"
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 8f5882615..712d0c086 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -51,8 +51,11 @@
LLFilePicker LLFilePicker::sInstance;
#if LL_WINDOWS
-#define SOUND_FILTER L"Sounds (*.wav)\0*.wav\0"
-#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
+//
+#define SOUND_FILTER L"Sounds (*.wav; *.ogg)\0*.wav;*.ogg\0"
+#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png; *.jp2; *.j2k; *.j2c)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png;*.jp2;*.j2k;*.j2c\0"
+#define INVGZ_FILTER L"Inv cache (*.inv; *.inv.gz)\0*.inv;*.inv.gz\0"
+//
#define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0"
#ifdef _CORY_TESTING
#define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0"
@@ -192,6 +195,16 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
mOFN.lpstrFilter = RAW_FILTER \
L"\0";
break;
+ //
+ case FFLOAD_INVGZ:
+ mOFN.lpstrFilter = INVGZ_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_AO:
+ mOFN.lpstrFilter = AO_FILTER \
+ L"\0";
+ break;
+ //
default:
res = FALSE;
break;
@@ -451,6 +464,218 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename)
L"Compressed Images (*.j2c)\0*.j2c\0" \
L"\0";
break;
+ //
+ case FFSAVE_ANIMATN:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.animatn", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"animatn";
+ mOFN.lpstrFilter =
+ L"SL Animations (*.animatn)\0*.animatn\0" \
+ L"\0";
+ break;
+ case FFSAVE_OGG:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.ogg", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"ogg";
+ mOFN.lpstrFilter =
+ L"Ogg (*.ogg)\0*.ogg\0" \
+ L"\0";
+ break;
+ case FFSAVE_NOTECARD:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.notecard", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"notecard";
+ mOFN.lpstrFilter =
+ L"Notecards (*.notecard)\0*.notecard\0" \
+ L"\0";
+ break;
+ case FFSAVE_GESTURE:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.gesture", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"gesture";
+ mOFN.lpstrFilter =
+ L"Gestures (*.gesture)\0*.gesture\0" \
+ L"\0";
+ break;
+ case FFSAVE_LSL:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.lsl", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"lsl";
+ mOFN.lpstrFilter =
+ L"LSL (*.lsl)\0*.lsl\0" \
+ L"\0";
+ break;
+ case FFSAVE_SHAPE:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.shape", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"shape";
+ mOFN.lpstrFilter =
+ L"Shapes (*.shape)\0*.shape\0" \
+ L"\0";
+ break;
+ case FFSAVE_SKIN:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.skin", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"skin";
+ mOFN.lpstrFilter =
+ L"Skins (*.skin)\0*.skin\0" \
+ L"\0";
+ break;
+ case FFSAVE_HAIR:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.hair", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"hair";
+ mOFN.lpstrFilter =
+ L"Hair (*.hair)\0*.hair\0" \
+ L"\0";
+ break;
+ case FFSAVE_EYES:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.eyes", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"eyes";
+ mOFN.lpstrFilter =
+ L"Eyes (*.eyes)\0*.eyes\0" \
+ L"\0";
+ break;
+ case FFSAVE_SHIRT:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.shirt", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"shirt";
+ mOFN.lpstrFilter =
+ L"Shirts (*.shirt)\0*.shirt\0" \
+ L"\0";
+ break;
+ case FFSAVE_PANTS:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.pants", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"pants";
+ mOFN.lpstrFilter =
+ L"Pants (*.pants)\0*.pants\0" \
+ L"\0";
+ break;
+ case FFSAVE_SHOES:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.shoes", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"shoes";
+ mOFN.lpstrFilter =
+ L"Shoes (*.shoes)\0*.shoes\0" \
+ L"\0";
+ break;
+ case FFSAVE_SOCKS:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.socks", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"socks";
+ mOFN.lpstrFilter =
+ L"Socks (*.socks)\0*.socks\0" \
+ L"\0";
+ break;
+ case FFSAVE_JACKET:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.jacket", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"jacket";
+ mOFN.lpstrFilter =
+ L"Jackets (*.jacket)\0*.jacket\0" \
+ L"\0";
+ break;
+ case FFSAVE_GLOVES:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.gloves", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"gloves";
+ mOFN.lpstrFilter =
+ L"Gloves (*.gloves)\0*.gloves\0" \
+ L"\0";
+ break;
+ case FFSAVE_UNDERSHIRT:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.undershirt", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"undershirt";
+ mOFN.lpstrFilter =
+ L"Undershirts (*.undershirt)\0*.undershirt\0" \
+ L"\0";
+ break;
+ case FFSAVE_UNDERPANTS:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.underpants", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"underpants";
+ mOFN.lpstrFilter =
+ L"Underpants (*.underpants)\0*.underpants\0" \
+ L"\0";
+ break;
+ case FFSAVE_SKIRT:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.skirt", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"skirt";
+ mOFN.lpstrFilter =
+ L"Skirts (*.skirt)\0*.skirt\0" \
+ L"\0";
+ break;
+ case FFSAVE_LANDMARK:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.landmark", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"landmark";
+ mOFN.lpstrFilter =
+ L"Landmarks (*.landmark)\0*.landmark\0" \
+ L"\0";
+ break;
+ case FFSAVE_AO:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.ao", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"ao";
+ mOFN.lpstrFilter =
+ L"Animation overrides (*.ao)\0*.ao\0" \
+ L"\0";
+ break;
+ case FFSAVE_INVGZ:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.inv", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L".inv";
+ mOFN.lpstrFilter =
+ L"InvCache (*.inv)\0*.inv\0" \
+ L"\0";
+ break;
+ //
default:
return FALSE;
}
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index 104e0df29..da541cf3f 100644
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -91,6 +91,10 @@ public:
FFLOAD_XML = 6,
FFLOAD_SLOBJECT = 7,
FFLOAD_RAW = 8,
+ //
+ FFLOAD_INVGZ = 9,
+ FFLOAD_AO = 10,
+ //
};
enum ESaveFilter
@@ -110,6 +114,30 @@ public:
FFSAVE_J2C = 12,
FFSAVE_PNG = 13,
FFSAVE_JPEG = 14,
+ //
+ FFSAVE_ANIMATN = 15,
+ FFSAVE_OGG = 16,
+ FFSAVE_NOTECARD = 17,
+ FFSAVE_GESTURE = 18,
+ FFSAVE_LSL = 19,
+ // good grief
+ FFSAVE_SHAPE = 20,
+ FFSAVE_SKIN = 21,
+ FFSAVE_HAIR = 22,
+ FFSAVE_EYES = 23,
+ FFSAVE_SHIRT = 24,
+ FFSAVE_PANTS = 25,
+ FFSAVE_SHOES = 26,
+ FFSAVE_SOCKS = 27,
+ FFSAVE_JACKET = 28,
+ FFSAVE_GLOVES = 29,
+ FFSAVE_UNDERSHIRT = 30,
+ FFSAVE_UNDERPANTS = 31,
+ FFSAVE_SKIRT = 32,
+ FFSAVE_INVGZ = 33,
+ FFSAVE_LANDMARK = 34,
+ FFSAVE_AO = 35,
+ //
};
// open the dialog. This is a modal operation
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index bc6908a0c..44a05dde5 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -86,6 +86,7 @@
#include "lluictrlfactory.h"
#include "llselectmgr.h"
#include "llfloateropenobject.h"
+#include "dofloaterhex.h"
// Helpers
// bug in busy count inc/dec right now, logic is complex... do we really need it?
@@ -443,6 +444,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, std::vectorgetItem(mUUID);
+ if(!item) return;
+ if(item->getCreatorUUID() != gAgentID) return;
+ DOFloaterHex::show(mUUID);
+ }
else if ("copy_uuid" == action)
{
// Single item only
diff --git a/indra/newview/llviewerimage.cpp b/indra/newview/llviewerimage.cpp
index 3a14bf009..d0be919d6 100644
--- a/indra/newview/llviewerimage.cpp
+++ b/indra/newview/llviewerimage.cpp
@@ -1320,6 +1320,31 @@ void LLViewerImage::setLoadedCallback( loaded_callback_func loaded_callback,
}
}
+// this method is stupid, remove it if at all possible -Day
+void LLViewerImage::setLoadedCallbackNoAux( loaded_callback_func loaded_callback,
+ S32 discard_level, BOOL keep_imageraw, BOOL needs_aux, void* userdata)
+{
+ //
+ // Don't do ANYTHING here, just add it to the global callback list
+ //
+ if (mLoadedCallbackList.empty())
+ {
+ // Put in list to call this->doLoadedCallbacks() periodically
+ gImageList.mCallbackList.insert(this);
+ }
+
+ LLLoadedCallbackEntry* entryp = new LLLoadedCallbackEntry(loaded_callback, discard_level, keep_imageraw, userdata);
+ mLoadedCallbackList.push_back(entryp);
+ mNeedsAux = needs_aux;
+
+ if (mNeedsAux && mAuxRawImage.isNull() && getDiscardLevel() >= 0)
+ {
+ // We need aux data, but we've already loaded the image, and it didn't have any
+ llwarns << "No aux data available for callback for image:" << getID() << llendl;
+ }
+}
+//
+
bool LLViewerImage::doLoadedCallbacks()
{
if (mNeedsCreateTexture)
diff --git a/indra/newview/llviewerimage.h b/indra/newview/llviewerimage.h
index fdf8ff040..91037473b 100644
--- a/indra/newview/llviewerimage.h
+++ b/indra/newview/llviewerimage.h
@@ -246,6 +246,12 @@ public:
void setLoadedCallback(loaded_callback_func cb,
S32 discard_level, BOOL keep_imageraw, BOOL needs_aux,
void* userdata);
+
+//
+ void setLoadedCallbackNoAux(loaded_callback_func cb,
+ S32 discard_level, BOOL keep_imageraw, BOOL needs_aux,
+ void* userdata);
+//
// ONLY call from LLViewerImageList
BOOL createTexture(S32 usename = 0);
@@ -445,4 +451,6 @@ public:
static S32 sLLViewerImageCount ;
};
+
+
#endif
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index dc5f5af21..3ad069eda 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -1092,3 +1092,43 @@ void init_menu_file()
(new LLFileEnableUpload())->registerListener(gMenuHolder, "File.EnableUpload");
(new LLFileEnableSaveAs())->registerListener(gMenuHolder, "File.EnableSaveAs");
}
+
+//
+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()));
+}
+//
diff --git a/indra/newview/llviewermenufile.h b/indra/newview/llviewermenufile.h
index bf2129208..c92e82da3 100644
--- a/indra/newview/llviewermenufile.h
+++ b/indra/newview/llviewermenufile.h
@@ -35,6 +35,14 @@
#include "llassettype.h"
#include "llinventorytype.h"
+//
+#include "llviewerinventory.h"
+
+class NewResourceItemCallback : public LLInventoryCallback
+{
+ void fire(const LLUUID& inv_item);
+};
+//
class LLTransactionID;
diff --git a/indra/newview/skins/default/xui/en-us/floater_hex.xml b/indra/newview/skins/default/xui/en-us/floater_hex.xml
new file mode 100644
index 000000000..ceb91a5ae
--- /dev/null
+++ b/indra/newview/skins/default/xui/en-us/floater_hex.xml
@@ -0,0 +1,9 @@
+
+
+ Loading...
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en-us/floater_inventory_backup.xml b/indra/newview/skins/default/xui/en-us/floater_inventory_backup.xml
new file mode 100644
index 000000000..323851084
--- /dev/null
+++ b/indra/newview/skins/default/xui/en-us/floater_inventory_backup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en-us/floater_inventory_backup_settings.xml b/indra/newview/skins/default/xui/en-us/floater_inventory_backup_settings.xml
new file mode 100644
index 000000000..198e0775f
--- /dev/null
+++ b/indra/newview/skins/default/xui/en-us/floater_inventory_backup_settings.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/indra/newview/skins/default/xui/en-us/menu_inventory.xml b/indra/newview/skins/default/xui/en-us/menu_inventory.xml
index 952bf7c7b..72101a59a 100644
--- a/indra/newview/skins/default/xui/en-us/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en-us/menu_inventory.xml
@@ -129,6 +129,14 @@
mouse_opaque="true" name="Restore Item" width="128">
+