llviewertexteditor.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599
  1. /**
  2. * @file llviewertexteditor.cpp
  3. * @brief Text editor widget to let users enter a multi-line document.
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include <utility>
  34. #include "llviewertexteditor.h"
  35. #include "llaudioengine.h"
  36. #include "llnotecard.h"
  37. #include "llmemorystream.h"
  38. #include "llnotifications.h"
  39. #include "llscrollbar.h"
  40. #include "lltrans.h"
  41. #include "lluictrlfactory.h"
  42. #include "llwindow.h"
  43. #include "llagent.h"
  44. #include "llappviewer.h"
  45. #include "llfloaterchat.h"
  46. #include "llfloaterworldmap.h"
  47. #include "llgridmanager.h"
  48. #include "llinventoryactions.h"
  49. #include "llinventorybridge.h"
  50. #include "lllogchat.h"
  51. #include "llpreviewlandmark.h"
  52. #include "llpreviewnotecard.h"
  53. #include "llpreviewtexture.h"
  54. #include "lltooldraganddrop.h"
  55. #include "llviewercontrol.h"
  56. #include "llviewerinventory.h"
  57. #include "llviewertexturelist.h"
  58. static const std::string LL_TEXT_EDITOR_TAG = "text_editor";
  59. static LLRegisterWidget<LLViewerTextEditor> r(LL_TEXT_EDITOR_TAG);
  60. ///----------------------------------------------------------------------------
  61. /// Class LLEmbeddedNotecardOpener
  62. ///----------------------------------------------------------------------------
  63. class LLEmbeddedNotecardOpener : public LLInventoryCallback
  64. {
  65. protected:
  66. LOG_CLASS(LLEmbeddedNotecardOpener);
  67. LLViewerTextEditor* mTextEditor;
  68. public:
  69. LLEmbeddedNotecardOpener()
  70. : mTextEditor(NULL)
  71. {
  72. }
  73. void setEditor(LLViewerTextEditor* editp) { mTextEditor = editp; }
  74. void fire(const LLUUID& inv_item) override
  75. {
  76. if (!mTextEditor)
  77. {
  78. // The parent text editor may have vanished by now. In that case
  79. // just quit.
  80. llwarns << "Copy from notecard callback fired but parent notecard closed. Item ID: "
  81. << inv_item << llendl;
  82. return;
  83. }
  84. LLInventoryItem* item = gInventory.getItem(inv_item);
  85. if (!item)
  86. {
  87. llwarns << "Item add reported, but not found in inventory. Item ID: "
  88. << inv_item << llendl;
  89. return;
  90. }
  91. LL_DEBUGS("CopyFromNotecard") << "Copy from notecard callback fired for item ID: "
  92. << inv_item << LL_ENDL;
  93. // See if we can bring an existing preview to the front
  94. if (!LLPreview::show(item->getUUID(), true))
  95. {
  96. // There is not one, so make a new preview
  97. S32 left, top;
  98. gFloaterViewp->getNewFloaterPosition(&left, &top);
  99. LLRect rect = gSavedSettings.getRect("NotecardEditorRect");
  100. rect.translate(left - rect.mLeft, top - rect.mTop);
  101. LLPreviewNotecard* preview =
  102. new LLPreviewNotecard("preview notecard", rect,
  103. "Embedded Note: " + item->getName(),
  104. item->getUUID(), LLUUID::null,
  105. item->getAssetUUID(), true,
  106. (LLViewerInventoryItem*)item);
  107. preview->setFocus(true);
  108. // Force to be entirely onscreen.
  109. gFloaterViewp->adjustToFitScreen(preview);
  110. }
  111. }
  112. };
  113. ///////////////////////////////////////////////////////////////////////////////
  114. // LLEmbeddedItems
  115. //
  116. // Embedded items are stored as:
  117. // * A global map of llwchar to LLInventoryItem
  118. // * This is unique for each item embedded in any notecard to support
  119. // copy/paste across notecards
  120. // * A per-notecard set of embeded llwchars for easy removal from the global
  121. // list
  122. // * A per-notecard vector of embedded lwchars for mapping from old style 0x80
  123. // + item format notechards
  124. class LLEmbeddedItems
  125. {
  126. protected:
  127. LOG_CLASS(LLEmbeddedItems);
  128. public:
  129. LLEmbeddedItems(const LLViewerTextEditor* editor);
  130. ~LLEmbeddedItems();
  131. void clear();
  132. // Returns true if there are no embedded items.
  133. bool empty();
  134. void bindEmbeddedChars(LLFontGL* font) const;
  135. void unbindEmbeddedChars(LLFontGL* font) const;
  136. bool insertEmbeddedItem(LLInventoryItem* item, llwchar* value, bool is_new);
  137. bool removeEmbeddedItem(llwchar ext_char);
  138. // Returns true if /this/ editor has an entry for this item
  139. bool hasEmbeddedItem(llwchar ext_char);
  140. void getEmbeddedItemList(std::vector<LLPointer<LLInventoryItem> >& items);
  141. void addItems(const std::vector<LLPointer<LLInventoryItem> >& items);
  142. llwchar getEmbeddedCharFromIndex(S32 index);
  143. void removeUnusedChars();
  144. void copyUsedCharsToIndexed();
  145. S32 getIndexFromEmbeddedChar(llwchar wch);
  146. void markSaved();
  147. // returns item from static list
  148. static LLInventoryItem* getEmbeddedItem(llwchar ext_char);
  149. // returns whether item from static list is saved
  150. static bool getEmbeddedItemSaved(llwchar ext_char);
  151. private:
  152. struct embedded_info_t
  153. {
  154. LLPointer<LLInventoryItem> mItem;
  155. bool mSaved;
  156. };
  157. typedef std::map<llwchar, embedded_info_t> item_map_t;
  158. static item_map_t sEntries;
  159. static std::stack<llwchar> sFreeEntries;
  160. std::set<llwchar> mEmbeddedUsedChars; // list of used llwchars
  161. // index -> wchar for 0x80 + index format
  162. std::vector<llwchar> mEmbeddedIndexedChars;
  163. const LLViewerTextEditor* mEditor;
  164. };
  165. //statics
  166. LLEmbeddedItems::item_map_t LLEmbeddedItems::sEntries;
  167. std::stack<llwchar> LLEmbeddedItems::sFreeEntries;
  168. LLEmbeddedItems::LLEmbeddedItems(const LLViewerTextEditor* editor)
  169. : mEditor(editor)
  170. {
  171. }
  172. LLEmbeddedItems::~LLEmbeddedItems()
  173. {
  174. clear();
  175. }
  176. void LLEmbeddedItems::clear()
  177. {
  178. // Remove entries for this editor from static list
  179. for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(),
  180. end = mEmbeddedUsedChars.end();
  181. iter != end; )
  182. {
  183. std::set<llwchar>::iterator curiter = iter++;
  184. removeEmbeddedItem(*curiter);
  185. }
  186. mEmbeddedUsedChars.clear();
  187. mEmbeddedIndexedChars.clear();
  188. }
  189. bool LLEmbeddedItems::empty()
  190. {
  191. removeUnusedChars();
  192. return mEmbeddedUsedChars.empty();
  193. }
  194. // Inserts a new unique entry
  195. bool LLEmbeddedItems::insertEmbeddedItem(LLInventoryItem* item,
  196. llwchar* ext_char, bool is_new)
  197. {
  198. // Now insert a new one
  199. llwchar wc_emb;
  200. if (!sFreeEntries.empty())
  201. {
  202. wc_emb = sFreeEntries.top();
  203. sFreeEntries.pop();
  204. }
  205. else if (sEntries.empty())
  206. {
  207. wc_emb = FIRST_EMBEDDED_CHAR;
  208. }
  209. else
  210. {
  211. item_map_t::iterator last = sEntries.end();
  212. --last;
  213. wc_emb = last->first;
  214. if (wc_emb >= LAST_EMBEDDED_CHAR)
  215. {
  216. return false;
  217. }
  218. ++wc_emb;
  219. }
  220. sEntries[wc_emb].mItem = item;
  221. sEntries[wc_emb].mSaved = !is_new;
  222. *ext_char = wc_emb;
  223. mEmbeddedUsedChars.insert(wc_emb);
  224. return true;
  225. }
  226. // Removes an entry (all entries are unique)
  227. bool LLEmbeddedItems::removeEmbeddedItem(llwchar ext_char)
  228. {
  229. mEmbeddedUsedChars.erase(ext_char);
  230. item_map_t::iterator iter = sEntries.find(ext_char);
  231. if (iter != sEntries.end())
  232. {
  233. sEntries.erase(ext_char);
  234. sFreeEntries.push(ext_char);
  235. return true;
  236. }
  237. return false;
  238. }
  239. //static
  240. LLInventoryItem* LLEmbeddedItems::getEmbeddedItem(llwchar ext_char)
  241. {
  242. if (ext_char >= FIRST_EMBEDDED_CHAR && ext_char <= LAST_EMBEDDED_CHAR)
  243. {
  244. item_map_t::iterator iter = sEntries.find(ext_char);
  245. if (iter != sEntries.end())
  246. {
  247. return iter->second.mItem;
  248. }
  249. }
  250. return NULL;
  251. }
  252. //static
  253. bool LLEmbeddedItems::getEmbeddedItemSaved(llwchar ext_char)
  254. {
  255. if (ext_char >= FIRST_EMBEDDED_CHAR && ext_char <= LAST_EMBEDDED_CHAR)
  256. {
  257. item_map_t::iterator iter = sEntries.find(ext_char);
  258. if (iter != sEntries.end())
  259. {
  260. return iter->second.mSaved;
  261. }
  262. }
  263. return false;
  264. }
  265. llwchar LLEmbeddedItems::getEmbeddedCharFromIndex(S32 index)
  266. {
  267. if (index >= (S32)mEmbeddedIndexedChars.size())
  268. {
  269. llwarns << "No item for embedded char " << index
  270. << " using LL_UNKNOWN_CHAR" << llendl;
  271. return LL_UNKNOWN_CHAR;
  272. }
  273. return mEmbeddedIndexedChars[index];
  274. }
  275. void LLEmbeddedItems::removeUnusedChars()
  276. {
  277. std::set<llwchar> used = mEmbeddedUsedChars;
  278. const LLWString& wtext = mEditor->getWText();
  279. for (S32 i = 0, count = wtext.size(); i < count; ++i)
  280. {
  281. llwchar wc = wtext[i];
  282. if (wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR)
  283. {
  284. used.erase(wc);
  285. }
  286. }
  287. // Remove chars not actually used
  288. for (std::set<llwchar>::iterator iter = used.begin(), end = used.end();
  289. iter != end; ++iter)
  290. {
  291. removeEmbeddedItem(*iter);
  292. }
  293. }
  294. void LLEmbeddedItems::copyUsedCharsToIndexed()
  295. {
  296. // Prune unused items
  297. removeUnusedChars();
  298. // Copy all used llwchars to mEmbeddedIndexedChars
  299. mEmbeddedIndexedChars.clear();
  300. for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(),
  301. end = mEmbeddedUsedChars.end();
  302. iter != end; ++iter)
  303. {
  304. mEmbeddedIndexedChars.emplace_back(*iter);
  305. }
  306. }
  307. S32 LLEmbeddedItems::getIndexFromEmbeddedChar(llwchar wch)
  308. {
  309. S32 idx = 0;
  310. for (std::vector<llwchar>::iterator iter = mEmbeddedIndexedChars.begin(),
  311. end = mEmbeddedIndexedChars.end();
  312. iter != end; ++iter)
  313. {
  314. if (wch == *iter)
  315. {
  316. break;
  317. }
  318. ++idx;
  319. }
  320. if (idx < (S32)mEmbeddedIndexedChars.size())
  321. {
  322. return idx;
  323. }
  324. else
  325. {
  326. llwarns << "Embedded char " << wch << " not found, using 0" << llendl;
  327. return 0;
  328. }
  329. }
  330. bool LLEmbeddedItems::hasEmbeddedItem(llwchar ext_char)
  331. {
  332. std::set<llwchar>::iterator iter = mEmbeddedUsedChars.find(ext_char);
  333. if (iter != mEmbeddedUsedChars.end())
  334. {
  335. return true;
  336. }
  337. return false;
  338. }
  339. void LLEmbeddedItems::bindEmbeddedChars(LLFontGL* font) const
  340. {
  341. if (sEntries.empty())
  342. {
  343. return;
  344. }
  345. for (std::set<llwchar>::const_iterator iter1 = mEmbeddedUsedChars.begin(),
  346. end1 = mEmbeddedUsedChars.end();
  347. iter1 != end1; ++iter1)
  348. {
  349. llwchar wch = *iter1;
  350. item_map_t::iterator iter2 = sEntries.find(wch);
  351. if (iter2 == sEntries.end())
  352. {
  353. continue;
  354. }
  355. LLInventoryItem* item = iter2->second.mItem;
  356. if (!item)
  357. {
  358. continue;
  359. }
  360. const char* img_name;
  361. switch (item->getType())
  362. {
  363. case LLAssetType::AT_TEXTURE:
  364. if (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)
  365. {
  366. img_name = "inv_item_snapshot.tga";
  367. }
  368. else
  369. {
  370. img_name = "inv_item_texture.tga";
  371. }
  372. break;
  373. case LLAssetType::AT_SOUND:
  374. img_name = "inv_item_sound.tga";
  375. break;
  376. case LLAssetType::AT_CALLINGCARD:
  377. img_name = "inv_item_callingcard_offline.tga";
  378. break;
  379. case LLAssetType::AT_LANDMARK:
  380. if (item->getFlags() &
  381. LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
  382. {
  383. img_name = "inv_item_landmark_visited.tga";
  384. }
  385. else
  386. {
  387. img_name = "inv_item_landmark.tga";
  388. }
  389. break;
  390. case LLAssetType::AT_CLOTHING:
  391. img_name = "inv_item_clothing.tga";
  392. break;
  393. case LLAssetType::AT_OBJECT:
  394. if (item->getFlags() &
  395. LLInventoryItem::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS)
  396. {
  397. img_name = "inv_item_object_multi.tga";
  398. }
  399. else
  400. {
  401. img_name = "inv_item_object.tga";
  402. }
  403. break;
  404. case LLAssetType::AT_NOTECARD:
  405. img_name = "inv_item_notecard.tga";
  406. break;
  407. case LLAssetType::AT_LSL_TEXT:
  408. img_name = "inv_item_script.tga";
  409. break;
  410. case LLAssetType::AT_BODYPART:
  411. img_name = "inv_item_skin.tga";
  412. break;
  413. case LLAssetType::AT_ANIMATION:
  414. img_name = "inv_item_animation.tga";
  415. break;
  416. case LLAssetType::AT_GESTURE:
  417. img_name = "inv_item_gesture.tga";
  418. break;
  419. case LLAssetType::AT_SETTINGS:
  420. img_name = "inv_item_settings.tga";
  421. break;
  422. case LLAssetType::AT_MATERIAL:
  423. img_name = "inv_item_material.tga";
  424. break;
  425. default:
  426. llwarns << "Unknown/unsupported embedded item, type: "
  427. << item->getType() << llendl;
  428. img_name = "inv_item_invalid.tga";
  429. }
  430. LLUIImagePtr image = LLUI::getUIImage(img_name);
  431. if (image.notNull())
  432. {
  433. font->addEmbeddedChar(wch, image->getImage(), item->getName());
  434. }
  435. else
  436. {
  437. llwarns << "Missing image: " << img_name << llendl;
  438. llassert(false);
  439. }
  440. }
  441. }
  442. void LLEmbeddedItems::unbindEmbeddedChars(LLFontGL* font) const
  443. {
  444. if (sEntries.empty())
  445. {
  446. return;
  447. }
  448. for (std::set<llwchar>::const_iterator iter = mEmbeddedUsedChars.begin(),
  449. end = mEmbeddedUsedChars.end();
  450. iter != end; ++iter)
  451. {
  452. font->removeEmbeddedChar(*iter);
  453. }
  454. }
  455. void LLEmbeddedItems::addItems(const std::vector<LLPointer<LLInventoryItem> >& items)
  456. {
  457. for (std::vector<LLPointer<LLInventoryItem> >::const_iterator
  458. iter = items.begin(), end = items.end();
  459. iter != end; ++iter)
  460. {
  461. LLInventoryItem* item = *iter;
  462. if (item)
  463. {
  464. llwchar wc;
  465. if (!insertEmbeddedItem(item, &wc, false))
  466. {
  467. break;
  468. }
  469. mEmbeddedIndexedChars.emplace_back(wc);
  470. }
  471. }
  472. }
  473. void LLEmbeddedItems::getEmbeddedItemList(std::vector<LLPointer<LLInventoryItem> >& items)
  474. {
  475. for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(),
  476. end = mEmbeddedUsedChars.end();
  477. iter != end; ++iter)
  478. {
  479. llwchar wc = *iter;
  480. LLPointer<LLInventoryItem> item = getEmbeddedItem(wc);
  481. if (item)
  482. {
  483. items.emplace_back(std::move(item));
  484. }
  485. }
  486. }
  487. void LLEmbeddedItems::markSaved()
  488. {
  489. for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(),
  490. end = mEmbeddedUsedChars.end();
  491. iter != end; ++iter)
  492. {
  493. llwchar wc = *iter;
  494. sEntries[wc].mSaved = true;
  495. }
  496. }
  497. ///////////////////////////////////////////////////////////////////////////////
  498. class LLViewerTextEditor::LLTextCmdInsertEmbeddedItem final
  499. : public LLTextEditor::LLTextCmd
  500. {
  501. public:
  502. LLTextCmdInsertEmbeddedItem(S32 pos, LLInventoryItem* item)
  503. : LLTextCmd(pos, false),
  504. mExtCharValue(0)
  505. {
  506. mItem = item;
  507. }
  508. bool execute(LLTextEditor* editor, S32* delta) override
  509. {
  510. LLViewerTextEditor* viewer_editor = (LLViewerTextEditor*)editor;
  511. // Take this opportunity to remove any unused embedded items from this
  512. // editor
  513. viewer_editor->mEmbeddedItemList->removeUnusedChars();
  514. if (viewer_editor->mEmbeddedItemList->insertEmbeddedItem(mItem,
  515. &mExtCharValue,
  516. true))
  517. {
  518. LLWString ws;
  519. ws.assign(1, mExtCharValue);
  520. *delta = insert(editor, getPosition(), ws);
  521. return *delta != 0;
  522. }
  523. return false;
  524. }
  525. S32 undo(LLTextEditor* editor) override
  526. {
  527. remove(editor, getPosition(), 1);
  528. return getPosition();
  529. }
  530. S32 redo(LLTextEditor* editor) override
  531. {
  532. LLWString ws;
  533. ws += mExtCharValue;
  534. insert(editor, getPosition(), ws);
  535. return getPosition() + 1;
  536. }
  537. bool hasExtCharValue(llwchar value) const override
  538. {
  539. return value == mExtCharValue;
  540. }
  541. private:
  542. LLPointer<LLInventoryItem> mItem;
  543. llwchar mExtCharValue;
  544. };
  545. struct LLNotecardCopyInfo
  546. {
  547. LLNotecardCopyInfo(LLViewerTextEditor* ed, LLInventoryItem* item)
  548. : mTextEd(ed)
  549. {
  550. mItem = item;
  551. }
  552. LLViewerTextEditor* mTextEd;
  553. // need to make this be a copy (not a * here) because it isn't stable.
  554. // I wish we had passed LLPointers all the way down, but we didn't
  555. LLPointer<LLInventoryItem> mItem;
  556. };
  557. //----------------------------------------------------------------------------
  558. // LLViewerTextEditor class proper
  559. //----------------------------------------------------------------------------
  560. LLViewerTextEditor::LLViewerTextEditor(const std::string& name,
  561. const LLRect& rect, S32 max_length,
  562. const std::string& default_text,
  563. LLFontGL* font,
  564. bool allow_embedded_items)
  565. : LLTextEditor(name, rect, max_length, default_text, font,
  566. allow_embedded_items),
  567. mDragItemChar(0),
  568. mDragItemSaved(false),
  569. mInventoryCallback(new LLEmbeddedNotecardOpener)
  570. {
  571. mEmbeddedItemList = new LLEmbeddedItems(this);
  572. mInventoryCallback->setEditor(this);
  573. }
  574. LLViewerTextEditor::~LLViewerTextEditor()
  575. {
  576. delete mEmbeddedItemList;
  577. // The inventory callback may still be in use by gInventoryCallbackManager
  578. // so set its reference to this to null.
  579. mInventoryCallback->setEditor(NULL);
  580. }
  581. //virtual
  582. void LLViewerTextEditor::makePristine()
  583. {
  584. mEmbeddedItemList->markSaved();
  585. LLTextEditor::makePristine();
  586. }
  587. bool LLViewerTextEditor::handleToolTip(S32 x, S32 y, std::string& msg,
  588. LLRect* sticky_rect_screen)
  589. {
  590. for (child_list_const_iter_t child_iter = getChildList()->begin();
  591. child_iter != getChildList()->end(); ++child_iter)
  592. {
  593. LLView* viewp = *child_iter;
  594. S32 local_x = x - viewp->getRect().mLeft;
  595. S32 local_y = y - viewp->getRect().mBottom;
  596. if (viewp->getVisible() && viewp->getEnabled() &&
  597. viewp->pointInView(local_x, local_y) &&
  598. viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen))
  599. {
  600. return true;
  601. }
  602. }
  603. if (mSegments.empty())
  604. {
  605. return true;
  606. }
  607. const LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
  608. if (cur_segment)
  609. {
  610. bool has_tool_tip = false;
  611. if (cur_segment->getStyle()->getIsEmbeddedItem())
  612. {
  613. LLWString wtip;
  614. has_tool_tip = getEmbeddedItemToolTipAtPos(cur_segment->getStart(),
  615. wtip);
  616. msg = wstring_to_utf8str(wtip);
  617. }
  618. else
  619. {
  620. has_tool_tip = cur_segment->getToolTip(msg);
  621. }
  622. if (has_tool_tip)
  623. {
  624. // Just use a slop area around the cursor
  625. // Convert rect local to screen coordinates
  626. S32 SLOP = 8;
  627. localPointToScreen(x - SLOP, y - SLOP,
  628. &(sticky_rect_screen->mLeft),
  629. &(sticky_rect_screen->mBottom));
  630. sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
  631. sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
  632. }
  633. }
  634. return true;
  635. }
  636. bool LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
  637. {
  638. // Let scrollbar have first dibs
  639. bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
  640. // Enable I Agree checkbox if the user scrolled through entire text
  641. if (mOnScrollEndCallback &&
  642. mScrollbar->getDocPos() == mScrollbar->getDocPosMax())
  643. {
  644. mOnScrollEndCallback(mOnScrollEndData);
  645. }
  646. if (!handled)
  647. {
  648. if (!(mask & MASK_SHIFT))
  649. {
  650. deselect();
  651. }
  652. bool start_select = true;
  653. if (allowsEmbeddedItems())
  654. {
  655. setCursorAtLocalPos(x, y, false);
  656. llwchar wc = 0;
  657. if (mCursorPos < getLength())
  658. {
  659. wc = getWChar(mCursorPos);
  660. }
  661. LLInventoryItem* item_at_pos = LLEmbeddedItems::getEmbeddedItem(wc);
  662. if (item_at_pos)
  663. {
  664. mDragItem = item_at_pos;
  665. mDragItemChar = wc;
  666. mDragItemSaved = LLEmbeddedItems::getEmbeddedItemSaved(wc);
  667. gFocusMgr.setMouseCapture(this);
  668. mMouseDownX = x;
  669. mMouseDownY = y;
  670. S32 screen_x;
  671. S32 screen_y;
  672. localPointToScreen(x, y, &screen_x, &screen_y);
  673. gToolDragAndDrop.setDragStart(screen_x, screen_y);
  674. start_select = false;
  675. }
  676. else
  677. {
  678. mDragItem = NULL;
  679. }
  680. }
  681. if (start_select)
  682. {
  683. // If we are not scrolling (handled by child) then we are selecting
  684. if (mask & MASK_SHIFT)
  685. {
  686. S32 old_cursor_pos = mCursorPos;
  687. setCursorAtLocalPos(x, y, true);
  688. if (hasSelection())
  689. {
  690. mSelectionEnd = mCursorPos;
  691. }
  692. else
  693. {
  694. mSelectionStart = old_cursor_pos;
  695. mSelectionEnd = mCursorPos;
  696. }
  697. // Assume we are starting a drag select
  698. mIsSelecting = true;
  699. }
  700. else
  701. {
  702. setCursorAtLocalPos(x, y, true);
  703. startSelection();
  704. }
  705. gFocusMgr.setMouseCapture(this);
  706. }
  707. handled = true;
  708. }
  709. if (hasTabStop())
  710. {
  711. setFocus(true);
  712. handled = true;
  713. }
  714. // Delay cursor flashing
  715. resetKeystrokeTimer();
  716. return handled;
  717. }
  718. bool LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask)
  719. {
  720. bool handled = false;
  721. if (!mDragItem)
  722. {
  723. // Leave hover segment active during drag and drop
  724. mHoverSegment = NULL;
  725. }
  726. if (hasMouseCapture())
  727. {
  728. if (mIsSelecting)
  729. {
  730. if (x != mLastSelectionX || y != mLastSelectionY)
  731. {
  732. mLastSelectionX = x;
  733. mLastSelectionY = y;
  734. }
  735. if (y > getTextRect().mTop)
  736. {
  737. mScrollbar->setDocPos(mScrollbar->getDocPos() - 1);
  738. }
  739. else if (y < getTextRect().mBottom)
  740. {
  741. mScrollbar->setDocPos(mScrollbar->getDocPos() + 1);
  742. }
  743. setCursorAtLocalPos(x, y, true);
  744. mSelectionEnd = mCursorPos;
  745. updateScrollFromCursor();
  746. gWindowp->setCursor(UI_CURSOR_IBEAM);
  747. }
  748. else if (mDragItem)
  749. {
  750. S32 screen_x;
  751. S32 screen_y;
  752. localPointToScreen(x, y, &screen_x, &screen_y);
  753. if (gToolDragAndDrop.isOverThreshold(screen_x, screen_y))
  754. {
  755. const LLUUID& src_id = isPristine() ? mSourceID : LLUUID::null;
  756. gToolDragAndDrop.beginDrag(
  757. LLAssetType::lookupDragAndDropType(mDragItem->getType()),
  758. mDragItem->getUUID(), LLToolDragAndDrop::SOURCE_NOTECARD,
  759. src_id, mObjectID);
  760. return gToolDragAndDrop.handleHover(x, y, mask);
  761. }
  762. gWindowp->setCursor(UI_CURSOR_HAND);
  763. }
  764. LL_DEBUGS("UserInput") << "hover handled by " << getName()
  765. << " (active)" << LL_ENDL;
  766. handled = true;
  767. }
  768. if (!handled)
  769. {
  770. // Pass to children
  771. handled = LLView::childrenHandleHover(x, y, mask) != NULL;
  772. }
  773. if (handled)
  774. {
  775. // Delay cursor flashing
  776. resetKeystrokeTimer();
  777. }
  778. // Opaque
  779. if (!handled)
  780. {
  781. // Check to see if we are over an HTML-style link
  782. if (!mSegments.empty())
  783. {
  784. const LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
  785. if (cur_segment)
  786. {
  787. if (cur_segment->getStyle()->isLink())
  788. {
  789. LL_DEBUGS("UserInput") << "hover handled by " << getName()
  790. << " (over link, inactive)"
  791. << LL_ENDL;
  792. gWindowp->setCursor(UI_CURSOR_HAND);
  793. handled = true;
  794. }
  795. else if (cur_segment->getStyle()->getIsEmbeddedItem())
  796. {
  797. LL_DEBUGS("UserInput") << "hover handled by "
  798. << getName()
  799. << " (over embedded item, inactive)"
  800. << LL_ENDL;
  801. gWindowp->setCursor(UI_CURSOR_HAND);
  802. handled = true;
  803. }
  804. mHoverSegment = cur_segment;
  805. }
  806. }
  807. if (!handled)
  808. {
  809. LL_DEBUGS("UserInput") << "hover handled by " << getName()
  810. << " (inactive)" << LL_ENDL;
  811. if (!mScrollbar->getVisible() ||
  812. x < getRect().getWidth() - SCROLLBAR_SIZE)
  813. {
  814. gWindowp->setCursor(UI_CURSOR_IBEAM);
  815. }
  816. else
  817. {
  818. gWindowp->setCursor(UI_CURSOR_ARROW);
  819. }
  820. handled = true;
  821. }
  822. }
  823. return handled;
  824. }
  825. bool LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
  826. {
  827. if (hasMouseCapture())
  828. {
  829. if (mDragItem)
  830. {
  831. // mouse down was on an item
  832. S32 dx = x - mMouseDownX;
  833. S32 dy = y - mMouseDownY;
  834. if (-2 < dx && dx < 2 && -2 < dy && dy < 2)
  835. {
  836. if (mDragItemSaved)
  837. {
  838. openEmbeddedItem(mDragItem, mDragItemChar);
  839. }
  840. else
  841. {
  842. showUnsavedAlertDialog(mDragItem);
  843. }
  844. }
  845. }
  846. mDragItem = NULL;
  847. }
  848. bool handled = LLTextEditor::handleMouseUp(x,y,mask);
  849. // Used to enable I Agree checkbox if the user scrolled through entire text
  850. if (mOnScrollEndCallback &&
  851. mScrollbar->getDocPos() == mScrollbar->getDocPosMax())
  852. {
  853. mOnScrollEndCallback(mOnScrollEndData);
  854. }
  855. return handled;
  856. }
  857. bool LLViewerTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
  858. {
  859. bool handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
  860. if (!handled)
  861. {
  862. handled = LLTextEditor::handleRightMouseDown(x, y, mask);
  863. }
  864. return handled;
  865. }
  866. bool LLViewerTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
  867. {
  868. bool handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL;
  869. if (!handled)
  870. {
  871. handled = LLTextEditor::handleMiddleMouseDown(x, y, mask);
  872. }
  873. return handled;
  874. }
  875. bool LLViewerTextEditor::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
  876. {
  877. return childrenHandleMiddleMouseUp(x, y, mask) != NULL;
  878. }
  879. bool LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
  880. {
  881. // let scrollbar have first dibs
  882. bool handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
  883. if (!handled)
  884. {
  885. if (allowsEmbeddedItems())
  886. {
  887. const LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
  888. if (cur_segment && cur_segment->getStyle()->getIsEmbeddedItem())
  889. {
  890. if (openEmbeddedItemAtPos(cur_segment->getStart()))
  891. {
  892. deselect();
  893. setFocus(false);
  894. return true;
  895. }
  896. }
  897. }
  898. setCursorAtLocalPos(x, y, false);
  899. deselect();
  900. const LLWString &text = getWText();
  901. if (LLWStringUtil::isPartOfWord(text[mCursorPos]))
  902. {
  903. // Select word the cursor is over
  904. while (mCursorPos > 0 &&
  905. LLWStringUtil::isPartOfWord(text[mCursorPos - 1]))
  906. {
  907. --mCursorPos;
  908. }
  909. startSelection();
  910. while (mCursorPos < (S32)text.length() &&
  911. LLWStringUtil::isPartOfWord(text[mCursorPos]))
  912. {
  913. ++mCursorPos;
  914. }
  915. mSelectionEnd = mCursorPos;
  916. }
  917. else if (mCursorPos < (S32)text.length() &&
  918. !iswspace(text[mCursorPos]))
  919. {
  920. // Select the character the cursor is over
  921. startSelection();
  922. mSelectionEnd = ++mCursorPos;
  923. }
  924. // We do not want handleMouseUp() to "finish" the selection and thereby
  925. // set mSelectionEnd to where the mouse is, so we finish the selection
  926. // here.
  927. mIsSelecting = false;
  928. // delay cursor flashing
  929. resetKeystrokeTimer();
  930. // take selection to 'primary' clipboard
  931. updatePrimary();
  932. handled = true;
  933. }
  934. return handled;
  935. }
  936. // Allows calling cards to be dropped onto text fields. Appends the name and
  937. // a carriage return.
  938. //virtual
  939. bool LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
  940. EDragAndDropType cargo_type,
  941. void* cargo_data,
  942. EAcceptance* accept,
  943. std::string& tooltip_msg)
  944. {
  945. LLToolDragAndDrop::ESource source = gToolDragAndDrop.getSource();
  946. if (source == LLToolDragAndDrop::SOURCE_NOTECARD)
  947. {
  948. // We currently do not handle dragging items from one notecard to
  949. // another since items in a notecard must be in Inventory to be
  950. // verified. See DEV-2891.
  951. LL_DEBUGS("DragAndDrop") << "Cannot drag from another notecard."
  952. << LL_ENDL;
  953. return false;
  954. }
  955. LL_DEBUGS("UserInput") << "dragAndDrop handled by LLViewerTextEditor "
  956. << getName() << LL_ENDL;
  957. if (!getEnabled() || !acceptsTextInput() || !allowsEmbeddedItems() ||
  958. !cargo_data)
  959. {
  960. // Not enabled/allowed/valid
  961. *accept = ACCEPT_NO;
  962. // Handled nonetheless
  963. return true;
  964. }
  965. bool supported;
  966. switch (cargo_type)
  967. {
  968. case DAD_CALLINGCARD:
  969. case DAD_TEXTURE:
  970. case DAD_SOUND:
  971. case DAD_LANDMARK:
  972. case DAD_SCRIPT:
  973. case DAD_CLOTHING:
  974. case DAD_OBJECT:
  975. case DAD_NOTECARD:
  976. case DAD_BODYPART:
  977. case DAD_ANIMATION:
  978. case DAD_GESTURE:
  979. #if LL_MESH_ASSET_SUPPORT
  980. case DAD_MESH:
  981. case DAD_GLTF:
  982. case DAD_GLTF_BIN:
  983. #endif
  984. supported = true;
  985. break;
  986. case DAD_MATERIAL:
  987. supported = gAgent.hasInventoryMaterial();
  988. case DAD_SETTINGS:
  989. supported = gAgent.hasExtendedEnvironment();
  990. break;
  991. default:
  992. supported = false;
  993. }
  994. if (!supported)
  995. {
  996. LL_DEBUGS("DragAndDrop") << "Unsupported item type " << cargo_type
  997. << "for embedding" << LL_ENDL;
  998. *accept = ACCEPT_NO;
  999. // Handled nonetheless
  1000. return true;
  1001. }
  1002. LLInventoryItem* item = (LLInventoryItem*)cargo_data;
  1003. U32 mask_next = item->getPermissions().getMaskNextOwner();
  1004. if ((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
  1005. {
  1006. if (drop)
  1007. {
  1008. deselect();
  1009. S32 old_cursor = mCursorPos;
  1010. setCursorAtLocalPos(x, y, true);
  1011. S32 insert_pos = mCursorPos;
  1012. setCursorPos(old_cursor);
  1013. bool inserted = insertEmbeddedItem(insert_pos, item);
  1014. if (inserted && old_cursor > mCursorPos)
  1015. {
  1016. setCursorPos(mCursorPos + 1);
  1017. }
  1018. updateLineStartList();
  1019. }
  1020. *accept = ACCEPT_YES_COPY_MULTI;
  1021. }
  1022. else
  1023. {
  1024. *accept = ACCEPT_NO;
  1025. LL_DEBUGS("DragAndDrop") << "Insufficient item permissions"
  1026. << LL_ENDL;
  1027. if (tooltip_msg.empty())
  1028. {
  1029. tooltip_msg.assign("Only items with unrestricted\n"
  1030. "'next owner' permissions \n"
  1031. "can be attached to notecards.");
  1032. }
  1033. }
  1034. return true;
  1035. }
  1036. void LLViewerTextEditor::setASCIIEmbeddedText(const std::string& instr)
  1037. {
  1038. LLWString wtext;
  1039. const U8* buffer = (U8*)(instr.c_str());
  1040. while (*buffer)
  1041. {
  1042. llwchar wch;
  1043. U8 c = *buffer++;
  1044. if (c >= 0x80)
  1045. {
  1046. S32 index = (S32)(c - 0x80);
  1047. wch = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
  1048. }
  1049. else
  1050. {
  1051. wch = (llwchar)c;
  1052. }
  1053. wtext.push_back(wch);
  1054. }
  1055. setWText(wtext);
  1056. }
  1057. void LLViewerTextEditor::setEmbeddedText(const std::string& instr)
  1058. {
  1059. LLWString wtext = utf8str_to_wstring(instr);
  1060. for (S32 i = 0, count = wtext.size(); i < count; ++i)
  1061. {
  1062. llwchar wch = wtext[i];
  1063. if (wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR)
  1064. {
  1065. S32 index = wch - FIRST_EMBEDDED_CHAR;
  1066. wtext[i] = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
  1067. }
  1068. }
  1069. setWText(wtext);
  1070. }
  1071. std::string LLViewerTextEditor::getEmbeddedText()
  1072. {
  1073. std::string outtext;
  1074. LLWString outtextw;
  1075. mEmbeddedItemList->copyUsedCharsToIndexed();
  1076. for (S32 i = 0, count = getWText().size(); i < count; ++i)
  1077. {
  1078. llwchar wch = getWChar(i);
  1079. if (wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR)
  1080. {
  1081. S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
  1082. wch = FIRST_EMBEDDED_CHAR + index;
  1083. }
  1084. outtextw.push_back(wch);
  1085. }
  1086. outtext = wstring_to_utf8str(outtextw);
  1087. return outtext;
  1088. }
  1089. std::string LLViewerTextEditor::appendTime(bool prepend_newline)
  1090. {
  1091. std::string text = LLLogChat::timestamp(true) + " ";
  1092. appendColoredText(text, false, prepend_newline, LLColor4::grey);
  1093. return text;
  1094. }
  1095. llwchar LLViewerTextEditor::pasteEmbeddedItem(llwchar ext_char)
  1096. {
  1097. if (mEmbeddedItemList->hasEmbeddedItem(ext_char))
  1098. {
  1099. return ext_char; // already exists in my list
  1100. }
  1101. LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(ext_char);
  1102. if (item)
  1103. {
  1104. // Add item to my list and return new llwchar associated with it
  1105. llwchar new_wc;
  1106. if (mEmbeddedItemList->insertEmbeddedItem(item, &new_wc, true))
  1107. {
  1108. return new_wc;
  1109. }
  1110. }
  1111. return LL_UNKNOWN_CHAR; // Item not found or list full
  1112. }
  1113. void LLViewerTextEditor::bindEmbeddedChars(LLFontGL* font) const
  1114. {
  1115. mEmbeddedItemList->bindEmbeddedChars(font);
  1116. }
  1117. void LLViewerTextEditor::unbindEmbeddedChars(LLFontGL* font) const
  1118. {
  1119. mEmbeddedItemList->unbindEmbeddedChars(font);
  1120. }
  1121. bool LLViewerTextEditor::getEmbeddedItemToolTipAtPos(S32 pos,
  1122. LLWString &msg) const
  1123. {
  1124. if (pos < getLength())
  1125. {
  1126. LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(getWChar(pos));
  1127. if (item)
  1128. {
  1129. msg = utf8str_to_wstring(item->getName());
  1130. msg += '\n';
  1131. msg += utf8str_to_wstring(item->getDescription());
  1132. return true;
  1133. }
  1134. }
  1135. return false;
  1136. }
  1137. bool LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos)
  1138. {
  1139. if (pos < getLength())
  1140. {
  1141. llwchar wc = getWChar(pos);
  1142. LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(wc);
  1143. if (item)
  1144. {
  1145. bool saved = LLEmbeddedItems::getEmbeddedItemSaved(wc);
  1146. if (saved)
  1147. {
  1148. return openEmbeddedItem(item, wc);
  1149. }
  1150. else
  1151. {
  1152. showUnsavedAlertDialog(item);
  1153. }
  1154. }
  1155. }
  1156. return false;
  1157. }
  1158. bool LLViewerTextEditor::openEmbeddedItem(LLInventoryItem* item, llwchar wc)
  1159. {
  1160. switch (item->getType())
  1161. {
  1162. case LLAssetType::AT_TEXTURE:
  1163. openEmbeddedTexture(item, wc);
  1164. return true;
  1165. case LLAssetType::AT_SOUND:
  1166. openEmbeddedSound(item, wc);
  1167. return true;
  1168. case LLAssetType::AT_NOTECARD:
  1169. openEmbeddedNotecard(item, wc);
  1170. return true;
  1171. case LLAssetType::AT_LANDMARK:
  1172. openEmbeddedLandmark(item, wc);
  1173. return true;
  1174. case LLAssetType::AT_CALLINGCARD:
  1175. openEmbeddedCallingcard(item, wc);
  1176. return true;
  1177. case LLAssetType::AT_LSL_TEXT:
  1178. case LLAssetType::AT_CLOTHING:
  1179. case LLAssetType::AT_OBJECT:
  1180. case LLAssetType::AT_BODYPART:
  1181. case LLAssetType::AT_ANIMATION:
  1182. case LLAssetType::AT_GESTURE:
  1183. case LLAssetType::AT_SETTINGS:
  1184. case LLAssetType::AT_MATERIAL:
  1185. showCopyToInvDialog(item, wc);
  1186. return true;
  1187. default:
  1188. return false;
  1189. }
  1190. }
  1191. void LLViewerTextEditor::openEmbeddedTexture(LLInventoryItem* item, llwchar wc)
  1192. {
  1193. // See if we can bring an existing preview to the front.
  1194. // *NOTE: Just for embedded texture, we should use getAssetUUID(),
  1195. // not getUUID(), because LLPreviewTexture passes AssetUUID into
  1196. // LLPreview constructor ItemUUID parameter.
  1197. if (!LLPreview::show(item->getAssetUUID()))
  1198. {
  1199. // There isn't one, so make a new preview
  1200. if (item)
  1201. {
  1202. S32 left, top;
  1203. gFloaterViewp->getNewFloaterPosition(&left, &top);
  1204. LLRect rect = gSavedSettings.getRect("PreviewTextureRect");
  1205. rect.translate(left - rect.mLeft, top - rect.mTop);
  1206. LLPreviewTexture* preview;
  1207. preview = new LLPreviewTexture("preview texture", rect,
  1208. item->getName(),
  1209. item->getAssetUUID(), true);
  1210. preview->setAuxItem(item);
  1211. preview->setNotecardInfo(mNotecardInventoryID, mObjectID);
  1212. }
  1213. }
  1214. }
  1215. void LLViewerTextEditor::openEmbeddedSound(LLInventoryItem* item, llwchar wc)
  1216. {
  1217. // Play sound locally
  1218. LLVector3d lpos_global = gAgent.getPositionGlobal();
  1219. constexpr F32 SOUND_GAIN = 1.f;
  1220. if (gAudiop)
  1221. {
  1222. gAudiop->triggerSound(item->getAssetUUID(), gAgentID, SOUND_GAIN,
  1223. LLAudioEngine::AUDIO_TYPE_UI, lpos_global);
  1224. }
  1225. showCopyToInvDialog(item, wc);
  1226. }
  1227. void LLViewerTextEditor::openEmbeddedLandmark(LLInventoryItem* item,
  1228. llwchar wc)
  1229. {
  1230. std::string title = LLTrans::getString("Landmark") + ": " +
  1231. item->getName();
  1232. open_landmark((LLViewerInventoryItem*)item, title);
  1233. }
  1234. void LLViewerTextEditor::openEmbeddedCallingcard(LLInventoryItem* item,
  1235. llwchar)
  1236. {
  1237. open_callingcard((LLViewerInventoryItem*)item);
  1238. }
  1239. void LLViewerTextEditor::openEmbeddedNotecard(LLInventoryItem* item,
  1240. llwchar wc)
  1241. {
  1242. copyInventory(item, gInventoryCallbacks.registerCB(mInventoryCallback));
  1243. }
  1244. void LLViewerTextEditor::showUnsavedAlertDialog(LLInventoryItem* item)
  1245. {
  1246. LLSD payload;
  1247. payload["item_id"] = item->getUUID();
  1248. payload["notecard_id"] = mNotecardInventoryID;
  1249. gNotifications.add("ConfirmNotecardSave", LLSD(), payload,
  1250. LLViewerTextEditor::onNotecardDialog);
  1251. }
  1252. //static
  1253. bool LLViewerTextEditor::onNotecardDialog(const LLSD& notification,
  1254. const LLSD& response)
  1255. {
  1256. if (LLNotification::getSelectedOption(notification, response) == 0)
  1257. {
  1258. // itemptr is deleted by LLPreview::save
  1259. LLPointer<LLInventoryItem>* itemptr =
  1260. new LLPointer<LLInventoryItem>(gInventory.getItem(notification["payload"]["item_id"].asUUID()));
  1261. LLPreview::save(notification["payload"]["notecard_id"].asUUID(),
  1262. itemptr);
  1263. }
  1264. return false;
  1265. }
  1266. void LLViewerTextEditor::showCopyToInvDialog(LLInventoryItem* item, llwchar wc)
  1267. {
  1268. LLSD payload;
  1269. LLUUID item_id = item->getUUID();
  1270. payload["item_id"] = item_id;
  1271. payload["item_wc"] = LLSD::Integer(wc);
  1272. gNotifications.add("ConfirmItemCopy", LLSD(), payload,
  1273. boost::bind(&LLViewerTextEditor::onCopyToInvDialog,
  1274. this, _1, _2));
  1275. }
  1276. bool LLViewerTextEditor::onCopyToInvDialog(const LLSD& notification,
  1277. const LLSD& response)
  1278. {
  1279. if (LLNotification::getSelectedOption(notification, response) == 0)
  1280. {
  1281. llwchar wc = llwchar(notification["payload"]["item_wc"].asInteger());
  1282. LLInventoryItem* itemp = LLEmbeddedItems::getEmbeddedItem(wc);
  1283. if (itemp)
  1284. {
  1285. copyInventory(itemp);
  1286. }
  1287. }
  1288. return false;
  1289. }
  1290. // Returns change in number of characters in mWText
  1291. S32 LLViewerTextEditor::insertEmbeddedItem(S32 pos, LLInventoryItem* item)
  1292. {
  1293. return execute(new LLTextCmdInsertEmbeddedItem(pos, item));
  1294. }
  1295. bool LLViewerTextEditor::importStream(std::istream& str)
  1296. {
  1297. LLNotecard nc(LLNotecard::MAX_SIZE);
  1298. bool success = nc.importStream(str);
  1299. if (success)
  1300. {
  1301. mEmbeddedItemList->clear();
  1302. const std::vector<LLPointer<LLInventoryItem> >& items = nc.getItems();
  1303. mEmbeddedItemList->addItems(items);
  1304. // Actually set the text
  1305. if (allowsEmbeddedItems())
  1306. {
  1307. if (nc.getVersion() == 1)
  1308. {
  1309. setASCIIEmbeddedText(nc.getText());
  1310. }
  1311. else
  1312. {
  1313. setEmbeddedText(nc.getText());
  1314. }
  1315. }
  1316. else
  1317. {
  1318. setText(nc.getText());
  1319. }
  1320. }
  1321. return success;
  1322. }
  1323. bool LLViewerTextEditor::importBuffer(const char* buffer, S32 length)
  1324. {
  1325. LLMemoryStream str((U8*)buffer, length);
  1326. return importStream(str);
  1327. }
  1328. bool LLViewerTextEditor::exportBuffer(std::string& buffer)
  1329. {
  1330. LLNotecard nc(LLNotecard::MAX_SIZE);
  1331. // Get the embedded text and update the item list to just be the used items
  1332. nc.setText(getEmbeddedText());
  1333. // Now get the used items and copy the list to the notecard
  1334. std::vector<LLPointer<LLInventoryItem> > embedded_items;
  1335. mEmbeddedItemList->getEmbeddedItemList(embedded_items);
  1336. nc.setItems(embedded_items);
  1337. std::stringstream out_stream;
  1338. nc.exportStream(out_stream);
  1339. buffer = out_stream.str();
  1340. return true;
  1341. }
  1342. void LLViewerTextEditor::copyInventory(const LLInventoryItem* item,
  1343. U32 callback_id)
  1344. {
  1345. copy_inventory_from_notecard(mObjectID, mNotecardInventoryID, item,
  1346. callback_id);
  1347. }
  1348. bool LLViewerTextEditor::hasEmbeddedInventory()
  1349. {
  1350. return !mEmbeddedItemList->empty();
  1351. }
  1352. //virtual
  1353. const std::string& LLViewerTextEditor::getTag() const
  1354. {
  1355. return LL_TEXT_EDITOR_TAG;
  1356. }
  1357. //virtual
  1358. LLXMLNodePtr LLViewerTextEditor::getXML(bool save_children) const
  1359. {
  1360. LLXMLNodePtr node = LLTextEditor::getXML();
  1361. node->setName(LL_TEXT_EDITOR_TAG);
  1362. return node;
  1363. }
  1364. LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView* parent,
  1365. LLUICtrlFactory*)
  1366. {
  1367. std::string name = LL_TEXT_EDITOR_TAG;
  1368. node->getAttributeString("name", name);
  1369. LLRect rect;
  1370. createRect(node, rect, parent, LLRect());
  1371. U32 max_text_length = 255;
  1372. node->getAttributeU32("max_length", max_text_length);
  1373. bool allow_embedded_items = false;
  1374. node->getAttributeBool("embedded_items", allow_embedded_items);
  1375. LLFontGL* font = LLView::selectFont(node);
  1376. // std::string text = node->getValue();
  1377. std::string text = node->getTextContents().substr(0, max_text_length - 1);
  1378. if (text.size() > max_text_length)
  1379. {
  1380. // Erase everything from max_text_length on.
  1381. text.erase(max_text_length);
  1382. }
  1383. LLViewerTextEditor* self = new LLViewerTextEditor(name, rect,
  1384. max_text_length,
  1385. LLStringUtil::null, font,
  1386. allow_embedded_items);
  1387. bool ignore_tabs = self->tabsToNextField();
  1388. node->getAttributeBool("ignore_tab", ignore_tabs);
  1389. self->setTabsToNextField(ignore_tabs);
  1390. self->setTextEditorParameters(node);
  1391. bool hide_scrollbar = false;
  1392. node->getAttributeBool("hide_scrollbar", hide_scrollbar);
  1393. self->setHideScrollbarForShortDocs(hide_scrollbar);
  1394. bool hide_border = !self->isBorderVisible();
  1395. node->getAttributeBool("hide_border", hide_border);
  1396. self->setBorderVisible(!hide_border);
  1397. bool parse_html = self->mParseHTML;
  1398. node->getAttributeBool("allow_html", parse_html);
  1399. self->setParseHTML(parse_html);
  1400. self->initFromXML(node, parent);
  1401. // Add text after all parameters have been set
  1402. self->appendStyledText(text, false, false);
  1403. return self;
  1404. }