PageRenderTime 34ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

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