PageRenderTime 929ms CodeModel.GetById 162ms app.highlight 598ms RepoModel.GetById 156ms app.codeStats 1ms

/indra/newview/llviewertexteditor.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1313 lines | 1056 code | 160 blank | 97 comment | 132 complexity | 54f6850dcc05e0ec047f30261d3b52f2 MD5 | raw file
   1/** 
   2 * @file llviewertexteditor.cpp
   3 * @brief Text editor widget to let users enter a multi-line document.
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "llviewertexteditor.h"
  30
  31#include "llagent.h"
  32#include "llaudioengine.h"
  33#include "llavataractions.h"
  34#include "llfloaterreg.h"
  35#include "llfloatersidepanelcontainer.h"
  36#include "llfloaterworldmap.h"
  37#include "llfocusmgr.h"
  38#include "llinventorybridge.h"
  39#include "llinventorydefines.h"
  40#include "llinventorymodel.h"
  41#include "lllandmark.h"
  42#include "lllandmarkactions.h"
  43#include "lllandmarklist.h"
  44#include "llmemorystream.h"
  45#include "llmenugl.h"
  46#include "llnotecard.h"
  47#include "llnotificationsutil.h"
  48#include "llpanelplaces.h"
  49#include "llpreview.h"
  50#include "llpreviewnotecard.h"
  51#include "llpreviewtexture.h"
  52#include "llscrollbar.h"
  53#include "llscrollcontainer.h"
  54#include "lltooldraganddrop.h"
  55#include "lltooltip.h"
  56#include "lltrans.h"
  57#include "lluictrlfactory.h"
  58#include "llviewerassettype.h"
  59#include "llviewercontrol.h"
  60#include "llviewerinventory.h"
  61#include "llviewertexturelist.h"
  62#include "llviewerwindow.h"
  63
  64static LLDefaultChildRegistry::Register<LLViewerTextEditor> r("text_editor");
  65
  66///-----------------------------------------------------------------------
  67///  Class LLEmbeddedLandmarkCopied
  68///-----------------------------------------------------------------------
  69class LLEmbeddedLandmarkCopied: public LLInventoryCallback
  70{
  71public:
  72
  73	LLEmbeddedLandmarkCopied(){}
  74	void fire(const LLUUID& inv_item)
  75	{
  76		showInfo(inv_item);
  77	}
  78	static void showInfo(const LLUUID& landmark_inv_id)
  79	{
  80		LLSD key;
  81		key["type"] = "landmark";
  82		key["id"] = landmark_inv_id;
  83		LLFloaterSidePanelContainer::showPanel("places", key);
  84	}
  85	static void processForeignLandmark(LLLandmark* landmark,
  86			const LLUUID& object_id, const LLUUID& notecard_inventory_id,
  87			LLPointer<LLInventoryItem> item_ptr)
  88	{
  89		LLVector3d global_pos;
  90		landmark->getGlobalPos(global_pos);
  91		LLViewerInventoryItem* agent_landmark =
  92				LLLandmarkActions::findLandmarkForGlobalPos(global_pos);
  93
  94		if (agent_landmark)
  95		{
  96			showInfo(agent_landmark->getUUID());
  97		}
  98		else
  99		{
 100			if (item_ptr.isNull())
 101			{
 102				// check to prevent a crash. See EXT-8459.
 103				llwarns << "Passed handle contains a dead inventory item. Most likely notecard has been closed and embedded item was destroyed." << llendl;
 104			}
 105			else
 106			{
 107				LLInventoryItem* item = item_ptr.get();
 108				LLPointer<LLEmbeddedLandmarkCopied> cb = new LLEmbeddedLandmarkCopied();
 109				copy_inventory_from_notecard(get_folder_by_itemtype(item),
 110											 object_id,
 111											 notecard_inventory_id,
 112											 item,
 113											 gInventoryCallbacks.registerCB(cb));
 114			}
 115		}
 116	}
 117};
 118///----------------------------------------------------------------------------
 119/// Class LLEmbeddedNotecardOpener
 120///----------------------------------------------------------------------------
 121class LLEmbeddedNotecardOpener : public LLInventoryCallback
 122{
 123	LLViewerTextEditor* mTextEditor;
 124
 125public:
 126	LLEmbeddedNotecardOpener()
 127		: mTextEditor(NULL)
 128	{
 129	}
 130
 131	void setEditor(LLViewerTextEditor* e) {mTextEditor = e;}
 132
 133	// override
 134	void fire(const LLUUID& inv_item)
 135	{
 136		if(!mTextEditor)
 137		{
 138			// The parent text editor may have vanished by now. 
 139            // In that case just quit.
 140			return;
 141		}
 142
 143		LLInventoryItem* item = gInventory.getItem(inv_item);
 144		if(!item)
 145		{
 146			llwarns << "Item add reported, but not found in inventory!: " << inv_item << llendl;
 147		}
 148		else
 149		{
 150			if(!gSavedSettings.getBOOL("ShowNewInventory"))
 151			{
 152				LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
 153			}
 154		}
 155	}
 156};
 157
 158//
 159// class LLEmbeddedItemSegment
 160//
 161
 162const S32 EMBEDDED_ITEM_LABEL_PADDING = 2;
 163
 164class LLEmbeddedItemSegment : public LLTextSegment
 165{
 166public:
 167	LLEmbeddedItemSegment(S32 pos, LLUIImagePtr image, LLPointer<LLInventoryItem> inv_item, LLTextEditor& editor)
 168	:	LLTextSegment(pos, pos + 1),
 169		mImage(image),
 170		mLabel(utf8str_to_wstring(inv_item->getName())),
 171		mItem(inv_item),
 172		mEditor(editor),
 173		mHasMouseHover(false)
 174	{
 175
 176		mStyle = new LLStyle(LLStyle::Params().font(LLFontGL::getFontSansSerif()));
 177		mToolTip = inv_item->getName() + '\n' + inv_item->getDescription();
 178	}
 179
 180	/*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
 181	{
 182		if (num_chars == 0)
 183		{
 184			width = 0;
 185			height = 0;
 186		}
 187		else
 188		{
 189			width = EMBEDDED_ITEM_LABEL_PADDING + mImage->getWidth() + mStyle->getFont()->getWidth(mLabel.c_str());
 190			height = llmax(mImage->getHeight(), llceil(mStyle->getFont()->getLineHeight()));
 191		}
 192		return false;
 193	}
 194
 195	/*virtual*/ S32				getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const 
 196	{
 197		// always draw at beginning of line
 198		if (line_offset == 0)
 199		{
 200			return 1;
 201		}
 202		else
 203		{
 204			S32 width, height;
 205			getDimensions(mStart, 1, width, height);
 206			if (width > num_pixels) 
 207			{
 208				return 0;
 209			}
 210			else
 211			{
 212				return 1;
 213			}
 214		}
 215	}
 216	/*virtual*/ F32				draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
 217	{
 218		LLRect image_rect = draw_rect;
 219		image_rect.mRight = image_rect.mLeft + mImage->getWidth();
 220		image_rect.mTop = image_rect.mBottom + mImage->getHeight();
 221		mImage->draw(image_rect);
 222
 223		LLColor4 color;
 224		if (mEditor.getReadOnly())
 225		{
 226			color = LLUIColorTable::instance().getColor("TextEmbeddedItemReadOnlyColor");
 227		}
 228		else
 229		{
 230			color = LLUIColorTable::instance().getColor("TextEmbeddedItemColor");
 231		}
 232
 233		F32 right_x;
 234		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);
 235		return right_x;
 236	}
 237	
 238	/*virtual*/ bool			canEdit() const { return false; }
 239
 240
 241	/*virtual*/ BOOL			handleHover(S32 x, S32 y, MASK mask)
 242	{
 243		LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
 244		return TRUE;
 245	}
 246	virtual BOOL				handleToolTip(S32 x, S32 y, MASK mask )
 247	{ 
 248		if (!mToolTip.empty())
 249		{
 250			LLToolTipMgr::instance().show(mToolTip);
 251			return TRUE;
 252		}
 253		return FALSE; 
 254	}
 255
 256	/*virtual*/ LLStyleConstSP		getStyle() const { return mStyle; }
 257
 258private:
 259	LLUIImagePtr	mImage;
 260	LLWString		mLabel;
 261	LLStyleSP		mStyle;
 262	std::string		mToolTip;
 263	LLPointer<LLInventoryItem> mItem;
 264	LLTextEditor&	mEditor;
 265	bool			mHasMouseHover;
 266
 267};
 268
 269
 270
 271////////////////////////////////////////////////////////////
 272// LLEmbeddedItems
 273//
 274// Embedded items are stored as:
 275// * A global map of llwchar to LLInventoryItem
 276// ** This is unique for each item embedded in any notecard
 277//    to support copy/paste across notecards
 278// * A per-notecard set of embeded llwchars for easy removal
 279//   from the global list
 280// * A per-notecard vector of embedded lwchars for mapping from
 281//   old style 0x80 + item format notechards
 282
 283class LLEmbeddedItems
 284{
 285public:
 286	LLEmbeddedItems(const LLViewerTextEditor* editor);
 287	~LLEmbeddedItems();
 288	void clear();
 289
 290	// return true if there are no embedded items.
 291	bool empty();
 292	
 293	BOOL	insertEmbeddedItem(LLInventoryItem* item, llwchar* value, bool is_new);
 294	BOOL	removeEmbeddedItem( llwchar ext_char );
 295
 296	BOOL	hasEmbeddedItem(llwchar ext_char); // returns TRUE if /this/ editor has an entry for this item
 297	LLUIImagePtr getItemImage(llwchar ext_char) const;
 298
 299	void	getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items );
 300	void	addItems(const std::vector<LLPointer<LLInventoryItem> >& items);
 301
 302	llwchar	getEmbeddedCharFromIndex(S32 index);
 303
 304	void 	removeUnusedChars();
 305	void	copyUsedCharsToIndexed();
 306	S32		getIndexFromEmbeddedChar(llwchar wch);
 307
 308	void	markSaved();
 309	
 310	static LLPointer<LLInventoryItem> getEmbeddedItemPtr(llwchar ext_char); // returns pointer to item from static list
 311	static BOOL getEmbeddedItemSaved(llwchar ext_char); // returns whether item from static list is saved
 312
 313private:
 314
 315	struct embedded_info_t
 316	{
 317		LLPointer<LLInventoryItem> mItemPtr;
 318		BOOL mSaved;
 319	};
 320	typedef std::map<llwchar, embedded_info_t > item_map_t;
 321	static item_map_t sEntries;
 322	static std::stack<llwchar> sFreeEntries;
 323
 324	std::set<llwchar> mEmbeddedUsedChars;	 // list of used llwchars
 325	std::vector<llwchar> mEmbeddedIndexedChars; // index -> wchar for 0x80 + index format
 326	const LLViewerTextEditor* mEditor;
 327};
 328
 329//statics
 330LLEmbeddedItems::item_map_t LLEmbeddedItems::sEntries;
 331std::stack<llwchar> LLEmbeddedItems::sFreeEntries;
 332
 333LLEmbeddedItems::LLEmbeddedItems(const LLViewerTextEditor* editor)
 334	: mEditor(editor)
 335{
 336}
 337
 338LLEmbeddedItems::~LLEmbeddedItems()
 339{
 340	clear();
 341}
 342
 343void LLEmbeddedItems::clear()
 344{
 345	// Remove entries for this editor from static list
 346	for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
 347		 iter != mEmbeddedUsedChars.end();)
 348	{
 349		std::set<llwchar>::iterator nextiter = iter++;
 350		removeEmbeddedItem(*nextiter);
 351	}
 352	mEmbeddedUsedChars.clear();
 353	mEmbeddedIndexedChars.clear();
 354}
 355
 356bool LLEmbeddedItems::empty()
 357{
 358	removeUnusedChars();
 359	return mEmbeddedUsedChars.empty();
 360}
 361
 362// Inserts a new unique entry
 363BOOL LLEmbeddedItems::insertEmbeddedItem( LLInventoryItem* item, llwchar* ext_char, bool is_new)
 364{
 365	// Now insert a new one
 366	llwchar wc_emb;
 367	if (!sFreeEntries.empty())
 368	{
 369		wc_emb = sFreeEntries.top();
 370		sFreeEntries.pop();
 371	}
 372	else if (sEntries.empty())
 373	{
 374		wc_emb = LLTextEditor::FIRST_EMBEDDED_CHAR;
 375	}
 376	else
 377	{
 378		item_map_t::iterator last = sEntries.end();
 379		--last;
 380		wc_emb = last->first;
 381		if (wc_emb >= LLTextEditor::LAST_EMBEDDED_CHAR)
 382		{
 383			return FALSE;
 384		}
 385		++wc_emb;
 386	}
 387
 388	sEntries[wc_emb].mItemPtr = item;
 389	sEntries[wc_emb].mSaved = is_new ? FALSE : TRUE;
 390	*ext_char = wc_emb;
 391	mEmbeddedUsedChars.insert(wc_emb);
 392	return TRUE;
 393}
 394
 395// Removes an entry (all entries are unique)
 396BOOL LLEmbeddedItems::removeEmbeddedItem( llwchar ext_char )
 397{
 398	mEmbeddedUsedChars.erase(ext_char);
 399	item_map_t::iterator iter = sEntries.find(ext_char);
 400	if (iter != sEntries.end())
 401	{
 402		sEntries.erase(ext_char);
 403		sFreeEntries.push(ext_char);
 404		return TRUE;
 405	}
 406	return FALSE;
 407}
 408	
 409// static
 410LLPointer<LLInventoryItem> LLEmbeddedItems::getEmbeddedItemPtr(llwchar ext_char)
 411{
 412	if( ext_char >= LLTextEditor::FIRST_EMBEDDED_CHAR && ext_char <= LLTextEditor::LAST_EMBEDDED_CHAR )
 413	{
 414		item_map_t::iterator iter = sEntries.find(ext_char);
 415		if (iter != sEntries.end())
 416		{
 417			return iter->second.mItemPtr;
 418		}
 419	}
 420	return NULL;
 421}
 422
 423// static
 424BOOL LLEmbeddedItems::getEmbeddedItemSaved(llwchar ext_char)
 425{
 426	if( ext_char >= LLTextEditor::FIRST_EMBEDDED_CHAR && ext_char <= LLTextEditor::LAST_EMBEDDED_CHAR )
 427	{
 428		item_map_t::iterator iter = sEntries.find(ext_char);
 429		if (iter != sEntries.end())
 430		{
 431			return iter->second.mSaved;
 432		}
 433	}
 434	return FALSE;
 435}
 436
 437llwchar	LLEmbeddedItems::getEmbeddedCharFromIndex(S32 index)
 438{
 439	if (index >= (S32)mEmbeddedIndexedChars.size())
 440	{
 441		llwarns << "No item for embedded char " << index << " using LL_UNKNOWN_CHAR" << llendl;
 442		return LL_UNKNOWN_CHAR;
 443	}
 444	return mEmbeddedIndexedChars[index];
 445}
 446
 447void LLEmbeddedItems::removeUnusedChars()
 448{
 449	std::set<llwchar> used = mEmbeddedUsedChars;
 450	const LLWString& wtext = mEditor->getWText();
 451	for (S32 i=0; i<(S32)wtext.size(); i++)
 452	{
 453		llwchar wc = wtext[i];
 454		if( wc >= LLTextEditor::FIRST_EMBEDDED_CHAR && wc <= LLTextEditor::LAST_EMBEDDED_CHAR )
 455		{
 456			used.erase(wc);
 457		}
 458	}
 459	// Remove chars not actually used
 460	for (std::set<llwchar>::iterator iter = used.begin();
 461		 iter != used.end(); ++iter)
 462	{
 463		removeEmbeddedItem(*iter);
 464	}
 465}
 466
 467void LLEmbeddedItems::copyUsedCharsToIndexed()
 468{
 469	// Prune unused items
 470	removeUnusedChars();
 471
 472	// Copy all used llwchars to mEmbeddedIndexedChars
 473	mEmbeddedIndexedChars.clear();
 474	for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
 475		 iter != mEmbeddedUsedChars.end(); ++iter)
 476	{
 477		mEmbeddedIndexedChars.push_back(*iter);
 478	}
 479}
 480
 481S32 LLEmbeddedItems::getIndexFromEmbeddedChar(llwchar wch)
 482{
 483	S32 idx = 0;
 484	for (std::vector<llwchar>::iterator iter = mEmbeddedIndexedChars.begin();
 485		 iter != mEmbeddedIndexedChars.end(); ++iter)
 486	{
 487		if (wch == *iter)
 488			break;
 489		++idx;
 490	}
 491	if (idx < (S32)mEmbeddedIndexedChars.size())
 492	{
 493		return idx;
 494	}
 495	else
 496	{
 497		llwarns << "Embedded char " << wch << " not found, using 0" << llendl;
 498		return 0;
 499	}
 500}
 501
 502BOOL LLEmbeddedItems::hasEmbeddedItem(llwchar ext_char)
 503{
 504	std::set<llwchar>::iterator iter = mEmbeddedUsedChars.find(ext_char);
 505	if (iter != mEmbeddedUsedChars.end())
 506	{
 507		return TRUE;
 508	}
 509	return FALSE;
 510}
 511
 512
 513LLUIImagePtr LLEmbeddedItems::getItemImage(llwchar ext_char) const
 514{
 515	LLInventoryItem* item = getEmbeddedItemPtr(ext_char);
 516	if (item)
 517	{
 518		const char* img_name = "";
 519		switch( item->getType() )
 520		{
 521			case LLAssetType::AT_TEXTURE:
 522				if(item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)
 523				{
 524					img_name = "Inv_Snapshot";
 525				}
 526				else
 527				{
 528					img_name = "Inv_Texture";
 529				}
 530
 531				break;
 532			case LLAssetType::AT_SOUND:			img_name = "Inv_Sound";		break;
 533			case LLAssetType::AT_CLOTHING:		img_name = "Inv_Clothing";	break;
 534			case LLAssetType::AT_OBJECT:
 535				img_name = LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS & item->getFlags() ?
 536					"Inv_Object_Multi" : "Inv_Object";
 537				break;
 538			case LLAssetType::AT_CALLINGCARD:	img_name = "Inv_CallingCard"; break;
 539			case LLAssetType::AT_LANDMARK:		img_name = "Inv_Landmark"; 	break;
 540			case LLAssetType::AT_NOTECARD:		img_name = "Inv_Notecard";	break;
 541			case LLAssetType::AT_LSL_TEXT:		img_name = "Inv_Script";	break;
 542			case LLAssetType::AT_BODYPART:		img_name = "Inv_Skin";		break;
 543			case LLAssetType::AT_ANIMATION:		img_name = "Inv_Animation";	break;
 544			case LLAssetType::AT_GESTURE:		img_name = "Inv_Gesture";	break;
 545			case LLAssetType::AT_MESH:          img_name = "Inv_Mesh";	    break;
 546			default: llassert(0);
 547		}
 548
 549		return LLUI::getUIImage(img_name);
 550	}
 551	return LLUIImagePtr();
 552}
 553
 554
 555void LLEmbeddedItems::addItems(const std::vector<LLPointer<LLInventoryItem> >& items)
 556{
 557	for (std::vector<LLPointer<LLInventoryItem> >::const_iterator iter = items.begin();
 558		 iter != items.end(); ++iter)
 559	{
 560		LLInventoryItem* item = *iter;
 561		if (item)
 562		{
 563			llwchar wc;
 564			if (!insertEmbeddedItem( item, &wc, false ))
 565			{
 566				break;
 567			}
 568			mEmbeddedIndexedChars.push_back(wc);
 569		}
 570	}
 571}
 572
 573void LLEmbeddedItems::getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items )
 574{
 575	for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
 576	{
 577		llwchar wc = *iter;
 578		LLPointer<LLInventoryItem> item = getEmbeddedItemPtr(wc);
 579		if (item)
 580		{
 581			items.push_back(item);
 582		}
 583	}
 584}
 585
 586void LLEmbeddedItems::markSaved()
 587{
 588	for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
 589	{
 590		llwchar wc = *iter;
 591		sEntries[wc].mSaved = TRUE;
 592	}
 593}
 594
 595///////////////////////////////////////////////////////////////////
 596
 597class LLViewerTextEditor::TextCmdInsertEmbeddedItem : public LLTextBase::TextCmd
 598{
 599public:
 600	TextCmdInsertEmbeddedItem( S32 pos, LLInventoryItem* item )
 601		: TextCmd(pos, FALSE), 
 602		  mExtCharValue(0)
 603	{
 604		mItem = item;
 605	}
 606
 607	virtual BOOL execute( LLTextBase* editor, S32* delta )
 608	{
 609		LLViewerTextEditor* viewer_editor = (LLViewerTextEditor*)editor;
 610		// Take this opportunity to remove any unused embedded items from this editor
 611		viewer_editor->mEmbeddedItemList->removeUnusedChars();
 612		if(viewer_editor->mEmbeddedItemList->insertEmbeddedItem( mItem, &mExtCharValue, true ) )
 613		{
 614			LLWString ws;
 615			ws.assign(1, mExtCharValue);
 616			*delta = insert(editor, getPosition(), ws );
 617			return (*delta != 0);
 618		}
 619		return FALSE;
 620	}
 621	
 622	virtual S32 undo( LLTextBase* editor )
 623	{
 624		remove(editor, getPosition(), 1);
 625		return getPosition(); 
 626	}
 627	
 628	virtual S32 redo( LLTextBase* editor )
 629	{ 
 630		LLWString ws;
 631		ws += mExtCharValue;
 632		insert(editor, getPosition(), ws );
 633		return getPosition() + 1;
 634	}
 635	virtual BOOL hasExtCharValue( llwchar value ) const
 636	{
 637		return (value == mExtCharValue);
 638	}
 639
 640private:
 641	LLPointer<LLInventoryItem> mItem;
 642	llwchar mExtCharValue;
 643};
 644
 645struct LLNotecardCopyInfo
 646{
 647	LLNotecardCopyInfo(LLViewerTextEditor *ed, LLInventoryItem *item)
 648		: mTextEd(ed)
 649	{
 650		mItem = item;
 651	}
 652
 653	LLViewerTextEditor* mTextEd;
 654	// need to make this be a copy (not a * here) because it isn't stable.
 655	// I wish we had passed LLPointers all the way down, but we didn't
 656	LLPointer<LLInventoryItem> mItem;
 657};
 658
 659//----------------------------------------------------------------------------
 660
 661//
 662// Member functions
 663//
 664LLViewerTextEditor::LLViewerTextEditor(const LLViewerTextEditor::Params& p)
 665:	LLTextEditor(p),
 666	mDragItemChar(0),
 667	mDragItemSaved(FALSE),
 668	mInventoryCallback(new LLEmbeddedNotecardOpener)
 669{
 670	mEmbeddedItemList = new LLEmbeddedItems(this);
 671	mInventoryCallback->setEditor(this);
 672}
 673
 674LLViewerTextEditor::~LLViewerTextEditor()
 675{
 676	delete mEmbeddedItemList;
 677	
 678	
 679	// The inventory callback may still be in use by gInventoryCallbackManager...
 680	// so set its reference to this to null.
 681	mInventoryCallback->setEditor(NULL); 
 682}
 683
 684///////////////////////////////////////////////////////////////////
 685// virtual
 686void LLViewerTextEditor::makePristine()
 687{
 688	mEmbeddedItemList->markSaved();
 689	LLTextEditor::makePristine();
 690}
 691
 692BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
 693{
 694	BOOL	handled = FALSE;
 695
 696	// Let scrollbar have first dibs
 697	handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
 698
 699	if( !handled)
 700	{
 701		if( allowsEmbeddedItems() )
 702		{
 703			setCursorAtLocalPos( x, y, FALSE );
 704			llwchar wc = 0;
 705			if (mCursorPos < getLength())
 706			{
 707				wc = getWText()[mCursorPos];
 708			}
 709			LLPointer<LLInventoryItem> item_at_pos = LLEmbeddedItems::getEmbeddedItemPtr(wc);
 710			if (item_at_pos)
 711			{
 712				mDragItem = item_at_pos;
 713				mDragItemChar = wc;
 714				mDragItemSaved = LLEmbeddedItems::getEmbeddedItemSaved(wc);
 715				gFocusMgr.setMouseCapture( this );
 716				mMouseDownX = x;
 717				mMouseDownY = y;
 718				S32 screen_x;
 719				S32 screen_y;
 720				localPointToScreen(x, y, &screen_x, &screen_y );
 721				LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y );
 722
 723				if (hasTabStop())
 724				{
 725					setFocus( TRUE );
 726				}
 727
 728				handled = TRUE;
 729			}
 730			else
 731			{
 732				mDragItem = NULL;
 733			}
 734		}
 735
 736		if (!handled)
 737		{
 738			handled = LLTextEditor::handleMouseDown(x, y, mask);
 739		}
 740	}
 741
 742	return handled;
 743}
 744
 745
 746BOOL LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask)
 747{
 748	BOOL handled = LLTextEditor::handleHover(x, y, mask);
 749
 750	if(hasMouseCapture() && mDragItem)
 751	{
 752		S32 screen_x;
 753		S32 screen_y;
 754		localPointToScreen(x, y, &screen_x, &screen_y );
 755
 756		mScroller->autoScroll(x, y);
 757
 758		if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) )
 759		{
 760			LLToolDragAndDrop::getInstance()->beginDrag(
 761				LLViewerAssetType::lookupDragAndDropType( mDragItem->getType() ),
 762				mDragItem->getUUID(),
 763				LLToolDragAndDrop::SOURCE_NOTECARD,
 764				mPreviewID, mObjectID);
 765			return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask );
 766		}
 767		getWindow()->setCursor(UI_CURSOR_HAND);
 768		handled = TRUE;
 769	}
 770
 771	return handled;
 772}
 773
 774
 775BOOL LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
 776{
 777	BOOL handled = FALSE;
 778
 779	if( hasMouseCapture() )
 780	{
 781		if (mDragItem)
 782		{
 783			// mouse down was on an item
 784			S32 dx = x - mMouseDownX;
 785			S32 dy = y - mMouseDownY;
 786			if (-2 < dx && dx < 2 && -2 < dy && dy < 2)
 787			{
 788				if(mDragItemSaved)
 789				{
 790					openEmbeddedItem(mDragItem, mDragItemChar);
 791				}
 792				else
 793				{
 794					showUnsavedAlertDialog(mDragItem);
 795				}
 796			}
 797		}
 798		mDragItem = NULL;
 799	}
 800
 801	handled = LLTextEditor::handleMouseUp(x,y,mask);
 802
 803	return handled;
 804}
 805
 806BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
 807{
 808	BOOL	handled = FALSE;
 809
 810	// let scrollbar have first dibs
 811	handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
 812
 813	if( !handled)
 814	{
 815		if( allowsEmbeddedItems() )
 816		{
 817			S32 doc_index = getDocIndexFromLocalCoord(x, y, FALSE);
 818			llwchar doc_char = getWText()[doc_index];
 819			if (mEmbeddedItemList->hasEmbeddedItem(doc_char))
 820			{
 821				if( openEmbeddedItemAtPos( doc_index ))
 822				{
 823					deselect();
 824					setFocus( FALSE );
 825					return TRUE;
 826				}
 827			}
 828		}
 829		handled = LLTextEditor::handleDoubleClick(x, y, mask);
 830	}
 831	return handled;
 832}
 833
 834
 835// virtual
 836BOOL LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
 837					  BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
 838					  EAcceptance *accept,
 839					  std::string& tooltip_msg)
 840{
 841	BOOL handled = FALSE;
 842	
 843	LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
 844	if (LLToolDragAndDrop::SOURCE_NOTECARD == source)
 845	{
 846		// We currently do not handle dragging items from one notecard to another
 847		// since items in a notecard must be in Inventory to be verified. See DEV-2891.
 848		return FALSE;
 849	}
 850	
 851	if (getEnabled() && acceptsTextInput())
 852	{
 853		switch( cargo_type )
 854		{
 855			case DAD_CALLINGCARD:
 856			case DAD_TEXTURE:
 857			case DAD_SOUND:
 858			case DAD_LANDMARK:
 859			case DAD_SCRIPT:
 860			case DAD_CLOTHING:
 861			case DAD_OBJECT:
 862			case DAD_NOTECARD:
 863			case DAD_BODYPART:
 864			case DAD_ANIMATION:
 865			case DAD_GESTURE:
 866			case DAD_MESH:
 867			{
 868				LLInventoryItem *item = (LLInventoryItem *)cargo_data;
 869				if( item && allowsEmbeddedItems() )
 870				{
 871					U32 mask_next = item->getPermissions().getMaskNextOwner();
 872					if((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
 873					{
 874						if( drop )
 875						{
 876							deselect();
 877							S32 old_cursor = mCursorPos;
 878							setCursorAtLocalPos( x, y, TRUE );
 879							S32 insert_pos = mCursorPos;
 880							setCursorPos(old_cursor);
 881							BOOL inserted = insertEmbeddedItem( insert_pos, item );
 882							if( inserted && (old_cursor > mCursorPos) )
 883							{
 884								setCursorPos(mCursorPos + 1);
 885							}
 886
 887							needsReflow();
 888							
 889						}
 890						*accept = ACCEPT_YES_COPY_MULTI;
 891					}
 892					else
 893					{
 894						*accept = ACCEPT_NO;
 895						if (tooltip_msg.empty())
 896						{
 897							// *TODO: Translate
 898							tooltip_msg.assign("Only items with unrestricted\n"
 899												"'next owner' permissions \n"
 900												"can be attached to notecards.");
 901						}
 902					}
 903				}
 904				else
 905				{
 906					*accept = ACCEPT_NO;
 907				}
 908				break;
 909			}
 910
 911		default:
 912			*accept = ACCEPT_NO;
 913			break;
 914		}
 915	}
 916	else
 917	{
 918		// Not enabled
 919		*accept = ACCEPT_NO;
 920	}
 921
 922	handled = TRUE;
 923	lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLViewerTextEditor " << getName() << llendl;
 924
 925	return handled;
 926}
 927
 928void LLViewerTextEditor::setASCIIEmbeddedText(const std::string& instr)
 929{
 930	LLWString wtext;
 931	const U8* buffer = (U8*)(instr.c_str());
 932	while (*buffer)
 933	{
 934		llwchar wch;
 935		U8 c = *buffer++;
 936		if (c >= 0x80)
 937		{
 938			S32 index = (S32)(c - 0x80);
 939			wch = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
 940		}
 941		else
 942		{
 943			wch = (llwchar)c;
 944		}
 945		wtext.push_back(wch);
 946	}
 947	setWText(wtext);
 948}
 949
 950void LLViewerTextEditor::setEmbeddedText(const std::string& instr)
 951{
 952	LLWString wtext = utf8str_to_wstring(instr);
 953	for (S32 i=0; i<(S32)wtext.size(); i++)
 954	{
 955		llwchar wch = wtext[i];
 956		if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
 957		{
 958			S32 index = wch - FIRST_EMBEDDED_CHAR;
 959			wtext[i] = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
 960		}
 961	}
 962	setWText(wtext);
 963}
 964
 965std::string LLViewerTextEditor::getEmbeddedText()
 966{
 967#if 1
 968	// New version (Version 2)
 969	mEmbeddedItemList->copyUsedCharsToIndexed();
 970	LLWString outtextw;
 971	for (S32 i=0; i<(S32)getWText().size(); i++)
 972	{
 973		llwchar wch = getWText()[i];
 974		if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
 975		{
 976			S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
 977			wch = FIRST_EMBEDDED_CHAR + index;
 978		}
 979		outtextw.push_back(wch);
 980	}
 981	std::string outtext = wstring_to_utf8str(outtextw);
 982	return outtext;
 983#else
 984	// Old version (Version 1)
 985	mEmbeddedItemList->copyUsedCharsToIndexed();
 986	std::string outtext;
 987	for (S32 i=0; i<(S32)mWText.size(); i++)
 988	{
 989		llwchar wch = mWText[i];
 990		if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
 991		{
 992			S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
 993			wch = 0x80 | index % 128;
 994		}
 995		else if (wch >= 0x80)
 996		{
 997			wch = LL_UNKNOWN_CHAR;
 998		}
 999		outtext.push_back((U8)wch);
1000	}
1001	return outtext;
1002#endif
1003}
1004
1005std::string LLViewerTextEditor::appendTime(bool prepend_newline)
1006{
1007	time_t utc_time;
1008	utc_time = time_corrected();
1009	std::string timeStr ="[["+ LLTrans::getString("TimeHour")+"]:["
1010		+LLTrans::getString("TimeMin")+"]] ";
1011
1012	LLSD substitution;
1013
1014	substitution["datetime"] = (S32) utc_time;
1015	LLStringUtil::format (timeStr, substitution);
1016	appendText(timeStr, prepend_newline, LLStyle::Params().color(LLColor4::grey));
1017	blockUndo();
1018
1019	return timeStr;
1020}
1021
1022//----------------------------------------------------------------------------
1023//----------------------------------------------------------------------------
1024
1025llwchar LLViewerTextEditor::pasteEmbeddedItem(llwchar ext_char)
1026{
1027	if (mEmbeddedItemList->hasEmbeddedItem(ext_char))
1028	{
1029		return ext_char; // already exists in my list
1030	}
1031	LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItemPtr(ext_char);
1032	if (item)
1033	{
1034		// Add item to my list and return new llwchar associated with it
1035		llwchar new_wc;
1036		if (mEmbeddedItemList->insertEmbeddedItem( item, &new_wc, true ))
1037		{
1038			return new_wc;
1039		}
1040	}
1041	return LL_UNKNOWN_CHAR; // item not found or list full
1042}
1043
1044void LLViewerTextEditor::onValueChange(S32 start, S32 end)
1045{
1046	updateSegments();
1047	updateLinkSegments();
1048	findEmbeddedItemSegments(start, end);
1049}
1050
1051void LLViewerTextEditor::findEmbeddedItemSegments(S32 start, S32 end)
1052{
1053	LLWString text = getWText();
1054
1055	LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get()  );
1056
1057	// Start with i just after the first embedded item
1058	for(S32 idx = start; idx < end; idx++ )
1059	{
1060		llwchar embedded_char = text[idx];
1061		if( embedded_char >= FIRST_EMBEDDED_CHAR 
1062			&& embedded_char <= LAST_EMBEDDED_CHAR 
1063			&& mEmbeddedItemList->hasEmbeddedItem(embedded_char) )
1064		{
1065			LLInventoryItem* itemp = mEmbeddedItemList->getEmbeddedItemPtr(embedded_char);
1066			LLUIImagePtr image = mEmbeddedItemList->getItemImage(embedded_char);
1067			insertSegment(new LLEmbeddedItemSegment(idx, image, itemp, *this));
1068		}
1069	}
1070}
1071
1072BOOL LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos)
1073{
1074	if( pos < getLength())
1075	{
1076		llwchar wc = getWText()[pos];
1077		LLPointer<LLInventoryItem> item = LLEmbeddedItems::getEmbeddedItemPtr( wc );
1078		if( item )
1079		{
1080			BOOL saved = LLEmbeddedItems::getEmbeddedItemSaved( wc );
1081			if (saved)
1082			{
1083				return openEmbeddedItem(item, wc); 
1084			}
1085			else
1086			{
1087				showUnsavedAlertDialog(item);
1088			}
1089		}
1090	}
1091	return FALSE;
1092}
1093
1094
1095BOOL LLViewerTextEditor::openEmbeddedItem(LLPointer<LLInventoryItem> item, llwchar wc)
1096{
1097
1098	switch( item->getType() )
1099	{
1100		case LLAssetType::AT_TEXTURE:
1101			openEmbeddedTexture( item, wc );
1102			return TRUE;
1103
1104		case LLAssetType::AT_SOUND:
1105			openEmbeddedSound( item, wc );
1106			return TRUE;
1107
1108		case LLAssetType::AT_NOTECARD:
1109			openEmbeddedNotecard( item, wc );
1110			return TRUE;
1111
1112		case LLAssetType::AT_LANDMARK:
1113			openEmbeddedLandmark( item, wc );
1114			return TRUE;
1115
1116		case LLAssetType::AT_CALLINGCARD:
1117			openEmbeddedCallingcard( item, wc );
1118			return TRUE;
1119
1120		case LLAssetType::AT_LSL_TEXT:
1121		case LLAssetType::AT_CLOTHING:
1122		case LLAssetType::AT_OBJECT:
1123		case LLAssetType::AT_BODYPART:
1124		case LLAssetType::AT_ANIMATION:
1125		case LLAssetType::AT_GESTURE:
1126			showCopyToInvDialog( item, wc );
1127			return TRUE;
1128		default:
1129			return FALSE;
1130	}
1131
1132}
1133
1134
1135void LLViewerTextEditor::openEmbeddedTexture( LLInventoryItem* item, llwchar wc )
1136{
1137	// *NOTE:  Just for embedded Texture , we should use getAssetUUID(), 
1138	// not getUUID(), because LLPreviewTexture pass in AssetUUID into 
1139	// LLPreview constructor ItemUUID parameter.
1140	if (!item)
1141		return;
1142	LLPreviewTexture* preview = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item->getAssetUUID()), TAKE_FOCUS_YES);
1143	if (preview)
1144	{
1145		preview->setAuxItem( item );
1146		preview->setNotecardInfo(mNotecardInventoryID, mObjectID);
1147	}
1148}
1149
1150void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item, llwchar wc )
1151{
1152	// Play sound locally
1153	LLVector3d lpos_global = gAgent.getPositionGlobal();
1154	const F32 SOUND_GAIN = 1.0f;
1155	if(gAudiop)
1156	{
1157		gAudiop->triggerSound(item->getAssetUUID(), gAgentID, SOUND_GAIN, LLAudioEngine::AUDIO_TYPE_UI, lpos_global);
1158	}
1159	showCopyToInvDialog( item, wc );
1160}
1161
1162
1163void LLViewerTextEditor::openEmbeddedLandmark( LLPointer<LLInventoryItem> item_ptr, llwchar wc )
1164{
1165	if (item_ptr.isNull())
1166		return;
1167
1168	LLLandmark* landmark = gLandmarkList.getAsset(item_ptr->getAssetUUID(),
1169			boost::bind(&LLEmbeddedLandmarkCopied::processForeignLandmark, _1, mObjectID, mNotecardInventoryID, item_ptr));
1170	if (landmark)
1171	{
1172		LLEmbeddedLandmarkCopied::processForeignLandmark(landmark, mObjectID,
1173				mNotecardInventoryID, item_ptr);
1174	}
1175}
1176
1177void LLViewerTextEditor::openEmbeddedNotecard( LLInventoryItem* item, llwchar wc )
1178{
1179	copyInventory(item, gInventoryCallbacks.registerCB(mInventoryCallback));
1180}
1181
1182void LLViewerTextEditor::openEmbeddedCallingcard( LLInventoryItem* item, llwchar wc )
1183{
1184	if(item && !item->getCreatorUUID().isNull())
1185	{
1186		LLAvatarActions::showProfile(item->getCreatorUUID());
1187	}
1188}
1189
1190void LLViewerTextEditor::showUnsavedAlertDialog( LLInventoryItem* item )
1191{
1192	LLSD payload;
1193	payload["item_id"] = item->getUUID();
1194	payload["notecard_id"] = mNotecardInventoryID;
1195	LLNotificationsUtil::add( "ConfirmNotecardSave", LLSD(), payload, LLViewerTextEditor::onNotecardDialog);
1196}
1197
1198// static
1199bool LLViewerTextEditor::onNotecardDialog(const LLSD& notification, const LLSD& response )
1200{
1201	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
1202	if( option == 0 )
1203	{
1204		LLPreviewNotecard* preview = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", notification["payload"]["notecard_id"]);;
1205		if (preview)
1206		{
1207			preview->saveItem();
1208		}
1209	}
1210	return false;
1211}
1212
1213
1214
1215void LLViewerTextEditor::showCopyToInvDialog( LLInventoryItem* item, llwchar wc )
1216{
1217	LLSD payload;
1218	LLUUID item_id = item->getUUID();
1219	payload["item_id"] = item_id;
1220	payload["item_wc"] = LLSD::Integer(wc);
1221	LLNotificationsUtil::add( "ConfirmItemCopy", LLSD(), payload,
1222		boost::bind(&LLViewerTextEditor::onCopyToInvDialog, this, _1, _2));
1223}
1224
1225bool LLViewerTextEditor::onCopyToInvDialog(const LLSD& notification, const LLSD& response)
1226{
1227	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
1228	if( 0 == option )
1229	{
1230		LLUUID item_id = notification["payload"]["item_id"].asUUID();
1231		llwchar wc = llwchar(notification["payload"]["item_wc"].asInteger());
1232		LLInventoryItem* itemp = LLEmbeddedItems::getEmbeddedItemPtr(wc);
1233		if (itemp)
1234			copyInventory(itemp);
1235	}
1236	return false;
1237}
1238
1239
1240
1241// Returns change in number of characters in mWText
1242S32 LLViewerTextEditor::insertEmbeddedItem( S32 pos, LLInventoryItem* item )
1243{
1244	return execute( new TextCmdInsertEmbeddedItem( pos, item ) );
1245}
1246
1247bool LLViewerTextEditor::importStream(std::istream& str)
1248{
1249	LLNotecard nc(LLNotecard::MAX_SIZE);
1250	bool success = nc.importStream(str);
1251	if (success)
1252	{
1253		mEmbeddedItemList->clear();
1254		const std::vector<LLPointer<LLInventoryItem> >& items = nc.getItems();
1255		mEmbeddedItemList->addItems(items);
1256		// Actually set the text
1257		if (allowsEmbeddedItems())
1258		{
1259			if (nc.getVersion() == 1)
1260				setASCIIEmbeddedText( nc.getText() );
1261			else
1262				setEmbeddedText( nc.getText() );
1263		}
1264		else
1265		{
1266			setText( nc.getText() );
1267		}
1268	}
1269	return success;
1270}
1271
1272void LLViewerTextEditor::copyInventory(const LLInventoryItem* item, U32 callback_id)
1273{
1274	copy_inventory_from_notecard(LLUUID::null,  // Don't specify a destination -- let the sim do that
1275								 mObjectID,
1276								 mNotecardInventoryID,
1277								 item,
1278								 callback_id);
1279}
1280
1281bool LLViewerTextEditor::hasEmbeddedInventory()
1282{
1283	return ! mEmbeddedItemList->empty();
1284}
1285
1286////////////////////////////////////////////////////////////////////////////
1287
1288BOOL LLViewerTextEditor::importBuffer( const char* buffer, S32 length )
1289{
1290	LLMemoryStream str((U8*)buffer, length);
1291	return importStream(str);
1292}
1293
1294BOOL LLViewerTextEditor::exportBuffer( std::string& buffer )
1295{
1296	LLNotecard nc(LLNotecard::MAX_SIZE);
1297
1298	// Get the embedded text and update the item list to just be the used items
1299	nc.setText(getEmbeddedText());
1300
1301	// Now get the used items and copy the list to the notecard
1302	std::vector<LLPointer<LLInventoryItem> > embedded_items;
1303	mEmbeddedItemList->getEmbeddedItemList(embedded_items);	
1304	nc.setItems(embedded_items);
1305	
1306	std::stringstream out_stream;
1307	nc.exportStream(out_stream);
1308	
1309	buffer = out_stream.str();
1310	
1311	return TRUE;
1312}
1313