diff options
Diffstat (limited to 'indra/newview/llviewertexteditor.cpp')
-rw-r--r-- | indra/newview/llviewertexteditor.cpp | 2766 |
1 files changed, 1383 insertions, 1383 deletions
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index bb8b5dc837..82d0caecb5 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -1,1383 +1,1383 @@ -/**
- * @file llviewertexteditor.cpp
- * @brief Text editor widget to let users enter a multi-line document.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llviewertexteditor.h"
-
-#include "llagent.h"
-#include "llaudioengine.h"
-#include "llavataractions.h"
-#include "llenvironment.h"
-#include "llfloaterreg.h"
-#include "llfloatersidepanelcontainer.h"
-#include "llfloaterworldmap.h"
-#include "llfocusmgr.h"
-#include "llinspecttexture.h"
-#include "llinventorybridge.h"
-#include "llinventorydefines.h"
-#include "llinventorymodel.h"
-#include "lllandmark.h"
-#include "lllandmarkactions.h"
-#include "lllandmarklist.h"
-#include "llmaterialeditor.h"
-#include "llmemorystream.h"
-#include "llmenugl.h"
-#include "llnotecard.h"
-#include "llnotificationsutil.h"
-#include "llpanelplaces.h"
-#include "llpreview.h"
-#include "llpreviewnotecard.h"
-#include "llpreviewtexture.h"
-#include "llscrollbar.h"
-#include "llscrollcontainer.h"
-#include "lltooldraganddrop.h"
-#include "lltooltip.h"
-#include "lltrans.h"
-#include "lluictrlfactory.h"
-#include "llviewerassettype.h"
-#include "llviewercontrol.h"
-#include "llviewerinventory.h"
-#include "llviewertexturelist.h"
-#include "llviewerwindow.h"
-
-static LLDefaultChildRegistry::Register<LLViewerTextEditor> r("text_editor");
-
-///-----------------------------------------------------------------------
-/// Class LLEmbeddedLandmarkCopied
-///-----------------------------------------------------------------------
-class LLEmbeddedLandmarkCopied: public LLInventoryCallback
-{
-public:
-
- LLEmbeddedLandmarkCopied(){}
- void fire(const LLUUID& inv_item)
- {
- showInfo(inv_item);
- }
- static void showInfo(const LLUUID& landmark_inv_id)
- {
- LLSD key;
- key["type"] = "landmark";
- key["id"] = landmark_inv_id;
- LLFloaterSidePanelContainer::showPanel("places", key);
- }
- static void processForeignLandmark(LLLandmark* landmark,
- const LLUUID& object_id, const LLUUID& notecard_inventory_id,
- LLPointer<LLInventoryItem> item_ptr)
- {
- LLVector3d global_pos;
- landmark->getGlobalPos(global_pos);
- LLViewerInventoryItem* agent_landmark =
- LLLandmarkActions::findLandmarkForGlobalPos(global_pos);
-
- if (agent_landmark)
- {
- showInfo(agent_landmark->getUUID());
- }
- else
- {
- if (item_ptr.isNull())
- {
- // check to prevent a crash. See EXT-8459.
- LL_WARNS() << "Passed handle contains a dead inventory item. Most likely notecard has been closed and embedded item was destroyed." << LL_ENDL;
- }
- else
- {
- LLInventoryItem* item = item_ptr.get();
- LLPointer<LLEmbeddedLandmarkCopied> cb = new LLEmbeddedLandmarkCopied();
- copy_inventory_from_notecard(get_folder_by_itemtype(item),
- object_id,
- notecard_inventory_id,
- item,
- gInventoryCallbacks.registerCB(cb));
- }
- }
- }
-};
-///----------------------------------------------------------------------------
-/// Class LLEmbeddedNotecardOpener
-///----------------------------------------------------------------------------
-class LLEmbeddedNotecardOpener : public LLInventoryCallback
-{
- LLViewerTextEditor* mTextEditor;
-
-public:
- LLEmbeddedNotecardOpener()
- : mTextEditor(NULL)
- {
- }
-
- void setEditor(LLViewerTextEditor* e) {mTextEditor = e;}
-
- // override
- void fire(const LLUUID& inv_item)
- {
- if(!mTextEditor)
- {
- // The parent text editor may have vanished by now.
- // In that case just quit.
- return;
- }
-
- LLInventoryItem* item = gInventory.getItem(inv_item);
- if(!item)
- {
- LL_WARNS() << "Item add reported, but not found in inventory!: " << inv_item << LL_ENDL;
- }
- else
- {
- if(!gSavedSettings.getBOOL("ShowNewInventory"))
- {
- LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
- }
- }
- }
-};
-
-//
-// class LLEmbeddedItemSegment
-//
-
-const S32 EMBEDDED_ITEM_LABEL_PADDING = 2;
-
-class LLEmbeddedItemSegment : public LLTextSegment
-{
-public:
- LLEmbeddedItemSegment(S32 pos, LLUIImagePtr image, LLPointer<LLInventoryItem> inv_item, LLTextEditor& editor)
- : LLTextSegment(pos, pos + 1),
- mImage(image),
- mLabel(utf8str_to_wstring(inv_item->getName())),
- mItem(inv_item),
- mEditor(editor)
- {
-
- mStyle = new LLStyle(LLStyle::Params().font(LLFontGL::getFontSansSerif()));
- mToolTip = inv_item->getName() + '\n' + inv_item->getDescription();
- }
-
- /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
- {
- if (num_chars == 0)
- {
- width = 0;
- height = 0;
- }
- else
- {
- width = EMBEDDED_ITEM_LABEL_PADDING + mImage->getWidth() + mStyle->getFont()->getWidthF32(mLabel.c_str());
- height = llmax(mImage->getHeight(), mStyle->getFont()->getLineHeight());
- }
- return false;
- }
-
- /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const
- {
- // always draw at beginning of line
- if (line_offset == 0)
- {
- return 1;
- }
- else
- {
- S32 width, height;
- getDimensions(mStart, 1, width, height);
- if (width > num_pixels)
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- }
- /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
- {
- LLRectf image_rect = draw_rect;
- image_rect.mRight = image_rect.mLeft + mImage->getWidth();
- image_rect.mTop = image_rect.mBottom + mImage->getHeight();
- mImage->draw(LLRect(image_rect.mLeft, image_rect.mTop, image_rect.mRight, image_rect.mBottom));
-
- LLColor4 color;
- if (mEditor.getReadOnly())
- {
- color = LLUIColorTable::instance().getColor("TextEmbeddedItemReadOnlyColor");
- }
- else
- {
- color = LLUIColorTable::instance().getColor("TextEmbeddedItemColor");
- }
-
- F32 right_x;
- mStyle->getFont()->render(mLabel, 0, image_rect.mRight + EMBEDDED_ITEM_LABEL_PADDING, draw_rect.mTop, color, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::UNDERLINE, LLFontGL::NO_SHADOW, mLabel.length(), S32_MAX, &right_x);
- return right_x;
- }
-
- /*virtual*/ bool canEdit() const { return false; }
-
-
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask)
- {
- LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND);
- return true;
- }
- virtual bool handleToolTip(S32 x, S32 y, MASK mask )
- {
- if (mItem->getThumbnailUUID().notNull())
- {
- LLSD params;
- params["inv_type"] = mItem->getInventoryType();
- params["thumbnail_id"] = mItem->getThumbnailUUID();
- params["asset_id"] = mItem->getAssetUUID();
-
- LLToolTipMgr::instance().show(LLToolTip::Params()
- .message(mToolTip)
- .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1))
- .create_params(params));
-
- return true;
- }
-
- if (!mToolTip.empty())
- {
- LLToolTipMgr::instance().show(mToolTip);
- return true;
- }
- return false;
- }
-
- /*virtual*/ LLStyleConstSP getStyle() const { return mStyle; }
-
-private:
- LLUIImagePtr mImage;
- LLWString mLabel;
- LLStyleSP mStyle;
- std::string mToolTip;
- LLPointer<LLInventoryItem> mItem;
- LLTextEditor& mEditor;
-};
-
-
-
-////////////////////////////////////////////////////////////
-// LLEmbeddedItems
-//
-// Embedded items are stored as:
-// * A global map of llwchar to LLInventoryItem
-// ** This is unique for each item embedded in any notecard
-// to support copy/paste across notecards
-// * A per-notecard set of embeded llwchars for easy removal
-// from the global list
-// * A per-notecard vector of embedded lwchars for mapping from
-// old style 0x80 + item format notechards
-
-class LLEmbeddedItems
-{
-public:
- LLEmbeddedItems(const LLViewerTextEditor* editor);
- ~LLEmbeddedItems();
- void clear();
-
- // return true if there are no embedded items.
- bool empty();
-
- bool insertEmbeddedItem(LLInventoryItem* item, llwchar* value, bool is_new);
- bool removeEmbeddedItem( llwchar ext_char );
-
- bool hasEmbeddedItem(llwchar ext_char); // returns true if /this/ editor has an entry for this item
- LLUIImagePtr getItemImage(llwchar ext_char) const;
-
- void getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items );
- void addItems(const std::vector<LLPointer<LLInventoryItem> >& items);
-
- llwchar getEmbeddedCharFromIndex(S32 index);
-
- void removeUnusedChars();
- void copyUsedCharsToIndexed();
- S32 getIndexFromEmbeddedChar(llwchar wch);
-
- void markSaved();
-
- static LLPointer<LLInventoryItem> getEmbeddedItemPtr(llwchar ext_char); // returns pointer to item from static list
- static bool getEmbeddedItemSaved(llwchar ext_char); // returns whether item from static list is saved
-
-private:
-
- struct embedded_info_t
- {
- LLPointer<LLInventoryItem> mItemPtr;
- bool mSaved;
- };
- typedef std::map<llwchar, embedded_info_t > item_map_t;
- static item_map_t sEntries;
- static std::stack<llwchar> sFreeEntries;
-
- std::set<llwchar> mEmbeddedUsedChars; // list of used llwchars
- std::vector<llwchar> mEmbeddedIndexedChars; // index -> wchar for 0x80 + index format
- const LLViewerTextEditor* mEditor;
-};
-
-//statics
-LLEmbeddedItems::item_map_t LLEmbeddedItems::sEntries;
-std::stack<llwchar> LLEmbeddedItems::sFreeEntries;
-
-LLEmbeddedItems::LLEmbeddedItems(const LLViewerTextEditor* editor)
- : mEditor(editor)
-{
-}
-
-LLEmbeddedItems::~LLEmbeddedItems()
-{
- clear();
-}
-
-void LLEmbeddedItems::clear()
-{
- // Remove entries for this editor from static list
- for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
- iter != mEmbeddedUsedChars.end();)
- {
- std::set<llwchar>::iterator nextiter = iter++;
- removeEmbeddedItem(*nextiter);
- }
- mEmbeddedUsedChars.clear();
- mEmbeddedIndexedChars.clear();
-}
-
-bool LLEmbeddedItems::empty()
-{
- removeUnusedChars();
- return mEmbeddedUsedChars.empty();
-}
-
-// Inserts a new unique entry
-bool LLEmbeddedItems::insertEmbeddedItem( LLInventoryItem* item, llwchar* ext_char, bool is_new)
-{
- // Now insert a new one
- llwchar wc_emb;
- if (!sFreeEntries.empty())
- {
- wc_emb = sFreeEntries.top();
- sFreeEntries.pop();
- }
- else if (sEntries.empty())
- {
- wc_emb = LLTextEditor::FIRST_EMBEDDED_CHAR;
- }
- else
- {
- item_map_t::iterator last = sEntries.end();
- --last;
- wc_emb = last->first;
- if (wc_emb >= LLTextEditor::LAST_EMBEDDED_CHAR)
- {
- return false;
- }
- ++wc_emb;
- }
-
- sEntries[wc_emb].mItemPtr = item;
- sEntries[wc_emb].mSaved = !is_new;
- *ext_char = wc_emb;
- mEmbeddedUsedChars.insert(wc_emb);
- return true;
-}
-
-// Removes an entry (all entries are unique)
-bool LLEmbeddedItems::removeEmbeddedItem( llwchar ext_char )
-{
- mEmbeddedUsedChars.erase(ext_char);
- item_map_t::iterator iter = sEntries.find(ext_char);
- if (iter != sEntries.end())
- {
- sEntries.erase(ext_char);
- sFreeEntries.push(ext_char);
- return true;
- }
- return false;
-}
-
-// static
-LLPointer<LLInventoryItem> LLEmbeddedItems::getEmbeddedItemPtr(llwchar ext_char)
-{
- if( ext_char >= LLTextEditor::FIRST_EMBEDDED_CHAR && ext_char <= LLTextEditor::LAST_EMBEDDED_CHAR )
- {
- item_map_t::iterator iter = sEntries.find(ext_char);
- if (iter != sEntries.end())
- {
- return iter->second.mItemPtr;
- }
- }
- return NULL;
-}
-
-// static
-bool LLEmbeddedItems::getEmbeddedItemSaved(llwchar ext_char)
-{
- if( ext_char >= LLTextEditor::FIRST_EMBEDDED_CHAR && ext_char <= LLTextEditor::LAST_EMBEDDED_CHAR )
- {
- item_map_t::iterator iter = sEntries.find(ext_char);
- if (iter != sEntries.end())
- {
- return iter->second.mSaved;
- }
- }
- return false;
-}
-
-llwchar LLEmbeddedItems::getEmbeddedCharFromIndex(S32 index)
-{
- if (index >= (S32)mEmbeddedIndexedChars.size())
- {
- LL_WARNS() << "No item for embedded char " << index << " using LL_UNKNOWN_CHAR" << LL_ENDL;
- return LL_UNKNOWN_CHAR;
- }
- return mEmbeddedIndexedChars[index];
-}
-
-void LLEmbeddedItems::removeUnusedChars()
-{
- std::set<llwchar> used = mEmbeddedUsedChars;
- const LLWString& wtext = mEditor->getWText();
- for (S32 i=0; i<(S32)wtext.size(); i++)
- {
- llwchar wc = wtext[i];
- if( wc >= LLTextEditor::FIRST_EMBEDDED_CHAR && wc <= LLTextEditor::LAST_EMBEDDED_CHAR )
- {
- used.erase(wc);
- }
- }
- // Remove chars not actually used
- for (std::set<llwchar>::iterator iter = used.begin();
- iter != used.end(); ++iter)
- {
- removeEmbeddedItem(*iter);
- }
-}
-
-void LLEmbeddedItems::copyUsedCharsToIndexed()
-{
- // Prune unused items
- removeUnusedChars();
-
- // Copy all used llwchars to mEmbeddedIndexedChars
- mEmbeddedIndexedChars.clear();
- for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
- iter != mEmbeddedUsedChars.end(); ++iter)
- {
- mEmbeddedIndexedChars.push_back(*iter);
- }
-}
-
-S32 LLEmbeddedItems::getIndexFromEmbeddedChar(llwchar wch)
-{
- S32 idx = 0;
- for (std::vector<llwchar>::iterator iter = mEmbeddedIndexedChars.begin();
- iter != mEmbeddedIndexedChars.end(); ++iter)
- {
- if (wch == *iter)
- break;
- ++idx;
- }
- if (idx < (S32)mEmbeddedIndexedChars.size())
- {
- return idx;
- }
- else
- {
- LL_WARNS() << "Embedded char " << wch << " not found, using 0" << LL_ENDL;
- return 0;
- }
-}
-
-bool LLEmbeddedItems::hasEmbeddedItem(llwchar ext_char)
-{
- std::set<llwchar>::iterator iter = mEmbeddedUsedChars.find(ext_char);
- if (iter != mEmbeddedUsedChars.end())
- {
- return true;
- }
- return false;
-}
-
-
-LLUIImagePtr LLEmbeddedItems::getItemImage(llwchar ext_char) const
-{
- LLInventoryItem* item = getEmbeddedItemPtr(ext_char);
- if (item)
- {
- const char* img_name = "";
- switch( item->getType() )
- {
- case LLAssetType::AT_TEXTURE:
- if(item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)
- {
- img_name = "Inv_Snapshot";
- }
- else
- {
- img_name = "Inv_Texture";
- }
-
- break;
- case LLAssetType::AT_SOUND: img_name = "Inv_Sound"; break;
- case LLAssetType::AT_CLOTHING: img_name = "Inv_Clothing"; break;
- case LLAssetType::AT_OBJECT:
- img_name = LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS & item->getFlags() ?
- "Inv_Object_Multi" : "Inv_Object";
- break;
- case LLAssetType::AT_CALLINGCARD: img_name = "Inv_CallingCard"; break;
- case LLAssetType::AT_LANDMARK: img_name = "Inv_Landmark"; break;
- case LLAssetType::AT_NOTECARD: img_name = "Inv_Notecard"; break;
- case LLAssetType::AT_LSL_TEXT: img_name = "Inv_Script"; break;
- case LLAssetType::AT_BODYPART: img_name = "Inv_Skin"; break;
- case LLAssetType::AT_ANIMATION: img_name = "Inv_Animation"; break;
- case LLAssetType::AT_GESTURE: img_name = "Inv_Gesture"; break;
- case LLAssetType::AT_MESH: img_name = "Inv_Mesh"; break;
- case LLAssetType::AT_SETTINGS: img_name = "Inv_Settings"; break;
- case LLAssetType::AT_MATERIAL: img_name = "Inv_Material"; break;
- default: img_name = "Inv_Invalid"; break; // use the Inv_Invalid icon for undefined object types (see MAINT-3981)
-
- }
-
- return LLUI::getUIImage(img_name);
- }
- return LLUIImagePtr();
-}
-
-
-void LLEmbeddedItems::addItems(const std::vector<LLPointer<LLInventoryItem> >& items)
-{
- for (std::vector<LLPointer<LLInventoryItem> >::const_iterator iter = items.begin();
- iter != items.end(); ++iter)
- {
- LLInventoryItem* item = *iter;
- if (item)
- {
- llwchar wc;
- if (!insertEmbeddedItem( item, &wc, false ))
- {
- break;
- }
- mEmbeddedIndexedChars.push_back(wc);
- }
- }
-}
-
-void LLEmbeddedItems::getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items )
-{
- for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
- {
- llwchar wc = *iter;
- LLPointer<LLInventoryItem> item = getEmbeddedItemPtr(wc);
- if (item)
- {
- items.push_back(item);
- }
- }
-}
-
-void LLEmbeddedItems::markSaved()
-{
- for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
- {
- llwchar wc = *iter;
- sEntries[wc].mSaved = true;
- }
-}
-
-///////////////////////////////////////////////////////////////////
-
-class LLViewerTextEditor::TextCmdInsertEmbeddedItem : public LLTextBase::TextCmd
-{
-public:
- TextCmdInsertEmbeddedItem( S32 pos, LLInventoryItem* item )
- : TextCmd(pos, false),
- mExtCharValue(0)
- {
- mItem = item;
- }
-
- virtual bool execute( LLTextBase* editor, S32* delta )
- {
- LLViewerTextEditor* viewer_editor = (LLViewerTextEditor*)editor;
- // Take this opportunity to remove any unused embedded items from this editor
- viewer_editor->mEmbeddedItemList->removeUnusedChars();
- if(viewer_editor->mEmbeddedItemList->insertEmbeddedItem( mItem, &mExtCharValue, true ) )
- {
- LLWString ws;
- ws.assign(1, mExtCharValue);
- *delta = insert(editor, getPosition(), ws );
- return (*delta != 0);
- }
- return false;
- }
-
- virtual S32 undo( LLTextBase* editor )
- {
- remove(editor, getPosition(), 1);
- return getPosition();
- }
-
- virtual S32 redo( LLTextBase* editor )
- {
- LLWString ws;
- ws += mExtCharValue;
- insert(editor, getPosition(), ws );
- return getPosition() + 1;
- }
- virtual bool hasExtCharValue( llwchar value ) const
- {
- return (value == mExtCharValue);
- }
-
-private:
- LLPointer<LLInventoryItem> mItem;
- llwchar mExtCharValue;
-};
-
-struct LLNotecardCopyInfo
-{
- LLNotecardCopyInfo(LLViewerTextEditor *ed, LLInventoryItem *item)
- : mTextEd(ed)
- {
- mItem = item;
- }
-
- LLViewerTextEditor* mTextEd;
- // need to make this be a copy (not a * here) because it isn't stable.
- // I wish we had passed LLPointers all the way down, but we didn't
- LLPointer<LLInventoryItem> mItem;
-};
-
-//----------------------------------------------------------------------------
-
-//
-// Member functions
-//
-LLViewerTextEditor::LLViewerTextEditor(const LLViewerTextEditor::Params& p)
-: LLTextEditor(p),
- mDragItemChar(0),
- mDragItemSaved(false),
- mInventoryCallback(new LLEmbeddedNotecardOpener)
-{
- mEmbeddedItemList = new LLEmbeddedItems(this);
- mInventoryCallback->setEditor(this);
-}
-
-LLViewerTextEditor::~LLViewerTextEditor()
-{
- delete mEmbeddedItemList;
-
-
- // The inventory callback may still be in use by gInventoryCallbackManager...
- // so set its reference to this to null.
- mInventoryCallback->setEditor(NULL);
-}
-
-///////////////////////////////////////////////////////////////////
-// virtual
-void LLViewerTextEditor::makePristine()
-{
- mEmbeddedItemList->markSaved();
- LLTextEditor::makePristine();
-}
-
-void LLViewerTextEditor::onVisibilityChange( bool new_visibility )
-{
- LLUICtrl::onVisibilityChange(new_visibility);
-}
-
-bool LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // Let scrollbar have first dibs
- handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
-
- if( !handled)
- {
- if( allowsEmbeddedItems() )
- {
- setCursorAtLocalPos( x, y, false );
- llwchar wc = 0;
- if (mCursorPos < getLength())
- {
- wc = getWText()[mCursorPos];
- }
- LLPointer<LLInventoryItem> item_at_pos = LLEmbeddedItems::getEmbeddedItemPtr(wc);
- if (item_at_pos)
- {
- mDragItem = item_at_pos;
- mDragItemChar = wc;
- mDragItemSaved = LLEmbeddedItems::getEmbeddedItemSaved(wc);
- gFocusMgr.setMouseCapture( this );
- mMouseDownX = x;
- mMouseDownY = y;
- S32 screen_x;
- S32 screen_y;
- localPointToScreen(x, y, &screen_x, &screen_y );
- LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y );
-
- if (hasTabStop())
- {
- setFocus( true );
- }
-
- handled = true;
- }
- else
- {
- mDragItem = NULL;
- }
- }
-
- if (!handled)
- {
- handled = LLTextEditor::handleMouseDown(x, y, mask);
- }
- }
-
- return handled;
-}
-
-
-bool LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask)
-{
- bool handled = LLTextEditor::handleHover(x, y, mask);
-
- if(hasMouseCapture() && mDragItem)
- {
- S32 screen_x;
- S32 screen_y;
- localPointToScreen(x, y, &screen_x, &screen_y );
-
- mScroller->autoScroll(x, y);
-
- if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) )
- {
- LLToolDragAndDrop::getInstance()->beginDrag(
- LLViewerAssetType::lookupDragAndDropType( mDragItem->getType() ),
- mDragItem->getUUID(),
- LLToolDragAndDrop::SOURCE_NOTECARD,
- mPreviewID, mObjectID);
- return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask );
- }
- getWindow()->setCursor(UI_CURSOR_HAND);
- handled = true;
- }
-
- return handled;
-}
-
-
-bool LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- if( hasMouseCapture() )
- {
- if (mDragItem)
- {
- // mouse down was on an item
- S32 dx = x - mMouseDownX;
- S32 dy = y - mMouseDownY;
- if (-2 < dx && dx < 2 && -2 < dy && dy < 2)
- {
- if(mDragItemSaved)
- {
- openEmbeddedItem(mDragItem, mDragItemChar);
- }
- else
- {
- showUnsavedAlertDialog(mDragItem);
- }
- }
- }
- mDragItem = NULL;
- }
-
- handled = LLTextEditor::handleMouseUp(x,y,mask);
-
- return handled;
-}
-
-bool LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // let scrollbar have first dibs
- handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
-
- if( !handled)
- {
- if( allowsEmbeddedItems() )
- {
- S32 doc_index = getDocIndexFromLocalCoord(x, y, false);
- llwchar doc_char = getWText()[doc_index];
- if (mEmbeddedItemList->hasEmbeddedItem(doc_char))
- {
- if( openEmbeddedItemAtPos( doc_index ))
- {
- deselect();
- setFocus( false );
- return true;
- }
- }
- }
- handled = LLTextEditor::handleDoubleClick(x, y, mask);
- }
- return handled;
-}
-
-
-// virtual
-bool LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
- bool drop, EDragAndDropType cargo_type, void *cargo_data,
- EAcceptance *accept,
- std::string& tooltip_msg)
-{
- bool handled = false;
-
- LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
- if (LLToolDragAndDrop::SOURCE_NOTECARD == source)
- {
- // We currently do not handle dragging items from one notecard to another
- // since items in a notecard must be in Inventory to be verified. See DEV-2891.
- return false;
- }
-
- if (getEnabled() && acceptsTextInput())
- {
- bool supported = false;
- switch( cargo_type )
- {
- case DAD_SETTINGS:
- {
- supported = LLEnvironment::instance().isExtendedEnvironmentEnabled();
- if (!supported && tooltip_msg.empty())
- {
- tooltip_msg.assign(LLTrans::getString("TooltipNotecardNotAllowedTypeDrop"));
- }
- break;
- }
- case DAD_CALLINGCARD:
- case DAD_TEXTURE:
- case DAD_SOUND:
- case DAD_LANDMARK:
- case DAD_SCRIPT:
- case DAD_CLOTHING:
- case DAD_OBJECT:
- case DAD_NOTECARD:
- case DAD_BODYPART:
- case DAD_ANIMATION:
- case DAD_GESTURE:
- case DAD_MESH:
- case DAD_MATERIAL:
- {
- supported = true;
- break;
- }
-
- default:
- supported = false;
- break;
- }
-
- LLInventoryItem *item = (LLInventoryItem *)cargo_data;
- if (item && allowsEmbeddedItems() && supported)
- {
- U32 mask_next = item->getPermissions().getMaskNextOwner();
- if((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
- {
- if( drop )
- {
- deselect();
- S32 old_cursor = mCursorPos;
- setCursorAtLocalPos( x, y, true );
- S32 insert_pos = mCursorPos;
- setCursorPos(old_cursor);
- bool inserted = insertEmbeddedItem( insert_pos, item );
- if( inserted && (old_cursor > mCursorPos) )
- {
- setCursorPos(mCursorPos + 1);
- }
-
- needsReflow();
- }
- *accept = ACCEPT_YES_COPY_MULTI;
- }
- else
- {
- *accept = ACCEPT_NO;
- if (tooltip_msg.empty())
- {
- tooltip_msg.assign(LLTrans::getString("TooltipNotecardOwnerRestrictedDrop"));
- }
- }
- }
- else
- {
- *accept = ACCEPT_NO;
- }
- }
- else
- {
- // Not enabled
- *accept = ACCEPT_NO;
- }
-
- handled = true;
- LL_DEBUGS("UserInput") << "dragAndDrop handled by LLViewerTextEditor " << getName() << LL_ENDL;
-
- return handled;
-}
-
-void LLViewerTextEditor::setASCIIEmbeddedText(const std::string& instr)
-{
- LLWString wtext;
- const U8* buffer = (U8*)(instr.c_str());
- while (*buffer)
- {
- llwchar wch;
- U8 c = *buffer++;
- if (c >= 0x80)
- {
- S32 index = (S32)(c - 0x80);
- wch = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
- }
- else
- {
- wch = (llwchar)c;
- }
- wtext.push_back(wch);
- }
- setWText(wtext);
-}
-
-void LLViewerTextEditor::setEmbeddedText(const std::string& instr)
-{
- LLWString wtext = utf8str_to_wstring(instr);
- for (S32 i=0; i<(S32)wtext.size(); i++)
- {
- llwchar wch = wtext[i];
- if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
- {
- S32 index = wch - FIRST_EMBEDDED_CHAR;
- wtext[i] = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
- }
- }
- setWText(wtext);
-}
-
-std::string LLViewerTextEditor::getEmbeddedText()
-{
-#if 1
- // New version (Version 2)
- mEmbeddedItemList->copyUsedCharsToIndexed();
- LLWString outtextw;
- for (S32 i=0; i<(S32)getWText().size(); i++)
- {
- llwchar wch = getWText()[i];
- if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
- {
- S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
- wch = FIRST_EMBEDDED_CHAR + index;
- }
- outtextw.push_back(wch);
- }
- std::string outtext = wstring_to_utf8str(outtextw);
- return outtext;
-#else
- // Old version (Version 1)
- mEmbeddedItemList->copyUsedCharsToIndexed();
- std::string outtext;
- for (S32 i=0; i<(S32)mWText.size(); i++)
- {
- llwchar wch = mWText[i];
- if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
- {
- S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
- wch = 0x80 | index % 128;
- }
- else if (wch >= 0x80)
- {
- wch = LL_UNKNOWN_CHAR;
- }
- outtext.push_back((U8)wch);
- }
- return outtext;
-#endif
-}
-
-std::string LLViewerTextEditor::appendTime(bool prepend_newline)
-{
- time_t utc_time;
- utc_time = time_corrected();
- std::string timeStr ="[["+ LLTrans::getString("TimeHour")+"]:["
- +LLTrans::getString("TimeMin")+"]] ";
-
- LLSD substitution;
-
- substitution["datetime"] = (S32) utc_time;
- LLStringUtil::format (timeStr, substitution);
- appendText(timeStr, prepend_newline, LLStyle::Params().color(LLColor4::grey));
- blockUndo();
-
- return timeStr;
-}
-
-//----------------------------------------------------------------------------
-//----------------------------------------------------------------------------
-
-llwchar LLViewerTextEditor::pasteEmbeddedItem(llwchar ext_char)
-{
- if (mEmbeddedItemList->hasEmbeddedItem(ext_char))
- {
- return ext_char; // already exists in my list
- }
- LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItemPtr(ext_char);
- if (item)
- {
- // Add item to my list and return new llwchar associated with it
- llwchar new_wc;
- if (mEmbeddedItemList->insertEmbeddedItem( item, &new_wc, true ))
- {
- return new_wc;
- }
- }
- return LL_UNKNOWN_CHAR; // item not found or list full
-}
-
-void LLViewerTextEditor::onValueChange(S32 start, S32 end)
-{
- updateSegments();
- updateLinkSegments();
- findEmbeddedItemSegments(start, end);
-}
-
-void LLViewerTextEditor::findEmbeddedItemSegments(S32 start, S32 end)
-{
- LLWString text = getWText();
-
- // Start with i just after the first embedded item
- for(S32 idx = start; idx < end; idx++ )
- {
- llwchar embedded_char = text[idx];
- if( embedded_char >= FIRST_EMBEDDED_CHAR
- && embedded_char <= LAST_EMBEDDED_CHAR
- && mEmbeddedItemList->hasEmbeddedItem(embedded_char) )
- {
- LLInventoryItem* itemp = mEmbeddedItemList->getEmbeddedItemPtr(embedded_char);
- LLUIImagePtr image = mEmbeddedItemList->getItemImage(embedded_char);
- insertSegment(new LLEmbeddedItemSegment(idx, image, itemp, *this));
- }
- }
-}
-
-bool LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos)
-{
- if( pos < getLength())
- {
- llwchar wc = getWText()[pos];
- LLPointer<LLInventoryItem> item = LLEmbeddedItems::getEmbeddedItemPtr( wc );
- if( item )
- {
- bool saved = LLEmbeddedItems::getEmbeddedItemSaved( wc );
- if (saved)
- {
- return openEmbeddedItem(item, wc);
- }
- else
- {
- showUnsavedAlertDialog(item);
- }
- }
- }
- return false;
-}
-
-
-bool LLViewerTextEditor::openEmbeddedItem(LLPointer<LLInventoryItem> item, llwchar wc)
-{
-
- switch( item->getType() )
- {
- case LLAssetType::AT_TEXTURE:
- openEmbeddedTexture( item, wc );
- return true;
-
- case LLAssetType::AT_SOUND:
- openEmbeddedSound( item, wc );
- return true;
-
- case LLAssetType::AT_LANDMARK:
- openEmbeddedLandmark( item, wc );
- return true;
-
- case LLAssetType::AT_CALLINGCARD:
- openEmbeddedCallingcard( item, wc );
- return true;
- case LLAssetType::AT_SETTINGS:
- openEmbeddedSetting(item, wc);
- return true;
- case LLAssetType::AT_MATERIAL:
- openEmbeddedGLTFMaterial(item, wc);
- return true;
- case LLAssetType::AT_NOTECARD:
- case LLAssetType::AT_LSL_TEXT:
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_OBJECT:
- case LLAssetType::AT_BODYPART:
- case LLAssetType::AT_ANIMATION:
- case LLAssetType::AT_GESTURE:
- showCopyToInvDialog( item, wc );
- return true;
- default:
- return false;
- }
-
-}
-
-
-void LLViewerTextEditor::openEmbeddedTexture( LLInventoryItem* item, llwchar wc )
-{
- // *NOTE: Just for embedded Texture , we should use getAssetUUID(),
- // not getUUID(), because LLPreviewTexture pass in AssetUUID into
- // LLPreview constructor ItemUUID parameter.
- if (!item)
- return;
- LLPreviewTexture* preview = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item->getAssetUUID()), TAKE_FOCUS_YES);
- if (preview)
- {
- preview->setAuxItem( item );
- preview->setNotecardInfo(mNotecardInventoryID, mObjectID);
- if (preview->hasString("Title"))
- {
- LLStringUtil::format_map_t args;
- args["[NAME]"] = item->getName();
- LLUIString title = preview->getString("Title", args);
- preview->setTitle(title.getString());
- }
- preview->getChild<LLUICtrl>("desc")->setValue(item->getDescription());
- }
-}
-
-void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item, llwchar wc )
-{
- // Play sound locally
- LLVector3d lpos_global = gAgent.getPositionGlobal();
- const F32 SOUND_GAIN = 1.0f;
- if(gAudiop)
- {
- gAudiop->triggerSound(item->getAssetUUID(), gAgentID, SOUND_GAIN, LLAudioEngine::AUDIO_TYPE_UI, lpos_global);
- }
- showCopyToInvDialog( item, wc );
-}
-
-
-void LLViewerTextEditor::openEmbeddedLandmark( LLPointer<LLInventoryItem> item_ptr, llwchar wc )
-{
- if (item_ptr.isNull())
- return;
-
- LLLandmark* landmark = gLandmarkList.getAsset(item_ptr->getAssetUUID(),
- boost::bind(&LLEmbeddedLandmarkCopied::processForeignLandmark, _1, mObjectID, mNotecardInventoryID, item_ptr));
- if (landmark)
- {
- LLEmbeddedLandmarkCopied::processForeignLandmark(landmark, mObjectID,
- mNotecardInventoryID, item_ptr);
- }
-}
-
-void LLViewerTextEditor::openEmbeddedCallingcard( LLInventoryItem* item, llwchar wc )
-{
- if (item && !item->getDescription().empty())
- {
- LLAvatarActions::showProfile(LLUUID(item->getDescription()));
- }
- else if (item && !item->getCreatorUUID().isNull())
- {
- LLAvatarActions::showProfile(item->getCreatorUUID());
- }
-}
-
-void LLViewerTextEditor::openEmbeddedSetting(LLInventoryItem* item, llwchar wc)
-{
- if (LLEnvironment::instance().isInventoryEnabled())
- {
- showCopyToInvDialog(item, wc);
- }
- else
- {
- LLNotificationsUtil::add("NoEnvironmentSettings");
- }
-}
-
-void LLViewerTextEditor::openEmbeddedGLTFMaterial(LLInventoryItem* item, llwchar wc)
-{
- if (!item)
- {
- return;
- }
-
- LLSD floater_key;
- floater_key["objectid"] = mObjectID;
- floater_key["notecardid"] = mNotecardInventoryID;
- LLMaterialEditor* preview = LLFloaterReg::getTypedInstance<LLMaterialEditor>("material_editor", floater_key);
- if (preview)
- {
- preview->setAuxItem(item);
- preview->setNotecardInfo(mNotecardInventoryID, mObjectID);
- preview->openFloater(floater_key);
- preview->setFocus(true);
- }
-}
-
-void LLViewerTextEditor::showUnsavedAlertDialog( LLInventoryItem* item )
-{
- LLSD payload;
- payload["item_id"] = item->getUUID();
- payload["notecard_id"] = mNotecardInventoryID;
- LLNotificationsUtil::add( "ConfirmNotecardSave", LLSD(), payload, LLViewerTextEditor::onNotecardDialog);
-}
-
-// static
-bool LLViewerTextEditor::onNotecardDialog(const LLSD& notification, const LLSD& response )
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if( option == 0 )
- {
- LLPreviewNotecard* preview = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", notification["payload"]["notecard_id"]);;
- if (preview)
- {
- preview->saveItem();
- }
- }
- return false;
-}
-
-
-
-void LLViewerTextEditor::showCopyToInvDialog( LLInventoryItem* item, llwchar wc )
-{
- LLSD payload;
- LLUUID item_id = item->getUUID();
- payload["item_id"] = item_id;
- payload["item_wc"] = LLSD::Integer(wc);
- LLNotificationsUtil::add( "ConfirmItemCopy", LLSD(), payload,
- boost::bind(&LLViewerTextEditor::onCopyToInvDialog, this, _1, _2));
-}
-
-bool LLViewerTextEditor::onCopyToInvDialog(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if( 0 == option )
- {
- llwchar wc = llwchar(notification["payload"]["item_wc"].asInteger());
- LLInventoryItem* itemp = LLEmbeddedItems::getEmbeddedItemPtr(wc);
- if (itemp)
- copyInventory(itemp);
- }
- return false;
-}
-
-
-
-// Returns change in number of characters in mWText
-S32 LLViewerTextEditor::insertEmbeddedItem( S32 pos, LLInventoryItem* item )
-{
- return execute( new TextCmdInsertEmbeddedItem( pos, item ) );
-}
-
-bool LLViewerTextEditor::importStream(std::istream& str)
-{
- LLNotecard nc(LLNotecard::MAX_SIZE);
- bool success = nc.importStream(str);
- if (success)
- {
- mEmbeddedItemList->clear();
- const std::vector<LLPointer<LLInventoryItem> >& items = nc.getItems();
- mEmbeddedItemList->addItems(items);
- // Actually set the text
- if (allowsEmbeddedItems())
- {
- if (nc.getVersion() == 1)
- setASCIIEmbeddedText( nc.getText() );
- else
- setEmbeddedText( nc.getText() );
- }
- else
- {
- setText( nc.getText() );
- }
- }
- return success;
-}
-
-void LLViewerTextEditor::copyInventory(const LLInventoryItem* item, U32 callback_id)
-{
- copy_inventory_from_notecard(LLUUID::null, // Don't specify a destination -- let the sim do that
- mObjectID,
- mNotecardInventoryID,
- item,
- callback_id);
-}
-
-bool LLViewerTextEditor::hasEmbeddedInventory()
-{
- return ! mEmbeddedItemList->empty();
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-bool LLViewerTextEditor::importBuffer( const char* buffer, S32 length )
-{
- LLMemoryStream str((U8*)buffer, length);
- return importStream(str);
-}
-
-bool LLViewerTextEditor::exportBuffer( std::string& buffer )
-{
- LLNotecard nc(LLNotecard::MAX_SIZE);
-
- // Get the embedded text and update the item list to just be the used items
- nc.setText(getEmbeddedText());
-
- // Now get the used items and copy the list to the notecard
- std::vector<LLPointer<LLInventoryItem> > embedded_items;
- mEmbeddedItemList->getEmbeddedItemList(embedded_items);
- nc.setItems(embedded_items);
-
- std::stringstream out_stream;
- nc.exportStream(out_stream);
-
- buffer = out_stream.str();
-
- return true;
-}
-
+/** + * @file llviewertexteditor.cpp + * @brief Text editor widget to let users enter a multi-line document. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewertexteditor.h" + +#include "llagent.h" +#include "llaudioengine.h" +#include "llavataractions.h" +#include "llenvironment.h" +#include "llfloaterreg.h" +#include "llfloatersidepanelcontainer.h" +#include "llfloaterworldmap.h" +#include "llfocusmgr.h" +#include "llinspecttexture.h" +#include "llinventorybridge.h" +#include "llinventorydefines.h" +#include "llinventorymodel.h" +#include "lllandmark.h" +#include "lllandmarkactions.h" +#include "lllandmarklist.h" +#include "llmaterialeditor.h" +#include "llmemorystream.h" +#include "llmenugl.h" +#include "llnotecard.h" +#include "llnotificationsutil.h" +#include "llpanelplaces.h" +#include "llpreview.h" +#include "llpreviewnotecard.h" +#include "llpreviewtexture.h" +#include "llscrollbar.h" +#include "llscrollcontainer.h" +#include "lltooldraganddrop.h" +#include "lltooltip.h" +#include "lltrans.h" +#include "lluictrlfactory.h" +#include "llviewerassettype.h" +#include "llviewercontrol.h" +#include "llviewerinventory.h" +#include "llviewertexturelist.h" +#include "llviewerwindow.h" + +static LLDefaultChildRegistry::Register<LLViewerTextEditor> r("text_editor"); + +///----------------------------------------------------------------------- +/// Class LLEmbeddedLandmarkCopied +///----------------------------------------------------------------------- +class LLEmbeddedLandmarkCopied: public LLInventoryCallback +{ +public: + + LLEmbeddedLandmarkCopied(){} + void fire(const LLUUID& inv_item) + { + showInfo(inv_item); + } + static void showInfo(const LLUUID& landmark_inv_id) + { + LLSD key; + key["type"] = "landmark"; + key["id"] = landmark_inv_id; + LLFloaterSidePanelContainer::showPanel("places", key); + } + static void processForeignLandmark(LLLandmark* landmark, + const LLUUID& object_id, const LLUUID& notecard_inventory_id, + LLPointer<LLInventoryItem> item_ptr) + { + LLVector3d global_pos; + landmark->getGlobalPos(global_pos); + LLViewerInventoryItem* agent_landmark = + LLLandmarkActions::findLandmarkForGlobalPos(global_pos); + + if (agent_landmark) + { + showInfo(agent_landmark->getUUID()); + } + else + { + if (item_ptr.isNull()) + { + // check to prevent a crash. See EXT-8459. + LL_WARNS() << "Passed handle contains a dead inventory item. Most likely notecard has been closed and embedded item was destroyed." << LL_ENDL; + } + else + { + LLInventoryItem* item = item_ptr.get(); + LLPointer<LLEmbeddedLandmarkCopied> cb = new LLEmbeddedLandmarkCopied(); + copy_inventory_from_notecard(get_folder_by_itemtype(item), + object_id, + notecard_inventory_id, + item, + gInventoryCallbacks.registerCB(cb)); + } + } + } +}; +///---------------------------------------------------------------------------- +/// Class LLEmbeddedNotecardOpener +///---------------------------------------------------------------------------- +class LLEmbeddedNotecardOpener : public LLInventoryCallback +{ + LLViewerTextEditor* mTextEditor; + +public: + LLEmbeddedNotecardOpener() + : mTextEditor(NULL) + { + } + + void setEditor(LLViewerTextEditor* e) {mTextEditor = e;} + + // override + void fire(const LLUUID& inv_item) + { + if(!mTextEditor) + { + // The parent text editor may have vanished by now. + // In that case just quit. + return; + } + + LLInventoryItem* item = gInventory.getItem(inv_item); + if(!item) + { + LL_WARNS() << "Item add reported, but not found in inventory!: " << inv_item << LL_ENDL; + } + else + { + if(!gSavedSettings.getBOOL("ShowNewInventory")) + { + LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES); + } + } + } +}; + +// +// class LLEmbeddedItemSegment +// + +const S32 EMBEDDED_ITEM_LABEL_PADDING = 2; + +class LLEmbeddedItemSegment : public LLTextSegment +{ +public: + LLEmbeddedItemSegment(S32 pos, LLUIImagePtr image, LLPointer<LLInventoryItem> inv_item, LLTextEditor& editor) + : LLTextSegment(pos, pos + 1), + mImage(image), + mLabel(utf8str_to_wstring(inv_item->getName())), + mItem(inv_item), + mEditor(editor) + { + + mStyle = new LLStyle(LLStyle::Params().font(LLFontGL::getFontSansSerif())); + mToolTip = inv_item->getName() + '\n' + inv_item->getDescription(); + } + + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const + { + if (num_chars == 0) + { + width = 0; + height = 0; + } + else + { + width = EMBEDDED_ITEM_LABEL_PADDING + mImage->getWidth() + mStyle->getFont()->getWidthF32(mLabel.c_str()); + height = llmax(mImage->getHeight(), mStyle->getFont()->getLineHeight()); + } + return false; + } + + /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const + { + // always draw at beginning of line + if (line_offset == 0) + { + return 1; + } + else + { + S32 width, height; + getDimensions(mStart, 1, width, height); + if (width > num_pixels) + { + return 0; + } + else + { + return 1; + } + } + } + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) + { + LLRectf image_rect = draw_rect; + image_rect.mRight = image_rect.mLeft + mImage->getWidth(); + image_rect.mTop = image_rect.mBottom + mImage->getHeight(); + mImage->draw(LLRect(image_rect.mLeft, image_rect.mTop, image_rect.mRight, image_rect.mBottom)); + + LLColor4 color; + if (mEditor.getReadOnly()) + { + color = LLUIColorTable::instance().getColor("TextEmbeddedItemReadOnlyColor"); + } + else + { + color = LLUIColorTable::instance().getColor("TextEmbeddedItemColor"); + } + + F32 right_x; + mStyle->getFont()->render(mLabel, 0, image_rect.mRight + EMBEDDED_ITEM_LABEL_PADDING, draw_rect.mTop, color, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::UNDERLINE, LLFontGL::NO_SHADOW, mLabel.length(), S32_MAX, &right_x); + return right_x; + } + + /*virtual*/ bool canEdit() const { return false; } + + + /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) + { + LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND); + return true; + } + virtual bool handleToolTip(S32 x, S32 y, MASK mask ) + { + if (mItem->getThumbnailUUID().notNull()) + { + LLSD params; + params["inv_type"] = mItem->getInventoryType(); + params["thumbnail_id"] = mItem->getThumbnailUUID(); + params["asset_id"] = mItem->getAssetUUID(); + + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(mToolTip) + .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1)) + .create_params(params)); + + return true; + } + + if (!mToolTip.empty()) + { + LLToolTipMgr::instance().show(mToolTip); + return true; + } + return false; + } + + /*virtual*/ LLStyleConstSP getStyle() const { return mStyle; } + +private: + LLUIImagePtr mImage; + LLWString mLabel; + LLStyleSP mStyle; + std::string mToolTip; + LLPointer<LLInventoryItem> mItem; + LLTextEditor& mEditor; +}; + + + +//////////////////////////////////////////////////////////// +// LLEmbeddedItems +// +// Embedded items are stored as: +// * A global map of llwchar to LLInventoryItem +// ** This is unique for each item embedded in any notecard +// to support copy/paste across notecards +// * A per-notecard set of embeded llwchars for easy removal +// from the global list +// * A per-notecard vector of embedded lwchars for mapping from +// old style 0x80 + item format notechards + +class LLEmbeddedItems +{ +public: + LLEmbeddedItems(const LLViewerTextEditor* editor); + ~LLEmbeddedItems(); + void clear(); + + // return true if there are no embedded items. + bool empty(); + + bool insertEmbeddedItem(LLInventoryItem* item, llwchar* value, bool is_new); + bool removeEmbeddedItem( llwchar ext_char ); + + bool hasEmbeddedItem(llwchar ext_char); // returns true if /this/ editor has an entry for this item + LLUIImagePtr getItemImage(llwchar ext_char) const; + + void getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items ); + void addItems(const std::vector<LLPointer<LLInventoryItem> >& items); + + llwchar getEmbeddedCharFromIndex(S32 index); + + void removeUnusedChars(); + void copyUsedCharsToIndexed(); + S32 getIndexFromEmbeddedChar(llwchar wch); + + void markSaved(); + + static LLPointer<LLInventoryItem> getEmbeddedItemPtr(llwchar ext_char); // returns pointer to item from static list + static bool getEmbeddedItemSaved(llwchar ext_char); // returns whether item from static list is saved + +private: + + struct embedded_info_t + { + LLPointer<LLInventoryItem> mItemPtr; + bool mSaved; + }; + typedef std::map<llwchar, embedded_info_t > item_map_t; + static item_map_t sEntries; + static std::stack<llwchar> sFreeEntries; + + std::set<llwchar> mEmbeddedUsedChars; // list of used llwchars + std::vector<llwchar> mEmbeddedIndexedChars; // index -> wchar for 0x80 + index format + const LLViewerTextEditor* mEditor; +}; + +//statics +LLEmbeddedItems::item_map_t LLEmbeddedItems::sEntries; +std::stack<llwchar> LLEmbeddedItems::sFreeEntries; + +LLEmbeddedItems::LLEmbeddedItems(const LLViewerTextEditor* editor) + : mEditor(editor) +{ +} + +LLEmbeddedItems::~LLEmbeddedItems() +{ + clear(); +} + +void LLEmbeddedItems::clear() +{ + // Remove entries for this editor from static list + for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); + iter != mEmbeddedUsedChars.end();) + { + std::set<llwchar>::iterator nextiter = iter++; + removeEmbeddedItem(*nextiter); + } + mEmbeddedUsedChars.clear(); + mEmbeddedIndexedChars.clear(); +} + +bool LLEmbeddedItems::empty() +{ + removeUnusedChars(); + return mEmbeddedUsedChars.empty(); +} + +// Inserts a new unique entry +bool LLEmbeddedItems::insertEmbeddedItem( LLInventoryItem* item, llwchar* ext_char, bool is_new) +{ + // Now insert a new one + llwchar wc_emb; + if (!sFreeEntries.empty()) + { + wc_emb = sFreeEntries.top(); + sFreeEntries.pop(); + } + else if (sEntries.empty()) + { + wc_emb = LLTextEditor::FIRST_EMBEDDED_CHAR; + } + else + { + item_map_t::iterator last = sEntries.end(); + --last; + wc_emb = last->first; + if (wc_emb >= LLTextEditor::LAST_EMBEDDED_CHAR) + { + return false; + } + ++wc_emb; + } + + sEntries[wc_emb].mItemPtr = item; + sEntries[wc_emb].mSaved = !is_new; + *ext_char = wc_emb; + mEmbeddedUsedChars.insert(wc_emb); + return true; +} + +// Removes an entry (all entries are unique) +bool LLEmbeddedItems::removeEmbeddedItem( llwchar ext_char ) +{ + mEmbeddedUsedChars.erase(ext_char); + item_map_t::iterator iter = sEntries.find(ext_char); + if (iter != sEntries.end()) + { + sEntries.erase(ext_char); + sFreeEntries.push(ext_char); + return true; + } + return false; +} + +// static +LLPointer<LLInventoryItem> LLEmbeddedItems::getEmbeddedItemPtr(llwchar ext_char) +{ + if( ext_char >= LLTextEditor::FIRST_EMBEDDED_CHAR && ext_char <= LLTextEditor::LAST_EMBEDDED_CHAR ) + { + item_map_t::iterator iter = sEntries.find(ext_char); + if (iter != sEntries.end()) + { + return iter->second.mItemPtr; + } + } + return NULL; +} + +// static +bool LLEmbeddedItems::getEmbeddedItemSaved(llwchar ext_char) +{ + if( ext_char >= LLTextEditor::FIRST_EMBEDDED_CHAR && ext_char <= LLTextEditor::LAST_EMBEDDED_CHAR ) + { + item_map_t::iterator iter = sEntries.find(ext_char); + if (iter != sEntries.end()) + { + return iter->second.mSaved; + } + } + return false; +} + +llwchar LLEmbeddedItems::getEmbeddedCharFromIndex(S32 index) +{ + if (index >= (S32)mEmbeddedIndexedChars.size()) + { + LL_WARNS() << "No item for embedded char " << index << " using LL_UNKNOWN_CHAR" << LL_ENDL; + return LL_UNKNOWN_CHAR; + } + return mEmbeddedIndexedChars[index]; +} + +void LLEmbeddedItems::removeUnusedChars() +{ + std::set<llwchar> used = mEmbeddedUsedChars; + const LLWString& wtext = mEditor->getWText(); + for (S32 i=0; i<(S32)wtext.size(); i++) + { + llwchar wc = wtext[i]; + if( wc >= LLTextEditor::FIRST_EMBEDDED_CHAR && wc <= LLTextEditor::LAST_EMBEDDED_CHAR ) + { + used.erase(wc); + } + } + // Remove chars not actually used + for (std::set<llwchar>::iterator iter = used.begin(); + iter != used.end(); ++iter) + { + removeEmbeddedItem(*iter); + } +} + +void LLEmbeddedItems::copyUsedCharsToIndexed() +{ + // Prune unused items + removeUnusedChars(); + + // Copy all used llwchars to mEmbeddedIndexedChars + mEmbeddedIndexedChars.clear(); + for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); + iter != mEmbeddedUsedChars.end(); ++iter) + { + mEmbeddedIndexedChars.push_back(*iter); + } +} + +S32 LLEmbeddedItems::getIndexFromEmbeddedChar(llwchar wch) +{ + S32 idx = 0; + for (std::vector<llwchar>::iterator iter = mEmbeddedIndexedChars.begin(); + iter != mEmbeddedIndexedChars.end(); ++iter) + { + if (wch == *iter) + break; + ++idx; + } + if (idx < (S32)mEmbeddedIndexedChars.size()) + { + return idx; + } + else + { + LL_WARNS() << "Embedded char " << wch << " not found, using 0" << LL_ENDL; + return 0; + } +} + +bool LLEmbeddedItems::hasEmbeddedItem(llwchar ext_char) +{ + std::set<llwchar>::iterator iter = mEmbeddedUsedChars.find(ext_char); + if (iter != mEmbeddedUsedChars.end()) + { + return true; + } + return false; +} + + +LLUIImagePtr LLEmbeddedItems::getItemImage(llwchar ext_char) const +{ + LLInventoryItem* item = getEmbeddedItemPtr(ext_char); + if (item) + { + const char* img_name = ""; + switch( item->getType() ) + { + case LLAssetType::AT_TEXTURE: + if(item->getInventoryType() == LLInventoryType::IT_SNAPSHOT) + { + img_name = "Inv_Snapshot"; + } + else + { + img_name = "Inv_Texture"; + } + + break; + case LLAssetType::AT_SOUND: img_name = "Inv_Sound"; break; + case LLAssetType::AT_CLOTHING: img_name = "Inv_Clothing"; break; + case LLAssetType::AT_OBJECT: + img_name = LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS & item->getFlags() ? + "Inv_Object_Multi" : "Inv_Object"; + break; + case LLAssetType::AT_CALLINGCARD: img_name = "Inv_CallingCard"; break; + case LLAssetType::AT_LANDMARK: img_name = "Inv_Landmark"; break; + case LLAssetType::AT_NOTECARD: img_name = "Inv_Notecard"; break; + case LLAssetType::AT_LSL_TEXT: img_name = "Inv_Script"; break; + case LLAssetType::AT_BODYPART: img_name = "Inv_Skin"; break; + case LLAssetType::AT_ANIMATION: img_name = "Inv_Animation"; break; + case LLAssetType::AT_GESTURE: img_name = "Inv_Gesture"; break; + case LLAssetType::AT_MESH: img_name = "Inv_Mesh"; break; + case LLAssetType::AT_SETTINGS: img_name = "Inv_Settings"; break; + case LLAssetType::AT_MATERIAL: img_name = "Inv_Material"; break; + default: img_name = "Inv_Invalid"; break; // use the Inv_Invalid icon for undefined object types (see MAINT-3981) + + } + + return LLUI::getUIImage(img_name); + } + return LLUIImagePtr(); +} + + +void LLEmbeddedItems::addItems(const std::vector<LLPointer<LLInventoryItem> >& items) +{ + for (std::vector<LLPointer<LLInventoryItem> >::const_iterator iter = items.begin(); + iter != items.end(); ++iter) + { + LLInventoryItem* item = *iter; + if (item) + { + llwchar wc; + if (!insertEmbeddedItem( item, &wc, false )) + { + break; + } + mEmbeddedIndexedChars.push_back(wc); + } + } +} + +void LLEmbeddedItems::getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items ) +{ + for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter) + { + llwchar wc = *iter; + LLPointer<LLInventoryItem> item = getEmbeddedItemPtr(wc); + if (item) + { + items.push_back(item); + } + } +} + +void LLEmbeddedItems::markSaved() +{ + for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter) + { + llwchar wc = *iter; + sEntries[wc].mSaved = true; + } +} + +/////////////////////////////////////////////////////////////////// + +class LLViewerTextEditor::TextCmdInsertEmbeddedItem : public LLTextBase::TextCmd +{ +public: + TextCmdInsertEmbeddedItem( S32 pos, LLInventoryItem* item ) + : TextCmd(pos, false), + mExtCharValue(0) + { + mItem = item; + } + + virtual bool execute( LLTextBase* editor, S32* delta ) + { + LLViewerTextEditor* viewer_editor = (LLViewerTextEditor*)editor; + // Take this opportunity to remove any unused embedded items from this editor + viewer_editor->mEmbeddedItemList->removeUnusedChars(); + if(viewer_editor->mEmbeddedItemList->insertEmbeddedItem( mItem, &mExtCharValue, true ) ) + { + LLWString ws; + ws.assign(1, mExtCharValue); + *delta = insert(editor, getPosition(), ws ); + return (*delta != 0); + } + return false; + } + + virtual S32 undo( LLTextBase* editor ) + { + remove(editor, getPosition(), 1); + return getPosition(); + } + + virtual S32 redo( LLTextBase* editor ) + { + LLWString ws; + ws += mExtCharValue; + insert(editor, getPosition(), ws ); + return getPosition() + 1; + } + virtual bool hasExtCharValue( llwchar value ) const + { + return (value == mExtCharValue); + } + +private: + LLPointer<LLInventoryItem> mItem; + llwchar mExtCharValue; +}; + +struct LLNotecardCopyInfo +{ + LLNotecardCopyInfo(LLViewerTextEditor *ed, LLInventoryItem *item) + : mTextEd(ed) + { + mItem = item; + } + + LLViewerTextEditor* mTextEd; + // need to make this be a copy (not a * here) because it isn't stable. + // I wish we had passed LLPointers all the way down, but we didn't + LLPointer<LLInventoryItem> mItem; +}; + +//---------------------------------------------------------------------------- + +// +// Member functions +// +LLViewerTextEditor::LLViewerTextEditor(const LLViewerTextEditor::Params& p) +: LLTextEditor(p), + mDragItemChar(0), + mDragItemSaved(false), + mInventoryCallback(new LLEmbeddedNotecardOpener) +{ + mEmbeddedItemList = new LLEmbeddedItems(this); + mInventoryCallback->setEditor(this); +} + +LLViewerTextEditor::~LLViewerTextEditor() +{ + delete mEmbeddedItemList; + + + // The inventory callback may still be in use by gInventoryCallbackManager... + // so set its reference to this to null. + mInventoryCallback->setEditor(NULL); +} + +/////////////////////////////////////////////////////////////////// +// virtual +void LLViewerTextEditor::makePristine() +{ + mEmbeddedItemList->markSaved(); + LLTextEditor::makePristine(); +} + +void LLViewerTextEditor::onVisibilityChange( bool new_visibility ) +{ + LLUICtrl::onVisibilityChange(new_visibility); +} + +bool LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // Let scrollbar have first dibs + handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + + if( !handled) + { + if( allowsEmbeddedItems() ) + { + setCursorAtLocalPos( x, y, false ); + llwchar wc = 0; + if (mCursorPos < getLength()) + { + wc = getWText()[mCursorPos]; + } + LLPointer<LLInventoryItem> item_at_pos = LLEmbeddedItems::getEmbeddedItemPtr(wc); + if (item_at_pos) + { + mDragItem = item_at_pos; + mDragItemChar = wc; + mDragItemSaved = LLEmbeddedItems::getEmbeddedItemSaved(wc); + gFocusMgr.setMouseCapture( this ); + mMouseDownX = x; + mMouseDownY = y; + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y ); + LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y ); + + if (hasTabStop()) + { + setFocus( true ); + } + + handled = true; + } + else + { + mDragItem = NULL; + } + } + + if (!handled) + { + handled = LLTextEditor::handleMouseDown(x, y, mask); + } + } + + return handled; +} + + +bool LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = LLTextEditor::handleHover(x, y, mask); + + if(hasMouseCapture() && mDragItem) + { + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y ); + + mScroller->autoScroll(x, y); + + if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) + { + LLToolDragAndDrop::getInstance()->beginDrag( + LLViewerAssetType::lookupDragAndDropType( mDragItem->getType() ), + mDragItem->getUUID(), + LLToolDragAndDrop::SOURCE_NOTECARD, + mPreviewID, mObjectID); + return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); + } + getWindow()->setCursor(UI_CURSOR_HAND); + handled = true; + } + + return handled; +} + + +bool LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + if( hasMouseCapture() ) + { + if (mDragItem) + { + // mouse down was on an item + S32 dx = x - mMouseDownX; + S32 dy = y - mMouseDownY; + if (-2 < dx && dx < 2 && -2 < dy && dy < 2) + { + if(mDragItemSaved) + { + openEmbeddedItem(mDragItem, mDragItemChar); + } + else + { + showUnsavedAlertDialog(mDragItem); + } + } + } + mDragItem = NULL; + } + + handled = LLTextEditor::handleMouseUp(x,y,mask); + + return handled; +} + +bool LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + bool handled = false; + + // let scrollbar have first dibs + handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL; + + if( !handled) + { + if( allowsEmbeddedItems() ) + { + S32 doc_index = getDocIndexFromLocalCoord(x, y, false); + llwchar doc_char = getWText()[doc_index]; + if (mEmbeddedItemList->hasEmbeddedItem(doc_char)) + { + if( openEmbeddedItemAtPos( doc_index )) + { + deselect(); + setFocus( false ); + return true; + } + } + } + handled = LLTextEditor::handleDoubleClick(x, y, mask); + } + return handled; +} + + +// virtual +bool LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask, + bool drop, EDragAndDropType cargo_type, void *cargo_data, + EAcceptance *accept, + std::string& tooltip_msg) +{ + bool handled = false; + + LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); + if (LLToolDragAndDrop::SOURCE_NOTECARD == source) + { + // We currently do not handle dragging items from one notecard to another + // since items in a notecard must be in Inventory to be verified. See DEV-2891. + return false; + } + + if (getEnabled() && acceptsTextInput()) + { + bool supported = false; + switch( cargo_type ) + { + case DAD_SETTINGS: + { + supported = LLEnvironment::instance().isExtendedEnvironmentEnabled(); + if (!supported && tooltip_msg.empty()) + { + tooltip_msg.assign(LLTrans::getString("TooltipNotecardNotAllowedTypeDrop")); + } + break; + } + case DAD_CALLINGCARD: + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: + case DAD_MESH: + case DAD_MATERIAL: + { + supported = true; + break; + } + + default: + supported = false; + break; + } + + LLInventoryItem *item = (LLInventoryItem *)cargo_data; + if (item && allowsEmbeddedItems() && supported) + { + U32 mask_next = item->getPermissions().getMaskNextOwner(); + if((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) + { + if( drop ) + { + deselect(); + S32 old_cursor = mCursorPos; + setCursorAtLocalPos( x, y, true ); + S32 insert_pos = mCursorPos; + setCursorPos(old_cursor); + bool inserted = insertEmbeddedItem( insert_pos, item ); + if( inserted && (old_cursor > mCursorPos) ) + { + setCursorPos(mCursorPos + 1); + } + + needsReflow(); + } + *accept = ACCEPT_YES_COPY_MULTI; + } + else + { + *accept = ACCEPT_NO; + if (tooltip_msg.empty()) + { + tooltip_msg.assign(LLTrans::getString("TooltipNotecardOwnerRestrictedDrop")); + } + } + } + else + { + *accept = ACCEPT_NO; + } + } + else + { + // Not enabled + *accept = ACCEPT_NO; + } + + handled = true; + LL_DEBUGS("UserInput") << "dragAndDrop handled by LLViewerTextEditor " << getName() << LL_ENDL; + + return handled; +} + +void LLViewerTextEditor::setASCIIEmbeddedText(const std::string& instr) +{ + LLWString wtext; + const U8* buffer = (U8*)(instr.c_str()); + while (*buffer) + { + llwchar wch; + U8 c = *buffer++; + if (c >= 0x80) + { + S32 index = (S32)(c - 0x80); + wch = mEmbeddedItemList->getEmbeddedCharFromIndex(index); + } + else + { + wch = (llwchar)c; + } + wtext.push_back(wch); + } + setWText(wtext); +} + +void LLViewerTextEditor::setEmbeddedText(const std::string& instr) +{ + LLWString wtext = utf8str_to_wstring(instr); + for (S32 i=0; i<(S32)wtext.size(); i++) + { + llwchar wch = wtext[i]; + if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR ) + { + S32 index = wch - FIRST_EMBEDDED_CHAR; + wtext[i] = mEmbeddedItemList->getEmbeddedCharFromIndex(index); + } + } + setWText(wtext); +} + +std::string LLViewerTextEditor::getEmbeddedText() +{ +#if 1 + // New version (Version 2) + mEmbeddedItemList->copyUsedCharsToIndexed(); + LLWString outtextw; + for (S32 i=0; i<(S32)getWText().size(); i++) + { + llwchar wch = getWText()[i]; + if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR ) + { + S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch); + wch = FIRST_EMBEDDED_CHAR + index; + } + outtextw.push_back(wch); + } + std::string outtext = wstring_to_utf8str(outtextw); + return outtext; +#else + // Old version (Version 1) + mEmbeddedItemList->copyUsedCharsToIndexed(); + std::string outtext; + for (S32 i=0; i<(S32)mWText.size(); i++) + { + llwchar wch = mWText[i]; + if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR ) + { + S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch); + wch = 0x80 | index % 128; + } + else if (wch >= 0x80) + { + wch = LL_UNKNOWN_CHAR; + } + outtext.push_back((U8)wch); + } + return outtext; +#endif +} + +std::string LLViewerTextEditor::appendTime(bool prepend_newline) +{ + time_t utc_time; + utc_time = time_corrected(); + std::string timeStr ="[["+ LLTrans::getString("TimeHour")+"]:[" + +LLTrans::getString("TimeMin")+"]] "; + + LLSD substitution; + + substitution["datetime"] = (S32) utc_time; + LLStringUtil::format (timeStr, substitution); + appendText(timeStr, prepend_newline, LLStyle::Params().color(LLColor4::grey)); + blockUndo(); + + return timeStr; +} + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +llwchar LLViewerTextEditor::pasteEmbeddedItem(llwchar ext_char) +{ + if (mEmbeddedItemList->hasEmbeddedItem(ext_char)) + { + return ext_char; // already exists in my list + } + LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItemPtr(ext_char); + if (item) + { + // Add item to my list and return new llwchar associated with it + llwchar new_wc; + if (mEmbeddedItemList->insertEmbeddedItem( item, &new_wc, true )) + { + return new_wc; + } + } + return LL_UNKNOWN_CHAR; // item not found or list full +} + +void LLViewerTextEditor::onValueChange(S32 start, S32 end) +{ + updateSegments(); + updateLinkSegments(); + findEmbeddedItemSegments(start, end); +} + +void LLViewerTextEditor::findEmbeddedItemSegments(S32 start, S32 end) +{ + LLWString text = getWText(); + + // Start with i just after the first embedded item + for(S32 idx = start; idx < end; idx++ ) + { + llwchar embedded_char = text[idx]; + if( embedded_char >= FIRST_EMBEDDED_CHAR + && embedded_char <= LAST_EMBEDDED_CHAR + && mEmbeddedItemList->hasEmbeddedItem(embedded_char) ) + { + LLInventoryItem* itemp = mEmbeddedItemList->getEmbeddedItemPtr(embedded_char); + LLUIImagePtr image = mEmbeddedItemList->getItemImage(embedded_char); + insertSegment(new LLEmbeddedItemSegment(idx, image, itemp, *this)); + } + } +} + +bool LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos) +{ + if( pos < getLength()) + { + llwchar wc = getWText()[pos]; + LLPointer<LLInventoryItem> item = LLEmbeddedItems::getEmbeddedItemPtr( wc ); + if( item ) + { + bool saved = LLEmbeddedItems::getEmbeddedItemSaved( wc ); + if (saved) + { + return openEmbeddedItem(item, wc); + } + else + { + showUnsavedAlertDialog(item); + } + } + } + return false; +} + + +bool LLViewerTextEditor::openEmbeddedItem(LLPointer<LLInventoryItem> item, llwchar wc) +{ + + switch( item->getType() ) + { + case LLAssetType::AT_TEXTURE: + openEmbeddedTexture( item, wc ); + return true; + + case LLAssetType::AT_SOUND: + openEmbeddedSound( item, wc ); + return true; + + case LLAssetType::AT_LANDMARK: + openEmbeddedLandmark( item, wc ); + return true; + + case LLAssetType::AT_CALLINGCARD: + openEmbeddedCallingcard( item, wc ); + return true; + case LLAssetType::AT_SETTINGS: + openEmbeddedSetting(item, wc); + return true; + case LLAssetType::AT_MATERIAL: + openEmbeddedGLTFMaterial(item, wc); + return true; + case LLAssetType::AT_NOTECARD: + case LLAssetType::AT_LSL_TEXT: + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_OBJECT: + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_ANIMATION: + case LLAssetType::AT_GESTURE: + showCopyToInvDialog( item, wc ); + return true; + default: + return false; + } + +} + + +void LLViewerTextEditor::openEmbeddedTexture( LLInventoryItem* item, llwchar wc ) +{ + // *NOTE: Just for embedded Texture , we should use getAssetUUID(), + // not getUUID(), because LLPreviewTexture pass in AssetUUID into + // LLPreview constructor ItemUUID parameter. + if (!item) + return; + LLPreviewTexture* preview = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item->getAssetUUID()), TAKE_FOCUS_YES); + if (preview) + { + preview->setAuxItem( item ); + preview->setNotecardInfo(mNotecardInventoryID, mObjectID); + if (preview->hasString("Title")) + { + LLStringUtil::format_map_t args; + args["[NAME]"] = item->getName(); + LLUIString title = preview->getString("Title", args); + preview->setTitle(title.getString()); + } + preview->getChild<LLUICtrl>("desc")->setValue(item->getDescription()); + } +} + +void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item, llwchar wc ) +{ + // Play sound locally + LLVector3d lpos_global = gAgent.getPositionGlobal(); + const F32 SOUND_GAIN = 1.0f; + if(gAudiop) + { + gAudiop->triggerSound(item->getAssetUUID(), gAgentID, SOUND_GAIN, LLAudioEngine::AUDIO_TYPE_UI, lpos_global); + } + showCopyToInvDialog( item, wc ); +} + + +void LLViewerTextEditor::openEmbeddedLandmark( LLPointer<LLInventoryItem> item_ptr, llwchar wc ) +{ + if (item_ptr.isNull()) + return; + + LLLandmark* landmark = gLandmarkList.getAsset(item_ptr->getAssetUUID(), + boost::bind(&LLEmbeddedLandmarkCopied::processForeignLandmark, _1, mObjectID, mNotecardInventoryID, item_ptr)); + if (landmark) + { + LLEmbeddedLandmarkCopied::processForeignLandmark(landmark, mObjectID, + mNotecardInventoryID, item_ptr); + } +} + +void LLViewerTextEditor::openEmbeddedCallingcard( LLInventoryItem* item, llwchar wc ) +{ + if (item && !item->getDescription().empty()) + { + LLAvatarActions::showProfile(LLUUID(item->getDescription())); + } + else if (item && !item->getCreatorUUID().isNull()) + { + LLAvatarActions::showProfile(item->getCreatorUUID()); + } +} + +void LLViewerTextEditor::openEmbeddedSetting(LLInventoryItem* item, llwchar wc) +{ + if (LLEnvironment::instance().isInventoryEnabled()) + { + showCopyToInvDialog(item, wc); + } + else + { + LLNotificationsUtil::add("NoEnvironmentSettings"); + } +} + +void LLViewerTextEditor::openEmbeddedGLTFMaterial(LLInventoryItem* item, llwchar wc) +{ + if (!item) + { + return; + } + + LLSD floater_key; + floater_key["objectid"] = mObjectID; + floater_key["notecardid"] = mNotecardInventoryID; + LLMaterialEditor* preview = LLFloaterReg::getTypedInstance<LLMaterialEditor>("material_editor", floater_key); + if (preview) + { + preview->setAuxItem(item); + preview->setNotecardInfo(mNotecardInventoryID, mObjectID); + preview->openFloater(floater_key); + preview->setFocus(true); + } +} + +void LLViewerTextEditor::showUnsavedAlertDialog( LLInventoryItem* item ) +{ + LLSD payload; + payload["item_id"] = item->getUUID(); + payload["notecard_id"] = mNotecardInventoryID; + LLNotificationsUtil::add( "ConfirmNotecardSave", LLSD(), payload, LLViewerTextEditor::onNotecardDialog); +} + +// static +bool LLViewerTextEditor::onNotecardDialog(const LLSD& notification, const LLSD& response ) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if( option == 0 ) + { + LLPreviewNotecard* preview = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", notification["payload"]["notecard_id"]);; + if (preview) + { + preview->saveItem(); + } + } + return false; +} + + + +void LLViewerTextEditor::showCopyToInvDialog( LLInventoryItem* item, llwchar wc ) +{ + LLSD payload; + LLUUID item_id = item->getUUID(); + payload["item_id"] = item_id; + payload["item_wc"] = LLSD::Integer(wc); + LLNotificationsUtil::add( "ConfirmItemCopy", LLSD(), payload, + boost::bind(&LLViewerTextEditor::onCopyToInvDialog, this, _1, _2)); +} + +bool LLViewerTextEditor::onCopyToInvDialog(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if( 0 == option ) + { + llwchar wc = llwchar(notification["payload"]["item_wc"].asInteger()); + LLInventoryItem* itemp = LLEmbeddedItems::getEmbeddedItemPtr(wc); + if (itemp) + copyInventory(itemp); + } + return false; +} + + + +// Returns change in number of characters in mWText +S32 LLViewerTextEditor::insertEmbeddedItem( S32 pos, LLInventoryItem* item ) +{ + return execute( new TextCmdInsertEmbeddedItem( pos, item ) ); +} + +bool LLViewerTextEditor::importStream(std::istream& str) +{ + LLNotecard nc(LLNotecard::MAX_SIZE); + bool success = nc.importStream(str); + if (success) + { + mEmbeddedItemList->clear(); + const std::vector<LLPointer<LLInventoryItem> >& items = nc.getItems(); + mEmbeddedItemList->addItems(items); + // Actually set the text + if (allowsEmbeddedItems()) + { + if (nc.getVersion() == 1) + setASCIIEmbeddedText( nc.getText() ); + else + setEmbeddedText( nc.getText() ); + } + else + { + setText( nc.getText() ); + } + } + return success; +} + +void LLViewerTextEditor::copyInventory(const LLInventoryItem* item, U32 callback_id) +{ + copy_inventory_from_notecard(LLUUID::null, // Don't specify a destination -- let the sim do that + mObjectID, + mNotecardInventoryID, + item, + callback_id); +} + +bool LLViewerTextEditor::hasEmbeddedInventory() +{ + return ! mEmbeddedItemList->empty(); +} + +//////////////////////////////////////////////////////////////////////////// + +bool LLViewerTextEditor::importBuffer( const char* buffer, S32 length ) +{ + LLMemoryStream str((U8*)buffer, length); + return importStream(str); +} + +bool LLViewerTextEditor::exportBuffer( std::string& buffer ) +{ + LLNotecard nc(LLNotecard::MAX_SIZE); + + // Get the embedded text and update the item list to just be the used items + nc.setText(getEmbeddedText()); + + // Now get the used items and copy the list to the notecard + std::vector<LLPointer<LLInventoryItem> > embedded_items; + mEmbeddedItemList->getEmbeddedItemList(embedded_items); + nc.setItems(embedded_items); + + std::stringstream out_stream; + nc.exportStream(out_stream); + + buffer = out_stream.str(); + + return true; +} + |