diff options
32 files changed, 754 insertions, 397 deletions
diff --git a/indra/llui/llclipboard.cpp b/indra/llui/llclipboard.cpp index 6910b962a1..14173fdbb0 100644 --- a/indra/llui/llclipboard.cpp +++ b/indra/llui/llclipboard.cpp @@ -34,109 +34,113 @@ #include "llview.h" #include "llwindow.h" -// Global singleton -LLClipboard gClipboard; - - -LLClipboard::LLClipboard() +LLClipboard::LLClipboard() : + mGeneration(0) { - mSourceItem = NULL; + reset(); } - LLClipboard::~LLClipboard() { + reset(); } - -void LLClipboard::copyFromSubstring(const LLWString &src, S32 pos, S32 len, const LLUUID& source_id ) +void LLClipboard::reset() { - mSourceID = source_id; - mString = src.substr(pos, len); - LLView::getWindow()->copyTextToClipboard( mString ); + // Increment the clipboard count + mGeneration++; + // Clear the clipboard + mObjects.clear(); + mCutMode = false; + mString = LLWString(); } -void LLClipboard::copyFromString(const LLWString &src, const LLUUID& source_id ) +// Copy the input uuid to the LL clipboard +bool LLClipboard::copyToClipboard(const LLUUID& src, const LLAssetType::EType type) { - mSourceID = source_id; - mString = src; - LLView::getWindow()->copyTextToClipboard( mString ); + reset(); + return addToClipboard(src, type); } -const LLWString& LLClipboard::getPasteWString( LLUUID* source_id ) +// Add the input uuid to the LL clipboard +// Convert the uuid to string and concatenate that string to the system clipboard if legit +bool LLClipboard::addToClipboard(const LLUUID& src, const LLAssetType::EType type) { - if( mSourceID.notNull() ) + bool res = false; + if (src.notNull()) { - LLWString temp_string; - LLView::getWindow()->pasteTextFromClipboard(temp_string); - - if( temp_string != mString ) + res = true; + if (LLAssetType::lookupIsAssetIDKnowable(type)) { - mSourceID.setNull(); - mString = temp_string; + LLWString source = utf8str_to_wstring(src.asString()); + res = addToClipboard(source, 0, source.size()); + } + if (res) + { + mObjects.push_back(src); + mGeneration++; } } - else - { - LLView::getWindow()->pasteTextFromClipboard(mString); - } + return res; +} - if( source_id ) +bool LLClipboard::pasteFromClipboard(std::vector<LLUUID>& inv_objects) const +{ + bool res = false; + S32 count = mObjects.size(); + if (count > 0) { - *source_id = mSourceID; + res = true; + inv_objects.clear(); + for (S32 i = 0; i < count; i++) + { + inv_objects.push_back(mObjects[i]); + } } - - return mString; + return res; } +// Returns true if the LL Clipboard has pasteable items in it +bool LLClipboard::hasContents() const +{ + return (mObjects.size() > 0); +} -BOOL LLClipboard::canPasteString() const +// Returns true if the input uuid is in the list of clipboard objects +bool LLClipboard::isOnClipboard(const LLUUID& object) const { - return LLView::getWindow()->isClipboardTextAvailable(); + std::vector<LLUUID>::const_iterator iter = std::find(mObjects.begin(), mObjects.end(), object); + return (iter != mObjects.end()); } +// Copy the input string to the LL and the system clipboard +bool LLClipboard::copyToClipboard(const LLWString &src, S32 pos, S32 len, bool use_primary) +{ + return addToClipboard(src, pos, len, use_primary); +} -void LLClipboard::copyFromPrimarySubstring(const LLWString &src, S32 pos, S32 len, const LLUUID& source_id ) +// Concatenate the input string to the LL and the system clipboard +bool LLClipboard::addToClipboard(const LLWString &src, S32 pos, S32 len, bool use_primary) { - mSourceID = source_id; mString = src.substr(pos, len); - LLView::getWindow()->copyTextToPrimary( mString ); + return (use_primary ? LLView::getWindow()->copyTextToPrimary(mString) : LLView::getWindow()->copyTextToClipboard(mString)); } - -const LLWString& LLClipboard::getPastePrimaryWString( LLUUID* source_id ) +// Copy the System clipboard to the output string. +// Manage the LL Clipboard / System clipboard consistency +bool LLClipboard::pasteFromClipboard(LLWString &dst, bool use_primary) { - if( mSourceID.notNull() ) - { - LLWString temp_string; - LLView::getWindow()->pasteTextFromPrimary(temp_string); - - if( temp_string != mString ) - { - mSourceID.setNull(); - mString = temp_string; - } - } - else - { - LLView::getWindow()->pasteTextFromPrimary(mString); - } - - if( source_id ) + bool res = (use_primary ? LLView::getWindow()->pasteTextFromPrimary(dst) : LLView::getWindow()->pasteTextFromClipboard(dst)); + if (res) { - *source_id = mSourceID; + mString = dst; } - - return mString; + return res; } - -BOOL LLClipboard::canPastePrimaryString() const +// Return true if there's something on the System clipboard +bool LLClipboard::isTextAvailable(bool use_primary) const { - return LLView::getWindow()->isPrimaryTextAvailable(); + return (use_primary ? LLView::getWindow()->isPrimaryTextAvailable() : LLView::getWindow()->isClipboardTextAvailable()); } -void LLClipboard::setSourceObject(const LLUUID& source_id, LLAssetType::EType type) -{ - mSourceItem = new LLInventoryObject (source_id, LLUUID::null, type, ""); -} diff --git a/indra/llui/llclipboard.h b/indra/llui/llclipboard.h index 9371b94284..fd2e7610df 100644 --- a/indra/llui/llclipboard.h +++ b/indra/llui/llclipboard.h @@ -27,46 +27,68 @@ #ifndef LL_LLCLIPBOARD_H #define LL_LLCLIPBOARD_H +#include <boost/function.hpp> #include "llstring.h" #include "lluuid.h" #include "stdenums.h" +#include "llsingleton.h" +#include "llassettype.h" #include "llinventory.h" +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLClipboard +// +// This class is used to cut/copy/paste text strings and inventory items around +// the world. Use LLClipboard::instance().method() to use its methods. +// Note that the text and UUIDs are loosely coupled only. There are few cases +// where the viewer does offer a serialized version of the UUID on the clipboard. +// In those case, the text is overridden when copying/cutting the item. +// In all other cases, the text and the UUIDs are very much independent. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLClipboard +class LLClipboard : public LLSingleton<LLClipboard> { public: LLClipboard(); ~LLClipboard(); + + // Clears the clipboard + void reset(); + // Returns the state of the clipboard so client can know if it has been modified (comparing with tracked state) + int getGeneration() const { return mGeneration; } - /* We support two flavors of clipboard. The default is the explicitly - copy-and-pasted clipboard. The second is the so-called 'primary' clipboard - which is implicitly copied upon selection on platforms which expect this - (i.e. X11/Linux). */ - - void copyFromSubstring(const LLWString ©_from, S32 pos, S32 len, const LLUUID& source_id = LLUUID::null ); - void copyFromString(const LLWString ©_from, const LLUUID& source_id = LLUUID::null ); - BOOL canPasteString() const; - const LLWString& getPasteWString(LLUUID* source_id = NULL); + // Text strings management: + // ------------------------ + // We support two flavors of text clipboards. The default is the explicitly + // copy-and-pasted clipboard. The second is the so-called 'primary' clipboard + // which is implicitly copied upon selection on platforms which expect this + // (i.e. X11/Linux, Mac). + bool copyToClipboard(const LLWString& src, S32 pos, S32 len, bool use_primary = false); + bool addToClipboard(const LLWString& src, S32 pos, S32 len, bool use_primary = false); + bool pasteFromClipboard(LLWString& dst, bool use_primary = false); + bool isTextAvailable(bool use_primary = false) const; + + // Object list management: + // ----------------------- + // Clears and adds one single object to the clipboard + bool copyToClipboard(const LLUUID& src, const LLAssetType::EType type = LLAssetType::AT_NONE); + // Adds one object to the current list of objects on the clipboard + bool addToClipboard(const LLUUID& src, const LLAssetType::EType type = LLAssetType::AT_NONE); + // Gets a copy of the objects on the clipboard + bool pasteFromClipboard(std::vector<LLUUID>& inventory_objects) const; + + bool hasContents() const; // True if the clipboard has pasteable objects + bool isOnClipboard(const LLUUID& object) const; // True if the input object uuid is on the clipboard - void copyFromPrimarySubstring(const LLWString ©_from, S32 pos, S32 len, const LLUUID& source_id = LLUUID::null ); - BOOL canPastePrimaryString() const; - const LLWString& getPastePrimaryWString(LLUUID* source_id = NULL); + bool isCutMode() const { return mCutMode; } + void setCutMode(bool mode) { mCutMode = mode; mGeneration++; } - // Support clipboard for object known only by their uuid and asset type - void setSourceObject(const LLUUID& source_id, LLAssetType::EType type); - const LLInventoryObject* getSourceObject() { return mSourceItem; } - private: - LLUUID mSourceID; - LLWString mString; - LLInventoryObject* mSourceItem; + std::vector<LLUUID> mObjects; // Objects on the clipboard. Can be empty while mString contains something licit (e.g. text from chat) + LLWString mString; // The text string. If mObjects is not empty, this string is reflecting them (UUIDs for the moment) if the asset type is knowable. + bool mCutMode; // This is a convenience flag for the viewer. + int mGeneration; // Incremented when the clipboard changes so that interested parties can check for changes on the clipboard. }; - -// Global singleton -extern LLClipboard gClipboard; - - #endif // LL_LLCLIPBOARD_H diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 7e84814c51..d0fbf4b913 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1047,7 +1047,7 @@ void LLLineEditor::cut() // Prepare for possible rollback LLLineEditorRollback rollback( this ); - gClipboard.copyFromSubstring( mText.getWString(), left_pos, length ); + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); deleteSelection(); // Validate new string and rollback the if needed. @@ -1078,13 +1078,13 @@ void LLLineEditor::copy() { S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 length = llabs( mSelectionStart - mSelectionEnd ); - gClipboard.copyFromSubstring( mText.getWString(), left_pos, length ); + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length ); } } BOOL LLLineEditor::canPaste() const { - return !mReadOnly && gClipboard.canPasteString(); + return !mReadOnly && LLClipboard::instance().isTextAvailable(); } void LLLineEditor::paste() @@ -1115,14 +1115,7 @@ void LLLineEditor::pasteHelper(bool is_primary) if (can_paste_it) { LLWString paste; - if (is_primary) - { - paste = gClipboard.getPastePrimaryWString(); - } - else - { - paste = gClipboard.getPasteWString(); - } + LLClipboard::instance().pasteFromClipboard(paste, is_primary); if (!paste.empty()) { @@ -1209,13 +1202,13 @@ void LLLineEditor::copyPrimary() { S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 length = llabs( mSelectionStart - mSelectionEnd ); - gClipboard.copyFromPrimarySubstring( mText.getWString(), left_pos, length ); + LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true); } } BOOL LLLineEditor::canPastePrimary() const { - return !mReadOnly && gClipboard.canPastePrimaryString(); + return !mReadOnly && LLClipboard::instance().isTextAvailable(true); } void LLLineEditor::updatePrimary() diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 466fac33ea..b3e1b63db5 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -2504,7 +2504,7 @@ void LLScrollListCtrl::copy() { buffer += (*itor)->getContentsCSV() + "\n"; } - gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length()); + LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, buffer.length()); } // virtual diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 3409b6817d..9720dded6c 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1332,7 +1332,7 @@ void LLTextEditor::cut() } S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 length = llabs( mSelectionStart - mSelectionEnd ); - gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID ); + LLClipboard::instance().copyToClipboard( getWText(), left_pos, length); deleteSelection( FALSE ); onKeyStroke(); @@ -1352,12 +1352,12 @@ void LLTextEditor::copy() } S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 length = llabs( mSelectionStart - mSelectionEnd ); - gClipboard.copyFromSubstring(getWText(), left_pos, length, mSourceID); + LLClipboard::instance().copyToClipboard(getWText(), left_pos, length); } BOOL LLTextEditor::canPaste() const { - return !mReadOnly && gClipboard.canPasteString(); + return !mReadOnly && LLClipboard::instance().isTextAvailable(); } // paste from clipboard @@ -1393,16 +1393,8 @@ void LLTextEditor::pasteHelper(bool is_primary) return; } - LLUUID source_id; LLWString paste; - if (is_primary) - { - paste = gClipboard.getPastePrimaryWString(&source_id); - } - else - { - paste = gClipboard.getPasteWString(&source_id); - } + LLClipboard::instance().pasteFromClipboard(paste, is_primary); if (paste.empty()) { @@ -1475,12 +1467,12 @@ void LLTextEditor::copyPrimary() } S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); S32 length = llabs( mSelectionStart - mSelectionEnd ); - gClipboard.copyFromPrimarySubstring(getWText(), left_pos, length, mSourceID); + LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true); } BOOL LLTextEditor::canPastePrimary() const { - return !mReadOnly && gClipboard.canPastePrimaryString(); + return !mReadOnly && LLClipboard::instance().isTextAvailable(true); } void LLTextEditor::updatePrimary() diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 66361c8fbf..0593475c54 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -288,7 +288,6 @@ set(viewer_SOURCE_FILES llinspectremoteobject.cpp llinspecttoast.cpp llinventorybridge.cpp - llinventoryclipboard.cpp llinventoryfilter.cpp llinventoryfunctions.cpp llinventoryicon.cpp @@ -843,7 +842,6 @@ set(viewer_HEADER_FILES llinspectremoteobject.h llinspecttoast.h llinventorybridge.h - llinventoryclipboard.h llinventoryfilter.h llinventoryfunctions.h llinventoryicon.h diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index f4b6dc2c81..575b613ccf 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -39,7 +39,7 @@ #include "llagent.h" #include "llclipboard.h" -#include "llinventoryclipboard.h" +#include "llclipboard.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llfloatersidepanelcontainer.h" @@ -1118,7 +1118,7 @@ BOOL LLFavoritesBarCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) } void copy_slurl_to_clipboard_cb(std::string& slurl) { - gClipboard.copyFromString(utf8str_to_wstring(slurl)); + LLClipboard::instance().copyToClipboard(utf8str_to_wstring(slurl),0,slurl.size()); LLSD args; args["SLURL"] = slurl; @@ -1187,7 +1187,7 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) } else if (action == "copy") { - LLInventoryClipboard::instance().store(mSelectedItemID); + LLClipboard::instance().copyToClipboard(mSelectedItemID, LLAssetType::AT_LANDMARK); } else if (action == "paste") { @@ -1211,13 +1211,13 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) BOOL LLFavoritesBarCtrl::isClipboardPasteable() const { - if (!LLInventoryClipboard::instance().hasContents()) + if (!LLClipboard::instance().hasContents()) { return FALSE; } LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); S32 count = objects.count(); for(S32 i = 0; i < count; i++) { @@ -1246,7 +1246,7 @@ void LLFavoritesBarCtrl::pastFromClipboard() const { LLInventoryItem* item = NULL; LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); S32 count = objects.count(); LLUUID parent_id(mFavoriteFolderId); for(S32 i = 0; i < count; i++) diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index 9ed9d10c75..56051ff684 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -32,7 +32,7 @@ #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" -#include "llinventoryclipboard.h" +#include "llclipboard.h" #include "llagent.h" #include "llappearancemgr.h" @@ -397,11 +397,11 @@ bool LLFloaterGesture::isActionEnabled(const LLSD& command) std::string command_name = command.asString(); if("paste" == command_name) { - if(!LLInventoryClipboard::instance().hasContents()) + if(!LLClipboard::instance().hasContents()) return false; LLDynamicArray<LLUUID> ids; - LLInventoryClipboard::instance().retrieve(ids); + LLClipboard::instance().pasteFromClipboard(ids); for(LLDynamicArray<LLUUID>::iterator it = ids.begin(); it != ids.end(); it++) { LLInventoryItem* item = gInventory.getItem(*it); @@ -496,27 +496,26 @@ void LLFloaterGesture::onActivateBtnClick() void LLFloaterGesture::onCopyPasteAction(const LLSD& command) { std::string command_name = command.asString(); - // since we select this comman inventory item had already arrived . + // Since we select this command, the inventory items must have already arrived if("copy_gesture" == command_name) { uuid_vec_t ids; getSelectedIds(ids); - // make sure that clopboard is empty - LLInventoryClipboard::instance().reset(); + // Make sure the clipboard is empty + LLClipboard::instance().reset(); for(uuid_vec_t::iterator it = ids.begin(); it != ids.end(); it++) { LLInventoryItem* item = gInventory.getItem(*it); if(item && item->getInventoryType() == LLInventoryType::IT_GESTURE) { - LLInventoryClipboard::instance().add(item->getUUID()); + LLClipboard::instance().addToClipboard(item->getUUID(),LLAssetType::AT_GESTURE); } } } else if ("paste" == command_name) { - LLInventoryClipboard& clipbord = LLInventoryClipboard::instance(); LLDynamicArray<LLUUID> ids; - clipbord.retrieve(ids); + LLClipboard::instance().pasteFromClipboard(ids); if(ids.empty() || !gInventory.isCategoryComplete(mGestureFolderID)) return; LLInventoryCategory* gesture_dir = gInventory.getCategory(mGestureFolderID); @@ -536,11 +535,11 @@ void LLFloaterGesture::onCopyPasteAction(const LLSD& command) gesture_dir->getUUID(), getString("copy_name", string_args), cb); } } - clipbord.reset(); + LLClipboard::instance().reset(); } else if ("copy_uuid" == command_name) { - gClipboard.copyFromString(utf8str_to_wstring(mGestureList->getCurrentID().asString()), mGestureList->getCurrentID()); + LLClipboard::instance().copyToClipboard(mGestureList->getCurrentID(),LLAssetType::AT_GESTURE); } } diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 6c4b781122..068a6407f7 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -30,7 +30,7 @@ #include "llcallbacklist.h" #include "llinventorybridge.h" -#include "llinventoryclipboard.h" // *TODO: remove this once hack below gone. +#include "llclipboard.h" // *TODO: remove this once hack below gone. #include "llinventoryfilter.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" @@ -456,8 +456,8 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen } else { - folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? - (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter + folderp->setVisible((show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? + (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation)))); } if (folderp->getVisible()) @@ -1045,6 +1045,36 @@ bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector<LLFol return false; } +// static +void LLFolderView::removeCutItems() +{ + // There's no item in "cut" mode on the clipboard -> exit + if (!LLClipboard::instance().isCutMode()) + return; + + // Get the list of clipboard item uuids and iterate through them + LLDynamicArray<LLUUID> objects; + LLClipboard::instance().pasteFromClipboard(objects); + for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin(); + iter != objects.end(); + ++iter) + { + const LLUUID& item_id = (*iter); + LLInventoryObject *obj = gInventory.getObject(item_id); + if (obj) + { + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + remove_category(&gInventory, item_id); + } + else + { + remove_item(&gInventory, item_id); + } + } + } +} + void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -1324,7 +1354,7 @@ BOOL LLFolderView::canCopy() const void LLFolderView::copy() { // *NOTE: total hack to clear the inventory clipboard - LLInventoryClipboard::instance().reset(); + LLClipboard::instance().reset(); S32 count = mSelectedItems.size(); if(getVisible() && getEnabled() && (count > 0)) { @@ -1365,7 +1395,7 @@ BOOL LLFolderView::canCut() const void LLFolderView::cut() { // clear the inventory clipboard - LLInventoryClipboard::instance().reset(); + LLClipboard::instance().reset(); S32 count = mSelectedItems.size(); if(getVisible() && getEnabled() && (count > 0)) { @@ -1379,6 +1409,7 @@ void LLFolderView::cut() listener->cutToClipboard(); } } + LLFolderView::removeCutItems(); } mSearchString.clear(); } @@ -2139,10 +2170,10 @@ bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) removeSelectedItems(); return true; } - - if ("copy" == action) - { - LLInventoryClipboard::instance().reset(); + if (("copy" == action) || ("cut" == action)) + { + // Clear the clipboard before we start adding things on it + LLClipboard::instance().reset(); } static const std::string change_folder_string = "change_folder_type_"; diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 5c5a3a894f..da8bb15f8e 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -80,8 +80,8 @@ protected: //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFolderView // -// Th LLFolderView represents the root level folder view object. It -// manages the screen region of the folder view. +// The LLFolderView represents the root level folder view object. +// It manages the screen region of the folder view. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler @@ -110,7 +110,7 @@ public: virtual LLFolderView* getRoot() { return this; } - // FolderViews default to sort by name. This will change that, + // FolderViews default to sort by name. This will change that, // and resort the items if necessary. void setSortOrder(U32 order); void setFilterPermMask(PermissionMask filter_perm_mask); @@ -139,20 +139,20 @@ public: virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse); virtual BOOL addFolder( LLFolderViewFolder* folder); - // Finds width and height of this object and it's children. Also - // makes sure that this view and it's children are the right size. + // Find width and height of this object and its children. Also + // makes sure that this view and its children are the right size. virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); void arrangeAll() { mArrangeGeneration++; } S32 getArrangeGeneration() { return mArrangeGeneration; } - // applies filters to control visibility of inventory items + // Apply filters to control visibility of inventory items virtual void filter( LLInventoryFilter& filter); - // get the last selected item + // Get the last selected item virtual LLFolderViewItem* getCurSelectedItem( void ); - // Record the selected item and pass it down the hierachy. + // Record the selected item and pass it down the hierarchy. virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); @@ -162,13 +162,13 @@ public: // Called once a frame to update the selection if mSelectThisID has been set void updateSelection(); - // This method is used to toggle the selection of an item. Walks - // children, and keeps track of selected objects. + // This method is used to toggle the selection of an item. + // Walks children and keeps track of selected objects. virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); virtual std::set<LLUUID> getSelectionList() const; - // make sure if ancestor is selected, descendents are not + // Make sure if ancestor is selected, descendents are not void sanitizeSelection(); void clearSelection(); void addToSelectionList(LLFolderViewItem* item); @@ -179,21 +179,22 @@ public: void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } - // deletion functionality + // Deletion functionality void removeSelectedItems(); + static void removeCutItems(); - // open the selected item. + // Open the selected item void openSelectedItems( void ); void propertiesSelectedItems( void ); - // change the folder type + // Change the folder type void changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type); void autoOpenItem(LLFolderViewFolder* item); void closeAutoOpenedFolders(); BOOL autoOpenTest(LLFolderViewFolder* item); - // copy & paste + // Copy & paste virtual void copy(); virtual BOOL canCopy() const; @@ -206,7 +207,7 @@ public: virtual void doDelete(); virtual BOOL canDoDelete() const; - // public rename functionality - can only start the process + // Public rename functionality - can only start the process void startRenamingSelectedItem( void ); // These functions were used when there was only one folderview, @@ -347,7 +348,7 @@ protected: /** * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. - * NOTE: For now it uses only to cut LLFolderViewItem::mLabel text to be used for Landmarks in Places Panel. + * NOTE: For now it's used only to cut LLFolderViewItem::mLabel text for Landmarks in Places Panel. */ bool mUseEllipses; // See EXT-719 diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h index aee31ca033..06682dcbf1 100644 --- a/indra/newview/llfoldervieweventlistener.h +++ b/indra/newview/llfoldervieweventlistener.h @@ -75,7 +75,7 @@ public: virtual void move( LLFolderViewEventListener* parent_listener ) = 0; virtual BOOL isItemCopyable() const = 0; virtual BOOL copyToClipboard() const = 0; - virtual void cutToClipboard() = 0; + virtual BOOL cutToClipboard() const = 0; virtual BOOL isClipboardPasteable() const = 0; virtual void pasteFromClipboard() = 0; virtual void pasteLinkFromClipboard() = 0; diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index afad27b4e0..712d3e4583 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -40,6 +40,7 @@ #include "llviewerwindow.h" // Argh, only for setCursor() // linden library includes +#include "llclipboard.h" #include "llfocusmgr.h" // gFocusMgr #include "lltrans.h" @@ -1002,7 +1003,7 @@ void LLFolderViewItem::draw() LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; if (highlight_link) color = sLinkColor; if (in_library) color = sLibraryColor; - + F32 right_x = 0; F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); @@ -1158,7 +1159,36 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) mNeedsSort = false; } + // evaluate mHasVisibleChildren mHasVisibleChildren = hasFilteredDescendants(filter_generation); + if (mHasVisibleChildren) + { + // We have to verify that there's at least one child that's not filtered out + bool found = false; + // Try the items first + for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit) + { + LLFolderViewItem* itemp = (*iit); + found = (itemp->getFiltered(filter_generation)); + if (found) + break; + } + if (!found) + { + // If no item found, try the folders + for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) + { + LLFolderViewFolder* folderp = (*fit); + found = ( folderp->getListener() + && (folderp->getFiltered(filter_generation) + || (folderp->getFilteredFolder(filter_generation) + && folderp->hasFilteredDescendants(filter_generation)))); + if (found) + break; + } + } + mHasVisibleChildren = found; + } // calculate height as a single item (without any children), and reshapes rectangle to match LLFolderViewItem::arrange( width, height, filter_generation ); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index da85c81cf2..8d3ac3e723 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -47,7 +47,7 @@ #include "llgiveinventory.h" #include "llimfloater.h" #include "llimview.h" -#include "llinventoryclipboard.h" +#include "llclipboard.h" #include "llinventorydefines.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" @@ -143,6 +143,42 @@ bool isMarketplaceSendAction(const std::string& action) return ("send_to_marketplace" == action); } +// Used by LLFolderBridge as callback for directory fetching recursion +class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver +{ +public: + LLRightClickInventoryFetchDescendentsObserver(const uuid_vec_t& ids) : LLInventoryFetchDescendentsObserver(ids) {} + ~LLRightClickInventoryFetchDescendentsObserver() {} + virtual void execute(bool clear_observer = false); + virtual void done() + { + execute(true); + } +}; + +// Used by LLFolderBridge as callback for directory content items fetching +class LLRightClickInventoryFetchObserver : public LLInventoryFetchItemsObserver +{ +public: + LLRightClickInventoryFetchObserver(const uuid_vec_t& ids) : LLInventoryFetchItemsObserver(ids) { }; + ~LLRightClickInventoryFetchObserver() {} + void execute(bool clear_observer = false) + { + if (clear_observer) + { + dec_busy_count(); + gInventory.removeObserver(this); + delete this; + } + // we've downloaded all the items, so repaint the dialog + LLFolderBridge::staticFolderOptionsMenu(); + } + virtual void done() + { + execute(true); + } +}; + // +=================================================+ // | LLInvFVBridge | // +=================================================+ @@ -215,13 +251,27 @@ BOOL LLInvFVBridge::isLink() const /** * @brief Adds this item into clipboard storage */ -void LLInvFVBridge::cutToClipboard() +BOOL LLInvFVBridge::cutToClipboard() const { - if(isItemMovable()) + const LLInventoryObject* obj = gInventory.getObject(mUUID); + if (obj && isItemMovable() && isItemRemovable()) { - LLInventoryClipboard::instance().cut(mUUID); + LLClipboard::instance().setCutMode(true); + return LLClipboard::instance().addToClipboard(mUUID); } + return FALSE; } + +BOOL LLInvFVBridge::copyToClipboard() const +{ + const LLInventoryObject* obj = gInventory.getObject(mUUID); + if (obj && isItemCopyable()) + { + return LLClipboard::instance().addToClipboard(mUUID); + } + return FALSE; +} + // *TODO: make sure this does the right thing void LLInvFVBridge::showProperties() { @@ -409,7 +459,8 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* BOOL LLInvFVBridge::isClipboardPasteable() const { - if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory()) + // Return FALSE on degenerated cases: empty clipboard, no inventory, no agent + if (!LLClipboard::instance().hasContents() || !isAgentInventory()) { return FALSE; } @@ -419,37 +470,42 @@ BOOL LLInvFVBridge::isClipboardPasteable() const return FALSE; } - const LLUUID &agent_id = gAgent.getID(); + // In cut mode, whatever is on the clipboard is always pastable + if (LLClipboard::instance().isCutMode()) + { + return TRUE; + } + // In normal mode, we need to check each element of the clipboard to know if we can paste or not LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); S32 count = objects.count(); for(S32 i = 0; i < count; i++) { const LLUUID &item_id = objects.get(i); - // Can't paste folders + // Folders are pastable if all items in there are copyable const LLInventoryCategory *cat = model->getCategory(item_id); - if (cat) - { - return FALSE; - } - - const LLInventoryItem *item = model->getItem(item_id); - if (item) + if (cat) { - if (!item->getPermissions().allowCopyBy(agent_id)) - { + LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, item_id); + if (!cat_br.isItemCopyable()) return FALSE; - } + // Skip to the next item in the clipboard + continue; } + + // Each item must be copyable to be pastable + LLItemBridge item_br(mInventoryPanel.get(), mRoot, item_id); + if (!item_br.isItemCopyable()) + return FALSE; } return TRUE; } BOOL LLInvFVBridge::isClipboardPasteableAsLink() const { - if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory()) + if (!LLClipboard::instance().hasContents() || !isAgentInventory()) { return FALSE; } @@ -460,7 +516,7 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const } LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); S32 count = objects.count(); for(S32 i = 0; i < count; i++) { @@ -611,6 +667,12 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Copy")); } + items.push_back(std::string("Cut")); + if (!isItemMovable() || !isItemRemovable()) + { + disabled_items.push_back(std::string("Cut")); + } + if (canListOnMarketplace()) { items.push_back(std::string("Marketplace Separator")); @@ -1290,6 +1352,12 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer)); return; } + else if ("cut" == action) + { + cutToClipboard(); + LLFolderView::removeCutItems(); + return; + } else if ("copy" == action) { copyToClipboard(); @@ -1297,7 +1365,6 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) } else if ("paste" == action) { - // Single item only LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; @@ -1672,16 +1739,6 @@ BOOL LLItemBridge::isItemCopyable() const return FALSE; } -BOOL LLItemBridge::copyToClipboard() const -{ - if(isItemCopyable()) - { - LLInventoryClipboard::instance().add(mUUID); - return TRUE; - } - return FALSE; -} - LLViewerInventoryItem* LLItemBridge::getItem() const { LLViewerInventoryItem* item = NULL; @@ -1713,18 +1770,22 @@ LLHandle<LLFolderBridge> LLFolderBridge::sSelf; BOOL LLFolderBridge::isItemMovable() const { LLInventoryObject* obj = getInventoryObject(); - if(obj) + if (obj) { - return (!LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType())); + // If it's a protected type folder, we can't move it + if (LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType())) + return FALSE; + return TRUE; } return FALSE; } void LLFolderBridge::selectItem() { + // Have no fear: the first thing start() does is to test if everything for that folder has been fetched... + LLInventoryModelBackgroundFetch::instance().start(getUUID(), true); } - // Iterate through a folder's children to determine if // all the children are removable. class LLIsItemRemovable : public LLFolderViewFunctor @@ -1780,18 +1841,34 @@ BOOL LLFolderBridge::isUpToDate() const BOOL LLFolderBridge::isItemCopyable() const { - // Can copy folders to paste-as-link, but not for straight paste. - return gSavedSettings.getBOOL("InventoryLinking"); -} - -BOOL LLFolderBridge::copyToClipboard() const -{ - if(isItemCopyable()) + // Folders are copyable if items in them are, recursively, copyable. + + // Get the content of the folder + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(mUUID,cat_array,item_array); + + // Check the items + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + LLItemBridge item_br(mInventoryPanel.get(), mRoot, item->getUUID()); + if (!item_br.isItemCopyable()) + return FALSE; + } + + // Check the folders + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) { - LLInventoryClipboard::instance().add(mUUID); - return TRUE; + LLViewerInventoryCategory* category = *iter; + LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, category->getUUID()); + if (!cat_br.isItemCopyable()) + return FALSE; } - return FALSE; + + return TRUE; } BOOL LLFolderBridge::isClipboardPasteable() const @@ -1809,7 +1886,7 @@ BOOL LLFolderBridge::isClipboardPasteable() const } LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); const LLViewerInventoryCategory *current_cat = getCategory(); // Search for the direct descendent of current Friends subfolder among all pasted items, @@ -1847,7 +1924,7 @@ BOOL LLFolderBridge::isClipboardPasteableAsLink() const const BOOL is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat ); const LLUUID ¤t_cat_id = current_cat->getUUID(); LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); S32 count = objects.count(); for(S32 i = 0; i < count; i++) { @@ -2437,121 +2514,114 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, return accept; } -//Used by LLFolderBridge as callback for directory recursion. -class LLRightClickInventoryFetchObserver : public LLInventoryFetchItemsObserver +void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer) { -public: - LLRightClickInventoryFetchObserver(const uuid_vec_t& ids) : - LLInventoryFetchItemsObserver(ids), - mCopyItems(false) - { }; - LLRightClickInventoryFetchObserver(const uuid_vec_t& ids, - const LLUUID& cat_id, - bool copy_items) : - LLInventoryFetchItemsObserver(ids), - mCatID(cat_id), - mCopyItems(copy_items) - { }; - virtual void done() - { - // we've downloaded all the items, so repaint the dialog - LLFolderBridge::staticFolderOptionsMenu(); - - gInventory.removeObserver(this); - delete this; - } - -protected: - LLUUID mCatID; - bool mCopyItems; - -}; - -//Used by LLFolderBridge as callback for directory recursion. -class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver -{ -public: - LLRightClickInventoryFetchDescendentsObserver(const uuid_vec_t& ids, - bool copy_items) : - LLInventoryFetchDescendentsObserver(ids), - mCopyItems(copy_items) - {} - ~LLRightClickInventoryFetchDescendentsObserver() {} - virtual void done(); -protected: - bool mCopyItems; -}; - -void LLRightClickInventoryFetchDescendentsObserver::done() -{ - // Avoid passing a NULL-ref as mCompleteFolders.front() down to - // gInventory.collectDescendents() + // Bail out immediately if no descendents if( mComplete.empty() ) { llwarns << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << llendl; - dec_busy_count(); - gInventory.removeObserver(this); - delete this; + if (clear_observer) + { + dec_busy_count(); + gInventory.removeObserver(this); + delete this; + } return; } - - // What we do here is get the complete information on the items in - // the library, and set up an observer that will wait for that to - // happen. - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - gInventory.collectDescendents(mComplete.front(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH); - S32 count = item_array.count(); -#if 0 // HACK/TODO: Why? - // This early causes a giant menu to get produced, and doesn't seem to be needed. - if(!count) - { - llwarns << "Nothing fetched in category " << mCompleteFolders.front() - << llendl; + + // Copy the list of complete fetched folders while "this" is still valid + uuid_vec_t completed_folder = mComplete; + + // Clean up, and remove this as an observer now since recursive calls + // could notify observers and throw us into an infinite loop. + if (clear_observer) + { dec_busy_count(); gInventory.removeObserver(this); delete this; - return; } -#endif - - uuid_vec_t ids; - for(S32 i = 0; i < count; ++i) + + for (uuid_vec_t::iterator current_folder = completed_folder.begin(); current_folder != completed_folder.end(); ++current_folder) { - ids.push_back(item_array.get(i)->getUUID()); - } + // Get the information on the fetched folder items and subfolders and fetch those + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(*current_folder, cat_array, item_array); - LLRightClickInventoryFetchObserver* outfit = new LLRightClickInventoryFetchObserver(ids, mComplete.front(), mCopyItems); + S32 item_count = item_array->count(); + S32 cat_count = cat_array->count(); + + // Move to next if current folder empty + if ((item_count == 0) && (cat_count == 0)) + { + continue; + } - // clean up, and remove this as an observer since the call to the - // outfit could notify observers and throw us into an infinite - // loop. - dec_busy_count(); - gInventory.removeObserver(this); - delete this; + uuid_vec_t ids; + LLRightClickInventoryFetchObserver* outfit = NULL; + LLRightClickInventoryFetchDescendentsObserver* categories = NULL; - // increment busy count and either tell the inventory to check & - // call done, or add this object to the inventory for observation. - inc_busy_count(); + // Fetch the items + if (item_count) + { + for (S32 i = 0; i < item_count; ++i) + { + ids.push_back(item_array->get(i)->getUUID()); + } + outfit = new LLRightClickInventoryFetchObserver(ids); + } + // Fetch the subfolders + if (cat_count) + { + for (S32 i = 0; i < cat_count; ++i) + { + ids.push_back(cat_array->get(i)->getUUID()); + } + categories = new LLRightClickInventoryFetchDescendentsObserver(ids); + } - // do the fetch - outfit->startFetch(); - outfit->done(); //Not interested in waiting and this will be right 99% of the time. -//Uncomment the following code for laggy Inventory UI. -/* if(outfit->isFinished()) - { - // everything is already here - call done. - outfit->done(); + // Perform the item fetch + if (outfit) + { + outfit->startFetch(); + outfit->execute(); // Not interested in waiting and this will be right 99% of the time. + delete outfit; + // Uncomment the following code for laggy Inventory UI. + /* + if (outfit->isFinished()) + { + // everything is already here - call done. + outfit->execute(); + delete outfit; + } + else + { + // it's all on its way - add an observer, and the inventory + // will call done for us when everything is here. + inc_busy_count(); + gInventory.addObserver(outfit); + } + */ + } + // Perform the subfolders fetch : this is where we truly recurse down the folder hierarchy + if (categories) + { + categories->startFetch(); + if (categories->isFinished()) + { + // everything is already here - call done. + categories->execute(); + delete categories; + } + else + { + // it's all on its way - add an observer, and the inventory + // will call done for us when everything is here. + inc_busy_count(); + gInventory.addObserver(categories); + } + } } - else - { - // it's all on it's way - add an observer, and the inventory - // will call done for us when everything is here. - gInventory.addObserver(outfit); - }*/ } @@ -2670,6 +2740,12 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) modifyOutfit(TRUE); return; } + else if ("cut" == action) + { + cutToClipboard(); + LLFolderView::removeCutItems(); + return; + } else if ("copy" == action) { copyToClipboard(); @@ -2881,7 +2957,7 @@ bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& re void LLFolderBridge::pasteFromClipboard() { LLInventoryModel* model = getInventoryModel(); - if(model && isClipboardPasteable()) + if (model && isClipboardPasteable()) { const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); @@ -2891,7 +2967,7 @@ void LLFolderBridge::pasteFromClipboard() const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); if (move_is_into_outbox) { @@ -2941,7 +3017,8 @@ void LLFolderBridge::pasteFromClipboard() const LLUUID& item_id = (*iter); LLInventoryItem *item = model->getItem(item_id); - if (item) + LLInventoryObject *obj = model->getObject(item_id); + if (obj) { if (move_is_into_current_outfit || move_is_into_outfit) { @@ -2950,29 +3027,56 @@ void LLFolderBridge::pasteFromClipboard() dropToOutfit(item, move_is_into_current_outfit); } } - else if(LLInventoryClipboard::instance().isCutMode()) + else if (LLClipboard::instance().isCutMode()) { - // move_inventory_item() is not enough, - //we have to update inventory locally too - LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item); - llassert(viitem); - if (viitem) + // Do a move to "paste" a "cut" + // move_inventory_item() is not enough, as we have to update inventory locally too + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); + llassert(vicat); + if (vicat) + { + changeCategoryParent(model, vicat, parent_id, FALSE); + } + } + else { - changeItemParent(model, viitem, parent_id, FALSE); + LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item); + llassert(viitem); + if (viitem) + { + changeItemParent(model, viitem, parent_id, FALSE); + } } } else { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - parent_id, - std::string(), - LLPointer<LLInventoryCallback>(NULL)); + // Do a "copy" to "paste" a regular copy clipboard + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); + llassert(vicat); + if (vicat) + { + copy_inventory_category(model, vicat, parent_id); + } + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + parent_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } } } } + // Change mode to paste for next paste + LLClipboard::instance().setCutMode(false); } } @@ -2997,7 +3101,7 @@ void LLFolderBridge::pasteLinkFromClipboard() const LLUUID parent_id(mUUID); LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) @@ -3035,6 +3139,8 @@ void LLFolderBridge::pasteLinkFromClipboard() LLPointer<LLInventoryCallback>(NULL)); } } + // Change mode to paste for next paste + LLClipboard::instance().setCutMode(false); } } @@ -3292,16 +3398,19 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) folders.push_back(category->getUUID()); sSelf = getHandle(); - LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders, FALSE); + LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); fetch->startFetch(); - inc_busy_count(); if (fetch->isFinished()) { + // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down + // This saves lots of time as buildContextMenu() is called a lot + delete fetch; buildContextMenuFolderOptions(flags); } else { // it's all on its way - add an observer, and the inventory will call done for us when everything is here. + inc_busy_count(); gInventory.addObserver(fetch); } } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 3b4f845f54..dc9e88d54d 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -106,8 +106,8 @@ public: virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch); virtual void move(LLFolderViewEventListener* new_parent_bridge) {} virtual BOOL isItemCopyable() const { return FALSE; } - virtual BOOL copyToClipboard() const { return FALSE; } - virtual void cutToClipboard(); + virtual BOOL copyToClipboard() const; + virtual BOOL cutToClipboard() const; virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; virtual void pasteFromClipboard() {} @@ -212,7 +212,6 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); virtual BOOL isItemCopyable() const; - virtual BOOL copyToClipboard() const; virtual BOOL hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } @@ -275,7 +274,6 @@ public: virtual BOOL isItemCopyable() const; virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; - virtual BOOL copyToClipboard() const; static void createWearable(LLFolderBridge* bridge, LLWearableType::EType type); diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 947f74315c..e859535d18 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -39,8 +39,11 @@ #include "llviewerfoldertype.h" // linden library includes +#include "llclipboard.h" #include "lltrans.h" +LLFastTimer::DeclareTimer FT_FILTER_CLIPBOARD("Filter Clipboard"); + LLInventoryFilter::FilterOps::FilterOps() : mFilterObjectTypes(0xffffffffffffffffULL), mFilterCategoryTypes(0xffffffffffffffffULL), @@ -88,11 +91,15 @@ LLInventoryFilter::~LLInventoryFilter() BOOL LLInventoryFilter::check(const LLFolderViewItem* item) { - // If it's a folder and we're showing all folders, return TRUE automatically. + // Clipboard cut items are *always* filtered so we need this value upfront + const LLFolderViewEventListener* listener = item->getListener(); + const BOOL passed_clipboard = (listener ? checkAgainstClipboard(listener->getUUID()) : TRUE); + + // If it's a folder and we're showing all folders, return automatically. const BOOL is_folder = (dynamic_cast<const LLFolderViewFolder*>(item) != NULL); if (is_folder && (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS)) { - return TRUE; + return passed_clipboard; } mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; @@ -103,6 +110,7 @@ BOOL LLInventoryFilter::check(const LLFolderViewItem* item) const BOOL passed = (passed_filtertype && passed_permissions && passed_filterlink && + passed_clipboard && (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); return passed; @@ -114,8 +122,10 @@ bool LLInventoryFilter::check(const LLInventoryItem* item) const bool passed_filtertype = checkAgainstFilterType(item); const bool passed_permissions = checkAgainstPermissions(item); + const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID()); const bool passed = (passed_filtertype && passed_permissions && + passed_clipboard && (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); return passed; @@ -145,10 +155,13 @@ bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { + // Always check against the clipboard + const BOOL passed_clipboard = checkAgainstClipboard(folder_id); + // we're showing all folders, overriding filter if (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS) { - return true; + return passed_clipboard; } if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY) @@ -163,7 +176,7 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const return false; } - return true; + return passed_clipboard; } BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) const @@ -255,7 +268,7 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con } } } - + return TRUE; } @@ -309,6 +322,31 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLInventoryItem* item) cons return true; } +// Items and folders that are on the clipboard or, recursively, in a folder which +// is on the clipboard must be filtered out if the clipboard is in the "cut" mode. +bool LLInventoryFilter::checkAgainstClipboard(const LLUUID& object_id) const +{ + if (LLClipboard::instance().isCutMode()) + { + LLFastTimer ft(FT_FILTER_CLIPBOARD); + LLUUID current_id = object_id; + LLInventoryObject *current_object = gInventory.getObject(object_id); + while (current_id.notNull() && current_object) + { + if (LLClipboard::instance().isOnClipboard(current_id)) + { + return false; + } + current_id = current_object->getParentUUID(); + if (current_id.notNull()) + { + current_object = gInventory.getObject(current_id); + } + } + } + return true; +} + BOOL LLInventoryFilter::checkAgainstPermissions(const LLFolderViewItem* item) const { const LLFolderViewEventListener* listener = item->getListener(); @@ -379,7 +417,7 @@ BOOL LLInventoryFilter::isNotDefault() const not_default |= (mFilterOps.mMinDate != mDefaultFilterOps.mMinDate); not_default |= (mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate); not_default |= (mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo); - + return not_default; } @@ -965,7 +1003,7 @@ void LLInventoryFilter::fromLLSD(LLSD& data) { if(data.has("filter_types")) { - setFilterObjectTypes((U32)data["filter_types"].asInteger()); + setFilterObjectTypes((U64)data["filter_types"].asInteger()); } if(data.has("min_date") && data.has("max_date")) diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 6be2acfaa3..1804637a04 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -124,6 +124,7 @@ public: BOOL checkAgainstPermissions(const LLFolderViewItem* item) const; bool checkAgainstPermissions(const LLInventoryItem* item) const; BOOL checkAgainstFilterLinks(const LLFolderViewItem* item) const; + bool checkAgainstClipboard(const LLUUID& object_id) const; std::string::size_type getStringMatchOffset() const; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index dd92188e9d..236c997ef6 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -54,7 +54,6 @@ #include "lliconctrl.h" #include "llimview.h" #include "llinventorybridge.h" -#include "llinventoryclipboard.h" #include "llinventorymodel.h" #include "llinventorypanel.h" #include "lllineeditor.h" @@ -161,6 +160,31 @@ void change_category_parent(LLInventoryModel* model, model->notifyObservers(); } +// Move the item to the trash. Works for folders and objects. +// Caution: This method assumes that the item is removable! +void remove_item(LLInventoryModel* model, const LLUUID& id) +{ + LLViewerInventoryItem* item = model->getItem(id); + if (!item) + return; + + if (item->getType() == LLAssetType::AT_CATEGORY) + { + // Call the general helper function to delete a folder + remove_category(model, id); + } + else + { + // Get the trash UUID + LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); + if (trash_id.notNull()) + { + // Finally, move the item to the trash + change_item_parent(model, item, trash_id, true); + } + } +} + void remove_category(LLInventoryModel* model, const LLUUID& cat_id) { if (!model || !get_is_category_removable(model, cat_id)) @@ -214,6 +238,49 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s model->notifyObservers(); } +void copy_inventory_category(LLInventoryModel* model, + LLViewerInventoryCategory* cat, + const LLUUID& parent_id, + const LLUUID& root_copy_id) +{ + // Create the initial folder + LLUUID new_cat_uuid = gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName()); + model->notifyObservers(); + + // We need to exclude the initial root of the copy to avoid recursively copying the copy, etc... + LLUUID root_id = (root_copy_id.isNull() ? new_cat_uuid : root_copy_id); + + // Get the content of the folder + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + + // Copy all the items + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + new_cat_uuid, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } + + // Copy all the folders + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory* category = *iter; + if (category->getUUID() != root_id) + { + copy_inventory_category(model, category, new_cat_uuid, root_id); + } + } +} + class LLInventoryCollectAllItems : public LLInventoryCollectFunctor { public: diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index ce2b89b22e..b3d9f4b966 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -62,6 +62,8 @@ void change_item_parent(LLInventoryModel* model, const LLUUID& new_parent_id, BOOL restamp); +void remove_item(LLInventoryModel* model, const LLUUID& id); + void change_category_parent(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& new_parent_id, @@ -71,6 +73,8 @@ void remove_category(LLInventoryModel* model, const LLUUID& cat_id); void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); +void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null); + // Generates a string containing the path to the item specified by item_id. void append_path(const LLUUID& id, std::string& path); diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index a71b699fdd..0eba8bd0f1 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -30,6 +30,7 @@ #include "llagent.h" #include "llagentwearables.h" #include "llappearancemgr.h" +#include "llclipboard.h" #include "llinventorypanel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" @@ -1110,50 +1111,82 @@ void LLInventoryModel::purgeDescendentsOf(const LLUUID& id) return; } LLPointer<LLViewerInventoryCategory> cat = getCategory(id); - if(cat.notNull()) - { - // do the cache accounting - llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName() - << llendl; - S32 descendents = cat->getDescendentCount(); - if(descendents > 0) - { - LLCategoryUpdate up(id, -descendents); - accountForUpdate(up); + if (cat.notNull()) + { + if (LLClipboard::instance().hasContents() && LLClipboard::instance().isCutMode()) + { + // Something on the clipboard is in "cut mode" and needs to be preserved + llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName() + << " iterate and purge non hidden items" << llendl; + cat_array_t* categories; + item_array_t* items; + // Get the list of direct descendants in tha categoy passed as argument + getDirectDescendentsOf(id, categories, items); + std::vector<LLUUID> list_uuids; + // Make a unique list with all the UUIDs of the direct descendants (items and categories are not treated differently) + // Note: we need to do that shallow copy as purging things will invalidate the categories or items lists + for (cat_array_t::const_iterator it = categories->begin(); it != categories->end(); ++it) + { + list_uuids.push_back((*it)->getUUID()); + } + for (item_array_t::const_iterator it = items->begin(); it != items->end(); ++it) + { + list_uuids.push_back((*it)->getUUID()); + } + // Iterate through the list and only purge the UUIDs that are not on the clipboard + for (std::vector<LLUUID>::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it) + { + if (!LLClipboard::instance().isOnClipboard(*it)) + { + purgeObject(*it); + } + } } + else + { + // Fast purge + // do the cache accounting + llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName() + << llendl; + S32 descendents = cat->getDescendentCount(); + if(descendents > 0) + { + LLCategoryUpdate up(id, -descendents); + accountForUpdate(up); + } - // we know that descendent count is 0, aide since the - // accounting may actually not do an update, we should force - // it here. - cat->setDescendentCount(0); + // we know that descendent count is 0, however since the + // accounting may actually not do an update, we should force + // it here. + cat->setDescendentCount(0); + + // send it upstream + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("PurgeInventoryDescendents"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("InventoryData"); + msg->addUUID("FolderID", id); + gAgent.sendReliableMessage(); - // send it upstream - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("PurgeInventoryDescendents"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("FolderID", id); - gAgent.sendReliableMessage(); - - // unceremoniously remove anything we have locally stored. - cat_array_t categories; - item_array_t items; - collectDescendents(id, - categories, - items, - INCLUDE_TRASH); - S32 count = items.count(); - S32 i; - for(i = 0; i < count; ++i) - { - deleteObject(items.get(i)->getUUID()); - } - count = categories.count(); - for(i = 0; i < count; ++i) - { - deleteObject(categories.get(i)->getUUID()); + // unceremoniously remove anything we have locally stored. + cat_array_t categories; + item_array_t items; + collectDescendents(id, + categories, + items, + INCLUDE_TRASH); + S32 count = items.count(); + for(S32 i = 0; i < count; ++i) + { + deleteObject(items.get(i)->getUUID()); + } + count = categories.count(); + for(S32 i = 0; i < count; ++i) + { + deleteObject(categories.get(i)->getUUID()); + } } } } diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index c47b74815f..71dd963f28 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -33,6 +33,7 @@ #include "llagentwearables.h" #include "llappearancemgr.h" #include "llavataractions.h" +#include "llclipboard.h" #include "llfloaterinventory.h" #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" @@ -248,6 +249,9 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) getFilter()->setFilterEmptySystemFolders(); } + // keep track of the clipboard state so that we avoid filtering too much + mClipboardState = LLClipboard::instance().getGeneration(); + // Initialize base class params. LLPanel::initFromParams(params); } @@ -278,6 +282,14 @@ void LLInventoryPanel::draw() { // Select the desired item (in case it wasn't loaded when the selection was requested) mFolderRoot->updateSelection(); + + // Nudge the filter if the clipboard state changed + if (mClipboardState != LLClipboard::instance().getGeneration()) + { + mClipboardState = LLClipboard::instance().getGeneration(); + getFilter()->setModified(LLClipboard::instance().isCutMode() ? LLInventoryFilter::FILTER_MORE_RESTRICTIVE : LLInventoryFilter::FILTER_LESS_RESTRICTIVE); + } + LLPanel::draw(); } diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 7d805f6862..6db59afb9b 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -222,6 +222,7 @@ public: private: std::string mSortOrderSetting; + int mClipboardState; //-------------------------------------------------------------------- // Hidden folders diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index c7454e85a9..68a3b6d1cd 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -1149,7 +1149,7 @@ Rules: - cut/rename/delete in any other accordions - paste - only in Favorites, Landmarks accordions 3. For Folders we can: perform any action in Landmarks accordion, except Received folder - 4. We can not paste folders from Clipboard (processed by LLFolderView::canPaste()) + 4. We can paste folders from Clipboard (processed by LLFolderView::canPaste()) 5. Check LLFolderView/Inventory Bridges rules */ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFolderViewItem* item) const @@ -1206,8 +1206,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold if ("cut" == command_name) { - // "Cut" disabled for folders. See EXT-8697. - can_be_modified = root_folder->canCut() && listenerp->getInventoryType() != LLInventoryType::IT_CATEGORY; + can_be_modified = root_folder->canCut(); } else if ("rename" == command_name) { diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index c3c62920d3..c11597f532 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -375,7 +375,7 @@ void LLPanelMainInventory::onClearSearch() if (mActivePanel) { mActivePanel->setFilterSubString(LLStringUtil::null); - mActivePanel->setFilterTypes(0xffffffff); + mActivePanel->setFilterTypes(0xffffffffffffffffULL); mActivePanel->setFilterLinks(LLInventoryFilter::FILTERLINK_INCLUDE_LINKS); } @@ -726,7 +726,7 @@ void LLFloaterInventoryFinder::updateElementsFromFilter() void LLFloaterInventoryFinder::draw() { LLMemType mt(LLMemType::MTYPE_INVENTORY_DRAW); - U32 filter = 0xffffffff; + U64 filter = 0xffffffffffffffffULL; BOOL filtered_by_all_types = TRUE; if (!getChild<LLUICtrl>("check_animation")->getValue()) diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index f3ecba7185..1ca24f3031 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -124,7 +124,7 @@ public: virtual void move(LLFolderViewEventListener* parent_listener); virtual BOOL isItemCopyable() const; virtual BOOL copyToClipboard() const; - virtual void cutToClipboard(); + virtual BOOL cutToClipboard() const; virtual BOOL isClipboardPasteable() const; virtual void pasteFromClipboard(); virtual void pasteLinkFromClipboard(); @@ -524,8 +524,9 @@ BOOL LLTaskInvFVBridge::copyToClipboard() const return FALSE; } -void LLTaskInvFVBridge::cutToClipboard() +BOOL LLTaskInvFVBridge::cutToClipboard() const { + return FALSE; } BOOL LLTaskInvFVBridge::isClipboardPasteable() const diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 1f1cccad85..c63d89fc98 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -358,7 +358,7 @@ void LLTeleportHistoryPanel::ContextMenu::onInfo() //static void LLTeleportHistoryPanel::ContextMenu::gotSLURLCallback(const std::string& slurl) { - gClipboard.copyFromString(utf8str_to_wstring(slurl)); + LLClipboard::instance().copyToClipboard(utf8str_to_wstring(slurl),0,slurl.size()); } void LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard() diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp index eb4c7572d4..280cc11179 100644 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -467,7 +467,7 @@ void LLPanelTopInfoBar::onContextMenuItemClicked(const LLSD::String& item) LLAgentUI::buildSLURL(slurl, false); LLUIString location_str(slurl.getSLURLString()); - gClipboard.copyFromString(location_str); + LLClipboard::instance().copyToClipboard(location_str,0,location_str.length()); } } diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index e2801c09bd..3b9934d4be 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -302,6 +302,6 @@ void LLPanelWearing::copyToClipboard() } } - gClipboard.copyFromString(utf8str_to_wstring(text)); + LLClipboard::instance().copyToClipboard(utf8str_to_wstring(text),0,text.size()); } // EOF diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp index eccb2cf2f1..81ad96f39e 100644 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -75,6 +75,7 @@ LLToolBarView::LLToolBarView(const LLToolBarView::Params& p) mDragStarted(false), mShowToolbars(true), mDragToolbarButton(NULL), + mDragItem(NULL), mToolbarsLoaded(false) { for (S32 i = 0; i < TOOLBAR_COUNT; i++) @@ -579,7 +580,6 @@ BOOL LLToolBarView::handleDragTool( S32 x, S32 y, const LLUUID& uuid, LLAssetTyp uuid_vec_t cargo_ids; types.push_back(DAD_WIDGET); cargo_ids.push_back(uuid); - gClipboard.setSourceObject(uuid,LLAssetType::AT_WIDGET); LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_VIEWER; LLUUID srcID; LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, src, srcID); @@ -662,6 +662,18 @@ void LLToolBarView::resetDragTool(LLToolBarButton* toolbarButton) gToolBarView->mDragToolbarButton = toolbarButton; } +// Provide a handle on a free standing inventory item containing references to the tool. +// This might be used by Drag and Drop to move around references to tool items. +LLInventoryObject* LLToolBarView::getDragItem() +{ + if (mDragToolbarButton) + { + LLUUID item_uuid = mDragToolbarButton->getCommandId().uuid(); + mDragItem = new LLInventoryObject (item_uuid, LLUUID::null, LLAssetType::AT_WIDGET, ""); + } + return mDragItem; +} + void LLToolBarView::setToolBarsVisible(bool visible) { mShowToolbars = visible; diff --git a/indra/newview/lltoolbarview.h b/indra/newview/lltoolbarview.h index be66bcae36..9c4194ebed 100644 --- a/indra/newview/lltoolbarview.h +++ b/indra/newview/lltoolbarview.h @@ -31,6 +31,7 @@ #include "lluictrl.h" #include "lltoolbar.h" #include "llcommandmanager.h" +#include "llinventory.h" class LLUICtrlFactory; @@ -106,6 +107,7 @@ public: static BOOL handleDragTool(S32 x, S32 y, const LLUUID& uuid, LLAssetType::EType type); static BOOL handleDropTool(void* cargo_data, S32 x, S32 y, LLToolBar* toolbar); static void resetDragTool(LLToolBarButton* toolbarButton); + LLInventoryObject* getDragItem(); bool isModified() const; @@ -129,6 +131,7 @@ private: bool mDragStarted; LLToolBarButton* mDragToolbarButton; + LLInventoryObject* mDragItem; bool mShowToolbars; }; diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index c7ab934f9e..4f4eef0f3d 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -48,6 +48,7 @@ #include "llpreviewnotecard.h" #include "llrootview.h" #include "llselectmgr.h" +#include "lltoolbarview.h" #include "lltoolmgr.h" #include "lltooltip.h" #include "lltrans.h" @@ -2528,7 +2529,7 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( } else if(mSource == SOURCE_VIEWER) { - item = (LLViewerInventoryItem*)gClipboard.getSourceObject(); + item = (LLViewerInventoryItem*)gToolBarView->getDragItem(); } if(item) return item; if(cat) return cat; diff --git a/indra/newview/llurllineeditorctrl.cpp b/indra/newview/llurllineeditorctrl.cpp index 56b5bbf942..cad5769042 100644 --- a/indra/newview/llurllineeditorctrl.cpp +++ b/indra/newview/llurllineeditorctrl.cpp @@ -89,5 +89,5 @@ void LLURLLineEditor::copyEscapedURLToClipboard() else // human-readable location text_to_copy = utf8str_to_wstring(unescaped_text); - gClipboard.copyFromString( text_to_copy ); + LLClipboard::instance().copyToClipboard(text_to_copy, 0, text_to_copy.size()); } diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index ef4a1bc061..b13bf5b508 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -453,6 +453,14 @@ layout="topleft" name="Copy Separator" /> <menu_item_call + label="Cut" + layout="topleft" + name="Cut"> + <menu_item_call.on_click + function="Inventory.DoToSelected" + parameter="cut" /> + </menu_item_call> + <menu_item_call label="Copy" layout="topleft" name="Copy"> |